Always load middle to handle forwarded proxy data (#51332)

This commit is contained in:
Franck Nijhof 2021-06-01 18:38:55 +02:00 committed by GitHub
parent d975f9eb0a
commit cdd1f6b2f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 85 additions and 136 deletions

View File

@ -14,13 +14,10 @@ from ipaddress import (
ip_address, ip_address,
ip_network, ip_network,
) )
import logging
from typing import Any, Dict, List, Union, cast from typing import Any, Dict, List, Union, cast
from aiohttp import hdrs
import voluptuous as vol import voluptuous as vol
from homeassistant.components import http
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -89,31 +86,9 @@ class TrustedNetworksAuthProvider(AuthProvider):
"""Trusted Networks auth provider does not support MFA.""" """Trusted Networks auth provider does not support MFA."""
return False return False
@callback
def is_allowed_request(self) -> bool:
"""Return if it is an allowed request."""
request = http.current_request.get()
if request is not None and (
self.hass.http.use_x_forwarded_for
or hdrs.X_FORWARDED_FOR not in request.headers
):
return True
logging.getLogger(__name__).warning(
"A request contained an x-forwarded-for header but your HTTP integration is not set-up "
"for reverse proxies. This usually means that you have not configured your reverse proxy "
"correctly. This request will be blocked in Home Assistant 2021.7 unless you configure "
"your HTTP integration to allow this header."
)
return True
async def async_login_flow(self, context: dict | None) -> LoginFlow: async def async_login_flow(self, context: dict | None) -> LoginFlow:
"""Return a flow to login.""" """Return a flow to login."""
assert context is not None assert context is not None
if not self.is_allowed_request():
return MisconfiguredTrustedNetworksLoginFlow(self)
ip_addr = cast(IPAddress, context.get("ip_address")) ip_addr = cast(IPAddress, context.get("ip_address"))
users = await self.store.async_get_users() users = await self.store.async_get_users()
available_users = [ available_users = [
@ -195,11 +170,6 @@ class TrustedNetworksAuthProvider(AuthProvider):
Raise InvalidAuthError if not. Raise InvalidAuthError if not.
Raise InvalidAuthError if trusted_networks is not configured. Raise InvalidAuthError if trusted_networks is not configured.
""" """
if not self.is_allowed_request():
raise InvalidAuthError(
"No request or it contains x-forwarded-for header and that's not allowed by configuration"
)
if not self.trusted_networks: if not self.trusted_networks:
raise InvalidAuthError("trusted_networks is not configured") raise InvalidAuthError("trusted_networks is not configured")
@ -220,16 +190,6 @@ class TrustedNetworksAuthProvider(AuthProvider):
self.async_validate_access(ip_address(remote_ip)) self.async_validate_access(ip_address(remote_ip))
class MisconfiguredTrustedNetworksLoginFlow(LoginFlow):
"""Login handler for misconfigured trusted networks."""
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Handle the step of the form."""
return self.async_abort(reason="forwared_for_header_not_allowed")
class TrustedNetworksLoginFlow(LoginFlow): class TrustedNetworksLoginFlow(LoginFlow):
"""Handler for the login flow.""" """Handler for the login flow."""

View File

@ -232,10 +232,7 @@ class HomeAssistantHTTP:
# forwarded middleware needs to go second. # forwarded middleware needs to go second.
setup_security_filter(app) setup_security_filter(app)
# Only register middleware if `use_x_forwarded_for` is enabled async_setup_forwarded(app, use_x_forwarded_for, trusted_proxies)
# and trusted proxies are provided
if use_x_forwarded_for and trusted_proxies:
async_setup_forwarded(app, trusted_proxies)
setup_request_context(app, current_request) setup_request_context(app, current_request)
@ -252,7 +249,6 @@ class HomeAssistantHTTP:
self.ssl_key = ssl_key self.ssl_key = ssl_key
self.server_host = server_host self.server_host = server_host
self.server_port = server_port self.server_port = server_port
self.use_x_forwarded_for = use_x_forwarded_for
self.trusted_proxies = trusted_proxies self.trusted_proxies = trusted_proxies
self.is_ban_enabled = is_ban_enabled self.is_ban_enabled = is_ban_enabled
self.ssl_profile = ssl_profile self.ssl_profile = ssl_profile

View File

@ -14,7 +14,9 @@ _LOGGER = logging.getLogger(__name__)
@callback @callback
def async_setup_forwarded(app: Application, trusted_proxies: list[str]) -> None: def async_setup_forwarded(
app: Application, use_x_forwarded_for: bool | None, trusted_proxies: list[str]
) -> None:
"""Create forwarded middleware for the app. """Create forwarded middleware for the app.
Process IP addresses, proto and host information in the forwarded for headers. Process IP addresses, proto and host information in the forwarded for headers.
@ -73,15 +75,37 @@ def async_setup_forwarded(app: Application, trusted_proxies: list[str]) -> None:
# No forwarding headers, continue as normal # No forwarding headers, continue as normal
return await handler(request) return await handler(request)
# Ensure the IP of the connected peer is trusted # Get connected IP
assert request.transport is not None if (
request.transport is None
or request.transport.get_extra_info("peername") is None
):
# Connected IP isn't retrieveable from the request transport, continue
return await handler(request)
connected_ip = ip_address(request.transport.get_extra_info("peername")[0]) connected_ip = ip_address(request.transport.get_extra_info("peername")[0])
if not any(connected_ip in trusted_proxy for trusted_proxy in trusted_proxies):
# We have X-Forwarded-For, but config does not agree
if not use_x_forwarded_for:
_LOGGER.warning( _LOGGER.warning(
"Received X-Forwarded-For header from untrusted proxy %s, headers not processed", "A request from a reverse proxy was received from %s, but your "
"HTTP integration is not set-up for reverse proxies; "
"This request will be blocked in Home Assistant 2021.7 unless "
"you configure your HTTP integration to allow this header",
connected_ip, connected_ip,
) )
# Not trusted, continue as normal # Block this request in the future, for now we pass.
return await handler(request)
# Ensure the IP of the connected peer is trusted
if not any(connected_ip in trusted_proxy for trusted_proxy in trusted_proxies):
_LOGGER.warning(
"Received X-Forwarded-For header from untrusted proxy %s, headers not processed; "
"This request will be blocked in Home Assistant 2021.7 unless you configure "
"your HTTP integration to allow this proxy to reverse your Home Assistant instance",
connected_ip,
)
# Not trusted, Block this request in the future, continue as normal
return await handler(request) return await handler(request)
# Multiple X-Forwarded-For headers # Multiple X-Forwarded-For headers

View File

@ -5,13 +5,10 @@ from unittest.mock import Mock, patch
import pytest import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant import auth, const from homeassistant import auth
from homeassistant.auth import auth_store from homeassistant.auth import auth_store
from homeassistant.auth.providers import trusted_networks as tn_auth from homeassistant.auth.providers import trusted_networks as tn_auth
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_CREATE_ENTRY from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_CREATE_ENTRY
from homeassistant.setup import async_setup_component
FORWARD_FOR_IS_WARNING = (const.MAJOR_VERSION, const.MINOR_VERSION) < (2021, 8)
@pytest.fixture @pytest.fixture
@ -115,17 +112,7 @@ def manager_bypass_login(hass, store, provider_bypass_login):
) )
@pytest.fixture async def test_trusted_networks_credentials(manager, provider):
def mock_allowed_request():
"""Mock that the request is allowed."""
with patch(
"homeassistant.auth.providers.trusted_networks.TrustedNetworksAuthProvider.is_allowed_request",
return_value=True,
):
yield
async def test_trusted_networks_credentials(manager, provider, mock_allowed_request):
"""Test trusted_networks credentials related functions.""" """Test trusted_networks credentials related functions."""
owner = await manager.async_create_user("test-owner") owner = await manager.async_create_user("test-owner")
tn_owner_cred = await provider.async_get_or_create_credentials({"user": owner.id}) tn_owner_cred = await provider.async_get_or_create_credentials({"user": owner.id})
@ -142,7 +129,7 @@ async def test_trusted_networks_credentials(manager, provider, mock_allowed_requ
await provider.async_get_or_create_credentials({"user": "invalid-user"}) await provider.async_get_or_create_credentials({"user": "invalid-user"})
async def test_validate_access(provider, mock_allowed_request): async def test_validate_access(provider):
"""Test validate access from trusted networks.""" """Test validate access from trusted networks."""
provider.async_validate_access(ip_address("192.168.0.1")) provider.async_validate_access(ip_address("192.168.0.1"))
provider.async_validate_access(ip_address("192.168.128.10")) provider.async_validate_access(ip_address("192.168.128.10"))
@ -157,7 +144,7 @@ async def test_validate_access(provider, mock_allowed_request):
provider.async_validate_access(ip_address("2001:db8::ff00:42:8329")) provider.async_validate_access(ip_address("2001:db8::ff00:42:8329"))
async def test_validate_refresh_token(provider, mock_allowed_request): async def test_validate_refresh_token(provider):
"""Verify re-validation of refresh token.""" """Verify re-validation of refresh token."""
with patch.object(provider, "async_validate_access") as mock: with patch.object(provider, "async_validate_access") as mock:
with pytest.raises(tn_auth.InvalidAuthError): with pytest.raises(tn_auth.InvalidAuthError):
@ -167,7 +154,7 @@ async def test_validate_refresh_token(provider, mock_allowed_request):
mock.assert_called_once_with(ip_address("127.0.0.1")) mock.assert_called_once_with(ip_address("127.0.0.1"))
async def test_login_flow(manager, provider, mock_allowed_request): async def test_login_flow(manager, provider):
"""Test login flow.""" """Test login flow."""
owner = await manager.async_create_user("test-owner") owner = await manager.async_create_user("test-owner")
user = await manager.async_create_user("test-user") user = await manager.async_create_user("test-user")
@ -194,9 +181,7 @@ async def test_login_flow(manager, provider, mock_allowed_request):
assert step["data"]["user"] == user.id assert step["data"]["user"] == user.id
async def test_trusted_users_login( async def test_trusted_users_login(manager_with_user, provider_with_user):
manager_with_user, provider_with_user, mock_allowed_request
):
"""Test available user list changed per different IP.""" """Test available user list changed per different IP."""
owner = await manager_with_user.async_create_user("test-owner") owner = await manager_with_user.async_create_user("test-owner")
sys_user = await manager_with_user.async_create_system_user( sys_user = await manager_with_user.async_create_system_user(
@ -276,9 +261,7 @@ async def test_trusted_users_login(
assert schema({"user": sys_user.id}) assert schema({"user": sys_user.id})
async def test_trusted_group_login( async def test_trusted_group_login(manager_with_user, provider_with_user):
manager_with_user, provider_with_user, mock_allowed_request
):
"""Test config trusted_user with group_id.""" """Test config trusted_user with group_id."""
owner = await manager_with_user.async_create_user("test-owner") owner = await manager_with_user.async_create_user("test-owner")
# create a user in user group # create a user in user group
@ -331,9 +314,7 @@ async def test_trusted_group_login(
assert schema({"user": user.id}) assert schema({"user": user.id})
async def test_bypass_login_flow( async def test_bypass_login_flow(manager_bypass_login, provider_bypass_login):
manager_bypass_login, provider_bypass_login, mock_allowed_request
):
"""Test login flow can be bypass if only one user available.""" """Test login flow can be bypass if only one user available."""
owner = await manager_bypass_login.async_create_user("test-owner") owner = await manager_bypass_login.async_create_user("test-owner")
@ -364,42 +345,3 @@ async def test_bypass_login_flow(
# both owner and user listed # both owner and user listed
assert schema({"user": owner.id}) assert schema({"user": owner.id})
assert schema({"user": user.id}) assert schema({"user": user.id})
async def test_allowed_request(hass, provider, current_request, caplog):
"""Test allowing requests."""
assert await async_setup_component(hass, "http", {})
provider.async_validate_access(ip_address("192.168.0.1"))
current_request.get.return_value = current_request.get.return_value.clone(
headers={
**current_request.get.return_value.headers,
"x-forwarded-for": "1.2.3.4",
}
)
if FORWARD_FOR_IS_WARNING:
caplog.clear()
provider.async_validate_access(ip_address("192.168.0.1"))
assert "This request will be blocked" in caplog.text
else:
with pytest.raises(tn_auth.InvalidAuthError):
provider.async_validate_access(ip_address("192.168.0.1"))
hass.http.use_x_forwarded_for = True
provider.async_validate_access(ip_address("192.168.0.1"))
@pytest.mark.skipif(FORWARD_FOR_IS_WARNING, reason="Currently a warning")
async def test_login_flow_no_request(provider):
"""Test getting a login flow."""
login_flow = await provider.async_login_flow({"ip_address": ip_address("1.1.1.1")})
assert await login_flow.async_step_init() == {
"description_placeholders": None,
"flow_id": None,
"handler": None,
"reason": "forwared_for_header_not_allowed",
"type": "abort",
}

View File

@ -53,7 +53,7 @@ def app(hass):
app = web.Application() app = web.Application()
app["hass"] = hass app["hass"] = hass
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, []) async_setup_forwarded(app, True, [])
return app return app

View File

@ -28,7 +28,7 @@ async def test_x_forwarded_for_without_trusted_proxy(aiohttp_client, caplog):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, []) async_setup_forwarded(app, True, [])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"}) resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
@ -74,7 +74,7 @@ async def test_x_forwarded_for_with_trusted_proxy(
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded( async_setup_forwarded(
app, [ip_network(trusted_proxy) for trusted_proxy in trusted_proxies] app, True, [ip_network(trusted_proxy) for trusted_proxy in trusted_proxies]
) )
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
@ -83,6 +83,33 @@ async def test_x_forwarded_for_with_trusted_proxy(
assert resp.status == 200 assert resp.status == 200
async def test_x_forwarded_for_disabled_with_proxy(aiohttp_client, caplog):
"""Test that we warn when processing is disabled, but proxy has been detected."""
async def handler(request):
url = mock_api_client.make_url("/")
assert request.host == f"{url.host}:{url.port}"
assert request.scheme == "http"
assert not request.secure
assert request.remote == "127.0.0.1"
return web.Response()
app = web.Application()
app.router.add_get("/", handler)
async_setup_forwarded(app, False, [])
mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
assert resp.status == 200
assert (
"A request from a reverse proxy was received from 127.0.0.1, but your HTTP "
"integration is not set-up for reverse proxies" in caplog.text
)
async def test_x_forwarded_for_with_untrusted_proxy(aiohttp_client): async def test_x_forwarded_for_with_untrusted_proxy(aiohttp_client):
"""Test that we get the IP from transport with untrusted proxy.""" """Test that we get the IP from transport with untrusted proxy."""
@ -97,7 +124,7 @@ async def test_x_forwarded_for_with_untrusted_proxy(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("1.1.1.1")]) async_setup_forwarded(app, True, [ip_network("1.1.1.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"}) resp = await mock_api_client.get("/", headers={X_FORWARDED_FOR: "255.255.255.255"})
@ -119,7 +146,7 @@ async def test_x_forwarded_for_with_spoofed_header(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -148,7 +175,7 @@ async def test_x_forwarded_for_with_malformed_header(
"""Test that we get a HTTP 400 bad request with a malformed header.""" """Test that we get a HTTP 400 bad request with a malformed header."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
@ -162,7 +189,7 @@ async def test_x_forwarded_for_with_multiple_headers(aiohttp_client, caplog):
"""Test that we get a HTTP 400 bad request with multiple headers.""" """Test that we get a HTTP 400 bad request with multiple headers."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
@ -193,7 +220,7 @@ async def test_x_forwarded_proto_without_trusted_proxy(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, []) async_setup_forwarded(app, True, [])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -245,7 +272,7 @@ async def test_x_forwarded_proto_with_trusted_proxy(
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.0/24")]) async_setup_forwarded(app, True, [ip_network("127.0.0.0/24")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -273,7 +300,7 @@ async def test_x_forwarded_proto_with_trusted_proxy_multiple_for(aiohttp_client)
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.0/24")]) async_setup_forwarded(app, True, [ip_network("127.0.0.0/24")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -301,7 +328,7 @@ async def test_x_forwarded_proto_not_processed_without_for(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_PROTO: "https"}) resp = await mock_api_client.get("/", headers={X_FORWARDED_PROTO: "https"})
@ -313,7 +340,7 @@ async def test_x_forwarded_proto_with_multiple_headers(aiohttp_client, caplog):
"""Test that we get a HTTP 400 bad request with multiple headers.""" """Test that we get a HTTP 400 bad request with multiple headers."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -339,7 +366,7 @@ async def test_x_forwarded_proto_empty_element(
"""Test that we get a HTTP 400 bad request with empty proto.""" """Test that we get a HTTP 400 bad request with empty proto."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -364,7 +391,7 @@ async def test_x_forwarded_proto_incorrect_number_of_elements(
"""Test that we get a HTTP 400 bad request with incorrect number of elements.""" """Test that we get a HTTP 400 bad request with incorrect number of elements."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -397,7 +424,7 @@ async def test_x_forwarded_host_without_trusted_proxy(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, []) async_setup_forwarded(app, True, [])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -421,7 +448,7 @@ async def test_x_forwarded_host_with_trusted_proxy(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -446,7 +473,7 @@ async def test_x_forwarded_host_not_processed_without_for(aiohttp_client):
app = web.Application() app = web.Application()
app.router.add_get("/", handler) app.router.add_get("/", handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get("/", headers={X_FORWARDED_HOST: "example.com"}) resp = await mock_api_client.get("/", headers={X_FORWARDED_HOST: "example.com"})
@ -458,7 +485,7 @@ async def test_x_forwarded_host_with_multiple_headers(aiohttp_client, caplog):
"""Test that we get a HTTP 400 bad request with multiple headers.""" """Test that we get a HTTP 400 bad request with multiple headers."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(
@ -478,7 +505,7 @@ async def test_x_forwarded_host_with_empty_header(aiohttp_client, caplog):
"""Test that we get a HTTP 400 bad request with empty host value.""" """Test that we get a HTTP 400 bad request with empty host value."""
app = web.Application() app = web.Application()
app.router.add_get("/", mock_handler) app.router.add_get("/", mock_handler)
async_setup_forwarded(app, [ip_network("127.0.0.1")]) async_setup_forwarded(app, True, [ip_network("127.0.0.1")])
mock_api_client = await aiohttp_client(app) mock_api_client = await aiohttp_client(app)
resp = await mock_api_client.get( resp = await mock_api_client.get(