Check cloud trusted proxies (#24395)
parent
7887d6d6e4
commit
f77514c6f2
|
@ -38,3 +38,7 @@ DISPATCHER_REMOTE_UPDATE = 'cloud_remote_update'
|
|||
|
||||
class InvalidTrustedNetworks(Exception):
|
||||
"""Raised when invalid trusted networks config."""
|
||||
|
||||
|
||||
class InvalidTrustedProxies(Exception):
|
||||
"""Raised when invalid trusted proxies config."""
|
||||
|
|
|
@ -18,7 +18,8 @@ from homeassistant.components.google_assistant import helpers as google_helpers
|
|||
|
||||
from .const import (
|
||||
DOMAIN, REQUEST_TIMEOUT, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE,
|
||||
PREF_GOOGLE_SECURE_DEVICES_PIN, InvalidTrustedNetworks)
|
||||
PREF_GOOGLE_SECURE_DEVICES_PIN, InvalidTrustedNetworks,
|
||||
InvalidTrustedProxies)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -52,7 +53,10 @@ SCHEMA_WS_HOOK_DELETE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
|
|||
_CLOUD_ERRORS = {
|
||||
InvalidTrustedNetworks:
|
||||
(500, 'Remote UI not compatible with 127.0.0.1/::1'
|
||||
' as a trusted network.')
|
||||
' as a trusted network.'),
|
||||
InvalidTrustedProxies:
|
||||
(500, 'Remote UI not compatible with 127.0.0.1/::1'
|
||||
' as trusted proxies.'),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from .const import (
|
|||
PREF_GOOGLE_SECURE_DEVICES_PIN, PREF_CLOUDHOOKS, PREF_CLOUD_USER,
|
||||
PREF_GOOGLE_ENTITY_CONFIGS, PREF_OVERRIDE_NAME, PREF_DISABLE_2FA,
|
||||
PREF_ALIASES, PREF_SHOULD_EXPOSE,
|
||||
InvalidTrustedNetworks)
|
||||
InvalidTrustedNetworks, InvalidTrustedProxies)
|
||||
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
|
@ -59,6 +59,9 @@ class CloudPreferences:
|
|||
if remote_enabled is True and self._has_local_trusted_network:
|
||||
raise InvalidTrustedNetworks
|
||||
|
||||
if remote_enabled is True and self._has_local_trusted_proxies:
|
||||
raise InvalidTrustedProxies
|
||||
|
||||
await self._store.async_save(self._prefs)
|
||||
|
||||
async def async_update_google_entity_config(
|
||||
|
@ -112,7 +115,7 @@ class CloudPreferences:
|
|||
if not enabled:
|
||||
return False
|
||||
|
||||
if self._has_local_trusted_network:
|
||||
if self._has_local_trusted_network or self._has_local_trusted_proxies:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -162,3 +165,18 @@ class CloudPreferences:
|
|||
return True
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def _has_local_trusted_proxies(self) -> bool:
|
||||
"""Return if we allow localhost to be a proxy and use its data."""
|
||||
if not hasattr(self._hass, 'http'):
|
||||
return False
|
||||
|
||||
local4 = ip_address('127.0.0.1')
|
||||
local6 = ip_address('::1')
|
||||
|
||||
if any(local4 in nwk or local6 in nwk
|
||||
for nwk in self._hass.http.trusted_proxies):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -228,6 +228,7 @@ class HomeAssistantHTTP:
|
|||
self.ssl_key = ssl_key
|
||||
self.server_host = server_host
|
||||
self.server_port = server_port
|
||||
self.trusted_proxies = trusted_proxies
|
||||
self.is_ban_enabled = is_ban_enabled
|
||||
self.ssl_profile = ssl_profile
|
||||
self._handler = None
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""Tests for the HTTP API for the cloud component."""
|
||||
import asyncio
|
||||
from unittest.mock import patch, MagicMock
|
||||
from ipaddress import ip_network
|
||||
|
||||
import pytest
|
||||
from jose import jwt
|
||||
|
@ -672,7 +673,7 @@ async def test_enabling_remote_trusted_networks_local6(
|
|||
|
||||
async def test_enabling_remote_trusted_networks_other(
|
||||
hass, hass_ws_client, setup_api, mock_cloud_login):
|
||||
"""Test we cannot enable remote UI when trusted networks active."""
|
||||
"""Test we can enable remote UI when trusted networks active."""
|
||||
hass.auth._providers[('trusted_networks', None)] = \
|
||||
tn_auth.TrustedNetworksAuthProvider(
|
||||
hass, None, tn_auth.CONFIG_SCHEMA({
|
||||
|
@ -749,3 +750,53 @@ async def test_update_google_entity(
|
|||
'aliases': ['lefty', 'righty'],
|
||||
'disable_2fa': False,
|
||||
}
|
||||
|
||||
|
||||
async def test_enabling_remote_trusted_proxies_local4(
|
||||
hass, hass_ws_client, setup_api, mock_cloud_login):
|
||||
"""Test we cannot enable remote UI when trusted networks active."""
|
||||
hass.http.trusted_proxies.append(ip_network('127.0.0.1'))
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
with patch(
|
||||
'hass_nabucasa.remote.RemoteUI.connect',
|
||||
side_effect=AssertionError
|
||||
) as mock_connect:
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/remote/connect',
|
||||
})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert not response['success']
|
||||
assert response['error']['code'] == 500
|
||||
assert response['error']['message'] == \
|
||||
'Remote UI not compatible with 127.0.0.1/::1 as trusted proxies.'
|
||||
|
||||
assert len(mock_connect.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_enabling_remote_trusted_proxies_local6(
|
||||
hass, hass_ws_client, setup_api, mock_cloud_login):
|
||||
"""Test we cannot enable remote UI when trusted networks active."""
|
||||
hass.http.trusted_proxies.append(ip_network('::1'))
|
||||
|
||||
client = await hass_ws_client(hass)
|
||||
|
||||
with patch(
|
||||
'hass_nabucasa.remote.RemoteUI.connect',
|
||||
side_effect=AssertionError
|
||||
) as mock_connect:
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/remote/connect',
|
||||
})
|
||||
response = await client.receive_json()
|
||||
|
||||
assert not response['success']
|
||||
assert response['error']['code'] == 500
|
||||
assert response['error']['message'] == \
|
||||
'Remote UI not compatible with 127.0.0.1/::1 as trusted proxies.'
|
||||
|
||||
assert len(mock_connect.mock_calls) == 0
|
||||
|
|
Loading…
Reference in New Issue