Drop block on local proxies from HA Cloud (#59334)

This commit is contained in:
Paulus Schoutsen 2021-11-14 08:11:12 -08:00 committed by GitHub
parent 26f3d50a32
commit afa7ca1222
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 0 additions and 209 deletions

View File

@ -63,13 +63,5 @@ MODE_PROD = "production"
DISPATCHER_REMOTE_UPDATE = "cloud_remote_update" DISPATCHER_REMOTE_UPDATE = "cloud_remote_update"
class InvalidTrustedNetworks(Exception):
"""Raised when invalid trusted networks config."""
class InvalidTrustedProxies(Exception):
"""Raised when invalid trusted proxies config."""
class RequireRelink(Exception): class RequireRelink(Exception):
"""The skill needs to be relinked.""" """The skill needs to be relinked."""

View File

@ -33,8 +33,6 @@ from .const import (
PREF_GOOGLE_SECURE_DEVICES_PIN, PREF_GOOGLE_SECURE_DEVICES_PIN,
PREF_TTS_DEFAULT_VOICE, PREF_TTS_DEFAULT_VOICE,
REQUEST_TIMEOUT, REQUEST_TIMEOUT,
InvalidTrustedNetworks,
InvalidTrustedProxies,
RequireRelink, RequireRelink,
) )
@ -42,14 +40,6 @@ _LOGGER = logging.getLogger(__name__)
_CLOUD_ERRORS = { _CLOUD_ERRORS = {
InvalidTrustedNetworks: (
HTTPStatus.INTERNAL_SERVER_ERROR,
"Remote UI not compatible with 127.0.0.1/::1 as a trusted network.",
),
InvalidTrustedProxies: (
HTTPStatus.INTERNAL_SERVER_ERROR,
"Remote UI not compatible with 127.0.0.1/::1 as trusted proxies.",
),
asyncio.TimeoutError: ( asyncio.TimeoutError: (
HTTPStatus.BAD_GATEWAY, HTTPStatus.BAD_GATEWAY,
"Unable to reach the Home Assistant cloud.", "Unable to reach the Home Assistant cloud.",

View File

@ -1,8 +1,6 @@
"""Preference management for cloud.""" """Preference management for cloud."""
from __future__ import annotations from __future__ import annotations
from ipaddress import ip_address
from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.auth.models import User from homeassistant.auth.models import User
from homeassistant.core import callback from homeassistant.core import callback
@ -34,8 +32,6 @@ from .const import (
PREF_SHOULD_EXPOSE, PREF_SHOULD_EXPOSE,
PREF_TTS_DEFAULT_VOICE, PREF_TTS_DEFAULT_VOICE,
PREF_USERNAME, PREF_USERNAME,
InvalidTrustedNetworks,
InvalidTrustedProxies,
) )
STORAGE_KEY = DOMAIN STORAGE_KEY = DOMAIN
@ -110,14 +106,6 @@ class CloudPreferences:
if value is not UNDEFINED: if value is not UNDEFINED:
prefs[key] = value prefs[key] = value
if remote_enabled is True and self._has_local_trusted_network:
prefs[PREF_ENABLE_REMOTE] = False
raise InvalidTrustedNetworks
if remote_enabled is True and self._has_local_trusted_proxies:
prefs[PREF_ENABLE_REMOTE] = False
raise InvalidTrustedProxies
await self._save_prefs(prefs) await self._save_prefs(prefs)
async def async_update_google_entity_config( async def async_update_google_entity_config(
@ -217,9 +205,6 @@ class CloudPreferences:
if not self._prefs.get(PREF_ENABLE_REMOTE, False): if not self._prefs.get(PREF_ENABLE_REMOTE, False):
return False return False
if self._has_local_trusted_network or self._has_local_trusted_proxies:
return False
return True return True
@property @property
@ -310,38 +295,6 @@ class CloudPreferences:
# an image was restored without restoring the cloud prefs. # an image was restored without restoring the cloud prefs.
return await self._hass.auth.async_get_user(user_id) return await self._hass.auth.async_get_user(user_id)
@property
def _has_local_trusted_network(self) -> bool:
"""Return if we allow localhost to bypass auth."""
local4 = ip_address("127.0.0.1")
local6 = ip_address("::1")
for prv in self._hass.auth.auth_providers:
if prv.type != "trusted_networks":
continue
for network in prv.trusted_networks:
if local4 in network or local6 in network:
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
async def _save_prefs(self, prefs): async def _save_prefs(self, prefs):
"""Save preferences to disk.""" """Save preferences to disk."""
self._prefs = prefs self._prefs = prefs

View File

@ -1,7 +1,6 @@
"""Tests for the HTTP API for the cloud component.""" """Tests for the HTTP API for the cloud component."""
import asyncio import asyncio
from http import HTTPStatus from http import HTTPStatus
from ipaddress import ip_network
from unittest.mock import AsyncMock, MagicMock, Mock, patch from unittest.mock import AsyncMock, MagicMock, Mock, patch
import aiohttp import aiohttp
@ -11,7 +10,6 @@ from hass_nabucasa.const import STATE_CONNECTED
from jose import jwt from jose import jwt
import pytest import pytest
from homeassistant.auth.providers import trusted_networks as tn_auth
from homeassistant.components.alexa import errors as alexa_errors from homeassistant.components.alexa import errors as alexa_errors
from homeassistant.components.alexa.entities import LightCapabilities from homeassistant.components.alexa.entities import LightCapabilities
from homeassistant.components.cloud.const import DOMAIN, RequireRelink from homeassistant.components.cloud.const import DOMAIN, RequireRelink
@ -558,100 +556,6 @@ async def test_enabling_remote(hass, hass_ws_client, setup_api, mock_cloud_login
assert len(mock_disconnect.mock_calls) == 1 assert len(mock_disconnect.mock_calls) == 1
async def test_enabling_remote_trusted_networks_local4(
hass, hass_ws_client, setup_api, mock_cloud_login
):
"""Test we cannot enable remote UI when trusted networks active."""
# pylint: disable=protected-access
hass.auth._providers[
("trusted_networks", None)
] = tn_auth.TrustedNetworksAuthProvider(
hass,
None,
tn_auth.CONFIG_SCHEMA(
{"type": "trusted_networks", "trusted_networks": ["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"] == HTTPStatus.INTERNAL_SERVER_ERROR
assert (
response["error"]["message"]
== "Remote UI not compatible with 127.0.0.1/::1 as a trusted network."
)
assert len(mock_connect.mock_calls) == 0
async def test_enabling_remote_trusted_networks_local6(
hass, hass_ws_client, setup_api, mock_cloud_login
):
"""Test we cannot enable remote UI when trusted networks active."""
# pylint: disable=protected-access
hass.auth._providers[
("trusted_networks", None)
] = tn_auth.TrustedNetworksAuthProvider(
hass,
None,
tn_auth.CONFIG_SCHEMA(
{"type": "trusted_networks", "trusted_networks": ["::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"] == HTTPStatus.INTERNAL_SERVER_ERROR
assert (
response["error"]["message"]
== "Remote UI not compatible with 127.0.0.1/::1 as a trusted network."
)
assert len(mock_connect.mock_calls) == 0
async def test_enabling_remote_trusted_networks_other(
hass, hass_ws_client, setup_api, mock_cloud_login
):
"""Test we can enable remote UI when trusted networks active."""
# pylint: disable=protected-access
hass.auth._providers[
("trusted_networks", None)
] = tn_auth.TrustedNetworksAuthProvider(
hass,
None,
tn_auth.CONFIG_SCHEMA(
{"type": "trusted_networks", "trusted_networks": ["192.168.0.0/24"]}
),
)
client = await hass_ws_client(hass)
cloud = hass.data[DOMAIN]
with patch("hass_nabucasa.remote.RemoteUI.connect") as mock_connect:
await client.send_json({"id": 5, "type": "cloud/remote/connect"})
response = await client.receive_json()
assert response["success"]
assert cloud.client.remote_autostart
assert len(mock_connect.mock_calls) == 1
async def test_list_google_entities(hass, hass_ws_client, setup_api, mock_cloud_login): async def test_list_google_entities(hass, hass_ws_client, setup_api, mock_cloud_login):
"""Test that we can list Google entities.""" """Test that we can list Google entities."""
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
@ -729,54 +633,6 @@ async def test_update_google_entity(hass, hass_ws_client, setup_api, mock_cloud_
} }
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"] == HTTPStatus.INTERNAL_SERVER_ERROR
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"] == HTTPStatus.INTERNAL_SERVER_ERROR
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_list_alexa_entities(hass, hass_ws_client, setup_api, mock_cloud_login): async def test_list_alexa_entities(hass, hass_ws_client, setup_api, mock_cloud_login):
"""Test that we can list Alexa entities.""" """Test that we can list Alexa entities."""
client = await hass_ws_client(hass) client = await hass_ws_client(hass)