From b61a74787639df09538971337892ce21888c0975 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 15 Feb 2021 20:42:03 +0100 Subject: [PATCH] Kernel modules add-on flag need no extra cap (#2569) --- supervisor/addons/model.py | 3 ++- supervisor/addons/utils.py | 35 ++++++++++++++--------------------- supervisor/addons/validate.py | 4 ++-- supervisor/const.py | 24 ------------------------ supervisor/docker/addon.py | 19 +++++++++++++++++-- supervisor/docker/const.py | 17 +++++++++++++++++ 6 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 supervisor/docker/const.py diff --git a/supervisor/addons/model.py b/supervisor/addons/model.py index 7720ddba6..1e9fc3120 100644 --- a/supervisor/addons/model.py +++ b/supervisor/addons/model.py @@ -71,6 +71,7 @@ from ..const import ( AddonStartup, ) from ..coresys import CoreSys, CoreSysAttributes +from ..docker.const import Capabilities from .options import AddonOptions, UiOptions from .validate import RE_SERVICE, RE_VOLUME @@ -307,7 +308,7 @@ class AddonModel(CoreSysAttributes, ABC): return self.data.get(ATTR_ENVIRONMENT) @property - def privileged(self) -> List[str]: + def privileged(self) -> List[Capabilities]: """Return list of privilege.""" return self.data.get(ATTR_PRIVILEGED, []) diff --git a/supervisor/addons/utils.py b/supervisor/addons/utils.py index 434dc7724..dfdea5002 100644 --- a/supervisor/addons/utils.py +++ b/supervisor/addons/utils.py @@ -6,18 +6,8 @@ import logging from pathlib import Path from typing import TYPE_CHECKING -from ..const import ( - PRIVILEGED_DAC_READ_SEARCH, - PRIVILEGED_NET_ADMIN, - PRIVILEGED_SYS_ADMIN, - PRIVILEGED_SYS_MODULE, - PRIVILEGED_SYS_PTRACE, - PRIVILEGED_SYS_RAWIO, - ROLE_ADMIN, - ROLE_MANAGER, - SECURITY_DISABLE, - SECURITY_PROFILE, -) +from ..const import ROLE_ADMIN, ROLE_MANAGER, SECURITY_DISABLE, SECURITY_PROFILE +from ..docker.const import Capabilities if TYPE_CHECKING: from .model import AddonModel @@ -46,16 +36,19 @@ def rating_security(addon: AddonModel) -> int: rating += 1 # Privileged options - if any( - privilege in addon.privileged - for privilege in ( - PRIVILEGED_NET_ADMIN, - PRIVILEGED_SYS_ADMIN, - PRIVILEGED_SYS_RAWIO, - PRIVILEGED_SYS_PTRACE, - PRIVILEGED_SYS_MODULE, - PRIVILEGED_DAC_READ_SEARCH, + if ( + any( + privilege in addon.privileged + for privilege in ( + Capabilities.NET_ADMIN, + Capabilities.SYS_ADMIN, + Capabilities.SYS_RAWIO, + Capabilities.SYS_PTRACE, + Capabilities.SYS_MODULE, + Capabilities.DAC_READ_SEARCH, + ) ) + or addon.with_kernel_modules ): rating += -1 diff --git a/supervisor/addons/validate.py b/supervisor/addons/validate.py index 844854d22..c86df3861 100644 --- a/supervisor/addons/validate.py +++ b/supervisor/addons/validate.py @@ -82,7 +82,6 @@ from ..const import ( ATTR_VIDEO, ATTR_WATCHDOG, ATTR_WEBUI, - PRIVILEGED_ALL, ROLE_ALL, ROLE_DEFAULT, AddonBoot, @@ -91,6 +90,7 @@ from ..const import ( AddonState, ) from ..discovery.validate import valid_discovery_service +from ..docker.const import Capabilities from ..validate import ( docker_image, docker_ports, @@ -233,7 +233,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema( vol.Optional(ATTR_TMPFS, default=False): vol.Boolean(), vol.Optional(ATTR_MAP, default=list): [vol.Match(RE_VOLUME)], vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): str}, - vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)], + vol.Optional(ATTR_PRIVILEGED): [vol.Coerce(Capabilities)], vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(), vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(), vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(), diff --git a/supervisor/const.py b/supervisor/const.py index b053217b8..726de1397 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -328,30 +328,6 @@ SECURITY_PROFILE = "profile" SECURITY_DEFAULT = "default" SECURITY_DISABLE = "disable" -PRIVILEGED_DAC_READ_SEARCH = "DAC_READ_SEARCH" -PRIVILEGED_IPC_LOCK = "IPC_LOCK" -PRIVILEGED_NET_ADMIN = "NET_ADMIN" -PRIVILEGED_SYS_ADMIN = "SYS_ADMIN" -PRIVILEGED_SYS_MODULE = "SYS_MODULE" -PRIVILEGED_SYS_NICE = "SYS_NICE" -PRIVILEGED_SYS_PTRACE = "SYS_PTRACE" -PRIVILEGED_SYS_RAWIO = "SYS_RAWIO" -PRIVILEGED_SYS_RESOURCE = "SYS_RESOURCE" -PRIVILEGED_SYS_TIME = "SYS_TIME" - -PRIVILEGED_ALL = [ - PRIVILEGED_NET_ADMIN, - PRIVILEGED_SYS_ADMIN, - PRIVILEGED_SYS_RAWIO, - PRIVILEGED_IPC_LOCK, - PRIVILEGED_SYS_TIME, - PRIVILEGED_SYS_NICE, - PRIVILEGED_SYS_RESOURCE, - PRIVILEGED_SYS_PTRACE, - PRIVILEGED_SYS_MODULE, - PRIVILEGED_DAC_READ_SEARCH, -] - ROLE_DEFAULT = "default" ROLE_HOMEASSISTANT = "homeassistant" ROLE_BACKUP = "backup" diff --git a/supervisor/docker/addon.py b/supervisor/docker/addon.py index 695f72fe3..62d38db44 100644 --- a/supervisor/docker/addon.py +++ b/supervisor/docker/addon.py @@ -6,7 +6,7 @@ from ipaddress import IPv4Address, ip_address import logging import os from pathlib import Path -from typing import TYPE_CHECKING, Awaitable, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Awaitable, Dict, List, Optional, Set, Union from awesomeversion import AwesomeVersion import docker @@ -31,6 +31,7 @@ from ..exceptions import CoreDNSError, DockerError, DockerNotFound, HardwareNotF from ..hardware.const import PolicyGroup from ..resolution.const import ContextType, IssueType, SuggestionType from ..utils import process_lock +from .const import Capabilities from .interface import DockerInterface if TYPE_CHECKING: @@ -225,6 +226,20 @@ class DockerAddon(DockerInterface): return "host" return None + @property + def capabilities(self) -> Optional[List[str]]: + """Generate needed capabilities.""" + capabilities: Set[Capabilities] = set(self.addon.privileged) + + # Need work with kernel modules + if self.addon.with_kernel_modules: + capabilities.add(Capabilities.SYS_MODULE) + + # Return None if no capabilities is present + if capabilities: + return [cap.value for cap in capabilities] + return None + @property def volumes(self) -> Dict[str, Dict[str, str]]: """Generate volumes for mappings.""" @@ -386,7 +401,7 @@ class DockerAddon(DockerInterface): ports=self.ports, extra_hosts=self.network_mapping, device_cgroup_rules=self.cgroups_rules, - cap_add=self.addon.privileged, + cap_add=self.capabilities, security_opt=self.security_opt, environment=self.environment, volumes=self.volumes, diff --git a/supervisor/docker/const.py b/supervisor/docker/const.py new file mode 100644 index 000000000..857aa97fc --- /dev/null +++ b/supervisor/docker/const.py @@ -0,0 +1,17 @@ +"""Docker constants.""" +from enum import Enum + + +class Capabilities(str, Enum): + """Linux Capabilities.""" + + DAC_READ_SEARCH = "DAC_READ_SEARCH" + IPC_LOCK = "IPC_LOCK" + NET_ADMIN = "NET_ADMIN" + SYS_ADMIN = "SYS_ADMIN" + SYS_MODULE = "SYS_MODULE" + SYS_NICE = "SYS_NICE" + SYS_PTRACE = "SYS_PTRACE" + SYS_RAWIO = "SYS_RAWIO" + SYS_RESOURCE = "SYS_RESOURCE" + SYS_TIME = "SYS_TIME"