Add host UTS namespace support for Add-Ons (#3596)

* Add host UTS namespace support for Add-Ons

Using the UTS host namespace is useful when running a mDNS responder
which learns the hostname from the gethostname syscall. This way the
add-on can use the system's hostname without further doing.

* Check host_uts default

* Adjust Security rating if host UTS mode and CAP_ADMIN is set

* Don't add hostname to DNS server if UTS namespace is disabled

* Simplify hostname logic

* Update supervisor/docker/addon.py

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

---------

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
This commit is contained in:
Stefan Agner 2023-02-09 23:26:10 +01:00 committed by GitHub
parent d73d8d00f0
commit 919f1e9149
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 30 additions and 2 deletions

View File

@ -34,6 +34,7 @@ from ..const import (
ATTR_HOST_IPC,
ATTR_HOST_NETWORK,
ATTR_HOST_PID,
ATTR_HOST_UTS,
ATTR_IMAGE,
ATTR_INGRESS,
ATTR_INGRESS_STREAM,
@ -308,6 +309,11 @@ class AddonModel(CoreSysAttributes, ABC):
"""Return True if add-on run on host IPC namespace."""
return self.data[ATTR_HOST_IPC]
@property
def host_uts(self) -> bool:
"""Return True if add-on run on host UTS namespace."""
return self.data[ATTR_HOST_UTS]
@property
def host_dbus(self) -> bool:
"""Return True if add-on run on host D-BUS."""

View File

@ -70,6 +70,10 @@ def rating_security(addon: AddonModel) -> int:
if addon.host_pid:
rating += -2
# UTS host namespace allows to set hostname only with SYS_ADMIN
if addon.host_uts and Capabilities.SYS_ADMIN in addon.privileged:
rating += -1
# Docker Access & full Access
if addon.access_docker_api or addon.with_full_access:
rating = 1

View File

@ -41,6 +41,7 @@ from ..const import (
ATTR_HOST_IPC,
ATTR_HOST_NETWORK,
ATTR_HOST_PID,
ATTR_HOST_UTS,
ATTR_IMAGE,
ATTR_INGRESS,
ATTR_INGRESS_ENTRY,
@ -283,6 +284,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
vol.Optional(ATTR_HOST_PID, default=False): vol.Boolean(),
vol.Optional(ATTR_HOST_IPC, default=False): vol.Boolean(),
vol.Optional(ATTR_HOST_UTS, default=False): vol.Boolean(),
vol.Optional(ATTR_HOST_DBUS, default=False): vol.Boolean(),
vol.Optional(ATTR_DEVICES): [str],
vol.Optional(ATTR_UDEV, default=False): vol.Boolean(),

View File

@ -46,6 +46,7 @@ from ..const import (
ATTR_HOST_IPC,
ATTR_HOST_NETWORK,
ATTR_HOST_PID,
ATTR_HOST_UTS,
ATTR_HOSTNAME,
ATTR_ICON,
ATTR_INGRESS,
@ -216,6 +217,7 @@ class APIAddons(CoreSysAttributes):
ATTR_HOST_NETWORK: addon.host_network,
ATTR_HOST_PID: addon.host_pid,
ATTR_HOST_IPC: addon.host_ipc,
ATTR_HOST_UTS: addon.host_uts,
ATTR_HOST_DBUS: addon.host_dbus,
ATTR_PRIVILEGED: addon.privileged,
ATTR_FULL_ACCESS: addon.with_full_access,

View File

@ -181,6 +181,7 @@ ATTR_HOST_INTERNET = "host_internet"
ATTR_HOST_IPC = "host_ipc"
ATTR_HOST_NETWORK = "host_network"
ATTR_HOST_PID = "host_pid"
ATTR_HOST_UTS = "host_uts"
ATTR_HOSTNAME = "hostname"
ATTR_ICON = "icon"
ATTR_ID = "id"

View File

@ -265,6 +265,13 @@ class DockerAddon(DockerInterface):
return "host"
return None
@property
def uts_mode(self) -> str | None:
"""Return UTS mode for add-on."""
if self.addon.host_uts:
return "host"
return None
@property
def capabilities(self) -> list[str] | None:
"""Generate needed capabilities."""
@ -472,18 +479,22 @@ class DockerAddon(DockerInterface):
# Cleanup
self._stop()
# Don't set a hostname if no separate UTS namespace is used
hostname = None if self.uts_mode else self.addon.hostname
# Create & Run container
try:
docker_container = self.sys_docker.run(
self.image,
tag=str(self.addon.version),
name=self.name,
hostname=self.addon.hostname,
hostname=hostname,
detach=True,
init=self.addon.default_init,
stdin_open=self.addon.with_stdin,
network_mode=self.network_mode,
pid_mode=self.pid_mode,
uts_mode=self.uts_mode,
ports=self.ports,
extra_hosts=self.network_mapping,
device_cgroup_rules=self.cgroups_rules,

View File

@ -176,7 +176,8 @@ class DockerAPI:
if dns:
kwargs["dns"] = [str(self.network.dns)]
kwargs["dns_search"] = [DNS_SUFFIX]
kwargs["domainname"] = DNS_SUFFIX
if hostname:
kwargs["domainname"] = DNS_SUFFIX
# Setup network
if not network_mode:

View File

@ -23,6 +23,7 @@ def test_basic_config():
assert not valid_config["host_ipc"]
assert not valid_config["host_dbus"]
assert not valid_config["host_pid"]
assert not valid_config["host_uts"]
assert not valid_config["hassio_api"]
assert not valid_config["homeassistant_api"]