loopback_ban_exempt: force-add egen custom_component (custom_components er ellers gitignored)

This commit is contained in:
2026-07-30 17:00:03 +02:00
parent 8e99492e2c
commit b61e85784d
2 changed files with 73 additions and 0 deletions
@@ -0,0 +1,64 @@
"""Exempt loopback addresses (127.0.0.1 / ::1) from Home Assistant's HTTP login-ban handling.
Home Assistant has no built-in way to whitelist an IP from the ``http`` ban
system. Local tools running on the same host (e.g. a VS Code session that
queries ``/api/config`` without a token) therefore show up as failed login
attempts from ``127.0.0.1`` and can eventually get localhost banned, which
would break legitimate local access.
This integration wraps ``process_wrong_login`` so that any request whose remote
address is a loopback address is silently ignored. External addresses are still
processed normally, so ``login_attempts_threshold`` protection stays intact.
"""
from __future__ import annotations
import logging
from ipaddress import ip_address
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
_LOGGER = logging.getLogger(__name__)
DOMAIN = "loopback_ban_exempt"
# Modules that hold a reference to process_wrong_login (either the definition
# itself or an ``from ... import process_wrong_login``). All must be patched.
_TARGET_MODULES = (
"homeassistant.components.http.ban",
"homeassistant.components.auth.login_flow",
"homeassistant.components.websocket_api.auth",
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Patch the ban handler to skip loopback addresses."""
from homeassistant.components.http import ban as ban_module
original = ban_module.process_wrong_login
async def process_wrong_login(request):
"""Ignore loopback, otherwise defer to the original handler."""
remote = getattr(request, "remote", None)
try:
if remote is not None and ip_address(remote).is_loopback:
return None
except ValueError:
pass
return await original(request)
patched = 0
for mod_path in _TARGET_MODULES:
try:
module = __import__(mod_path, fromlist=["process_wrong_login"])
except ImportError:
continue
if getattr(module, "process_wrong_login", None) is not None:
module.process_wrong_login = process_wrong_login
patched += 1
_LOGGER.info(
"Loopback ban exemption active (patched %s reference(s))", patched
)
return True
@@ -0,0 +1,9 @@
{
"domain": "loopback_ban_exempt",
"name": "Loopback Ban Exempt",
"version": "1.0.0",
"documentation": "https://www.home-assistant.io/integrations/http/",
"dependencies": ["http"],
"codeowners": [],
"iot_class": "local_push"
}