Use Unix socket for Supervisor to Core communication#6590
Use Unix socket for Supervisor to Core communication#6590
Conversation
|
What's this waiting on btw @agners ? It looks like both PRs are in draft and there's a good number of comments on the other. Is this dependent on something else in the epic or is it just a time issue? |
Yeah it's waiting on Core part to complete. I've prioritized some release things and other bug fixes so just a time thing. I'll get back to it next week. |
c6c3917 to
bffc3e8
Compare
bffc3e8 to
9f5deeb
Compare
9f5deeb to
7c47347
Compare
There was a problem hiding this comment.
Pull request overview
Switches Supervisor→Home Assistant Core internal REST/WebSocket traffic from TCP (port 8123) to a Unix domain socket when Core is new enough, keeping TCP as a fallback for older versions and explicitly excluding the Landingpage “version” from version comparisons.
Changes:
- Add version-gated Unix-socket transport support to
HomeAssistantAPI(session + URL selection) and close the Unix session on Supervisor shutdown. - Update Core WebSocket client and proxy WebSocket code to support “trusted transport” connections (no auth handshake) vs TCP token-auth connections.
- Update Docker Core container configuration (mount + env var) and extend tests for mount/env behavior and WebSocket client attribute rename.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
supervisor/homeassistant/api.py |
Adds Unix-socket-aware session/url selection and uses it for API requests (token auth only for TCP). |
supervisor/homeassistant/websocket.py |
Splits WS connection logic into Unix-socket no-auth vs TCP auth; renames _client→client. |
supervisor/api/proxy.py |
Reuses WSClient for proxy WebSocket connection setup, including Unix-socket mode. |
supervisor/docker/const.py |
Introduces ENV_CORE_API_SOCKET and MOUNT_CORE_RUN; extends bind options serialization. |
supervisor/docker/homeassistant.py |
Mounts /run/supervisor into Core and sets socket path env var when supported. |
supervisor/const.py |
Adds SOCKET_CORE path constant used for Unix connector. |
supervisor/core.py |
Ensures the Unix-socket ClientSession is closed during Supervisor shutdown. |
tests/docker/test_homeassistant.py |
Updates expected mounts and adds coverage for the Core socket env var. |
tests/conftest.py |
Adjusts WebSocket client mocking for renamed attribute. |
tests/homeassistant/test_module.py |
Updates tests to use websocket.client instead of protected _client. |
| # Supervisor <-> Core communication socket | ||
| MOUNT_CORE_RUN, |
There was a problem hiding this comment.
MOUNT_CORE_RUN is added for all non-LANDINGPAGE Core containers, even when use_unix_socket is false. Since this mount exposes /run/supervisor from the host into the Core container and is only needed for the Unix socket feature, consider gating MOUNT_CORE_RUN behind self.sys_homeassistant.api.use_unix_socket to minimize unnecessary host filesystem exposure on older Core versions.
| # Supervisor <-> Core communication socket | |
| MOUNT_CORE_RUN, | |
| ] | |
| ) | |
| if self.sys_homeassistant.api.use_unix_socket: | |
| # Supervisor <-> Core communication socket | |
| mounts.append(MOUNT_CORE_RUN) | |
| mounts.extend( | |
| [ |
There was a problem hiding this comment.
Does it matter much 🤷 I think I'd just always mount it for simplicity.
Switch internal Supervisor-to-Core HTTP and WebSocket communication from TCP (port 8123) to a Unix domain socket. The existing /run/supervisor directory on the host (already mounted at /run/os inside the Supervisor container) is bind-mounted into the Core container at /run/supervisor. Core receives the socket path via the SUPERVISOR_CORE_API_SOCKET environment variable, creates the socket there, and Supervisor connects to it via aiohttp.UnixConnector at /run/os/core.sock. Since the Unix socket is only reachable by processes on the same host, requests arriving over it are implicitly trusted and authenticated as the existing Supervisor system user. This removes the token round-trip where Supervisor had to obtain and send Bearer tokens on every Core API call. WebSocket connections are likewise authenticated implicitly, skipping the auth_required/auth handshake. Key design decisions: - Version-gated by CORE_UNIX_SOCKET_MIN_VERSION so older Core versions transparently continue using TCP with token auth - LANDINGPAGE is explicitly excluded (not a CalVer version) - Hard-fails with a clear error if the socket file is unexpectedly missing when Unix socket communication is expected - WSClient.connect() for Unix socket (no auth) and WSClient.connect_with_auth() for TCP (token auth) separate the two connection modes cleanly - Token refresh always uses the TCP websession since it is inherently a TCP/Bearer-auth operation - Logs which transport (Unix socket vs TCP) is being used on first request Closes #6626 Related Core PR: home-assistant/core#163907 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
7c47347 to
2e7c612
Compare
Ensure the underlying WebSocket connection is closed before raising when the handshake produces an unexpected message. Also validate that the first TCP message is auth_required before sending credentials. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Split use_unix_socket into two properties to handle the Supervisor upgrade transition where Core is still running with a container started by the old Supervisor (without SUPERVISOR_CORE_API_SOCKET): - supports_unix_socket: version check only, used when creating the Core container to decide whether to set the env var - use_unix_socket: version check + running container env check, used for communication decisions This ensures TCP fallback during the upgrade transition while still hard-failing if the socket is missing after Supervisor configured Core to use it. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Proposed change
Switch internal Supervisor→Core HTTP and WebSocket communication from TCP (port 8123) to a Unix domain socket when the installed Core version supports it.
The existing
/run/supervisordirectory on the host (already mounted at/run/osinside the Supervisor container) is bind-mounted into the Core container as/run/supervisor. Core receives theSUPERVISOR_CORE_API_SOCKETenvironment variable with the socket path, creates the socket there, and Supervisor connects to it viaaiohttp.UnixConnectorat/run/os/core.sock.Since the Unix socket is only reachable by processes on the same host, requests arriving over it are implicitly trusted and authenticated as the existing "Supervisor" system user. This removes the current token round-trip where Core creates a refresh token, hands it to Supervisor, and Supervisor sends it back as a Bearer token on every Core API call. WebSocket connections are likewise authenticated implicitly, skipping the
auth_required/authhandshake.This reduces attack surface by removing the need for network-based communication between Supervisor and Core, avoids potential port conflicts or
http.server_hostconfiguration issues, and removes authentication overhead for internal IPC.Key design decisions:
HomeAssistantAPIas public properties (session,api_url,ws_url) used by WebSocket and proxy codeCORE_UNIX_SOCKET_MIN_VERSIONso older Core versions continue using TCP transparentlyWSClient.connect()for Unix socket (no auth) andWSClient.connect_with_auth()for TCP (token auth) cleanly separate the two connection modes_websocket_clientreusesWSClientinstead of duplicating the WebSocket auth handshake/run/os↔/run/supervisorhost mount rather than creating new pathsType of change
Additional information
Checklist
ruff format supervisor tests)If API endpoints or add-on configuration are added/changed: