From 4e255286af89297ebd313bb74ea79ce0c45f1842 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 5 Nov 2025 12:13:56 +0100 Subject: [PATCH] Create issue to warn against using http.server_host in supervised installs (#155837) --- homeassistant/components/http/__init__.py | 15 +++++- homeassistant/components/http/strings.json | 4 ++ tests/components/http/test_init.py | 60 +++++++++++++++++++++- tests/scripts/test_check_config.py | 1 - 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index f048d571b9c..3939386a9de 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -38,6 +38,7 @@ from homeassistant.const import ( from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, issue_registry as ir, storage +from homeassistant.helpers.hassio import is_hassio from homeassistant.helpers.http import ( KEY_ALLOW_CONFIGURED_CORS, KEY_AUTHENTICATED, # noqa: F401 @@ -109,7 +110,7 @@ HTTP_SCHEMA: Final = vol.All( cv.deprecated(CONF_BASE_URL), vol.Schema( { - vol.Optional(CONF_SERVER_HOST, default=_DEFAULT_BIND): vol.All( + vol.Optional(CONF_SERVER_HOST): vol.All( cv.ensure_list, vol.Length(min=1), [cv.string] ), vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port, @@ -207,7 +208,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: if conf is None: conf = cast(ConfData, HTTP_SCHEMA({})) - server_host = conf[CONF_SERVER_HOST] + if CONF_SERVER_HOST in conf and is_hassio(hass): + ir.async_create_issue( + hass, + DOMAIN, + "server_host_may_break_hassio", + is_fixable=False, + severity=ir.IssueSeverity.ERROR, + translation_key="server_host_may_break_hassio", + ) + + server_host = conf.get(CONF_SERVER_HOST, _DEFAULT_BIND) server_port = conf[CONF_SERVER_PORT] ssl_certificate = conf.get(CONF_SSL_CERTIFICATE) ssl_peer_certificate = conf.get(CONF_SSL_PEER_CERTIFICATE) diff --git a/homeassistant/components/http/strings.json b/homeassistant/components/http/strings.json index 43e1c6816a1..199515dda98 100644 --- a/homeassistant/components/http/strings.json +++ b/homeassistant/components/http/strings.json @@ -1,5 +1,9 @@ { "issues": { + "server_host_may_break_hassio": { + "description": "The `server_host` configuration option in the HTTP integration is prone to break the communication between Home Assistant Core and Supervisor, and will be removed in a future release.\n\nIf you are using this option to bind Home Assistant to specific network interfaces, please remove it from your configuration. Home Assistant will automatically bind to all available interfaces by default.\n\nIf you have specific networking requirements, consider using firewall rules or other network configuration to control access to Home Assistant.", + "title": "The `server_host` HTTP configuration may break Home Assistant Core - Supervisor communication" + }, "ssl_configured_without_configured_urls": { "description": "Home Assistant detected that SSL has been set up on your instance, however, no custom external internet URL has been set.\n\nThis may result in unexpected behavior. Text-to-speech may fail, and integrations may not be able to connect back to your instance correctly.\n\nTo address this issue, go to Settings > System > Network; under the \"Home Assistant URL\" section, configure your new \"Internet\" and \"Local network\" addresses that match your new SSL configuration.", "title": "SSL is configured without an external URL or internal URL" diff --git a/tests/components/http/test_init.py b/tests/components/http/test_init.py index 195a291b140..5cf8b3b10b5 100644 --- a/tests/components/http/test_init.py +++ b/tests/components/http/test_init.py @@ -7,7 +7,7 @@ from http import HTTPStatus from ipaddress import ip_network import logging from pathlib import Path -from unittest.mock import Mock, patch +from unittest.mock import ANY, Mock, patch import pytest @@ -667,3 +667,61 @@ async def test_ssl_issue_urls_configured( "http", "ssl_configured_without_configured_urls", ) not in issue_registry.issues + + +@pytest.mark.parametrize( + ( + "hassio", + "http_config", + "expected_serverhost", + "expected_issues", + ), + [ + (False, {}, ["0.0.0.0", "::"], set()), + (False, {"server_host": "0.0.0.0"}, ["0.0.0.0"], set()), + (True, {}, ["0.0.0.0", "::"], set()), + ( + True, + {"server_host": "0.0.0.0"}, + [ + "0.0.0.0", + ], + {("http", "server_host_may_break_hassio")}, + ), + ], +) +async def test_server_host( + hass: HomeAssistant, + hassio: bool, + issue_registry: ir.IssueRegistry, + http_config: dict, + expected_serverhost: list, + expected_issues: set[tuple[str, str]], +) -> None: + """Test server_host behavior.""" + mock_server = Mock() + with ( + patch("homeassistant.components.http.is_hassio", return_value=hassio), + patch( + "asyncio.BaseEventLoop.create_server", return_value=mock_server + ) as mock_create_server, + ): + assert await async_setup_component( + hass, + "http", + {"http": http_config}, + ) + await hass.async_start() + await hass.async_block_till_done() + + mock_create_server.assert_called_once_with( + ANY, + expected_serverhost, + 8123, + ssl=None, + backlog=128, + reuse_address=None, + reuse_port=None, + ) + + assert set(issue_registry.issues) == expected_issues diff --git a/tests/scripts/test_check_config.py b/tests/scripts/test_check_config.py index b2dcb004bcb..9ca3fd3e55b 100644 --- a/tests/scripts/test_check_config.py +++ b/tests/scripts/test_check_config.py @@ -137,7 +137,6 @@ def test_secrets() -> None: "server_port": 8123, "ssl_profile": "modern", "use_x_frame_options": True, - "server_host": ["0.0.0.0", "::"], } assert res["secret_cache"] == { get_test_config_dir("secrets.yaml"): {"http_pw": "http://google.com"}