Fix submounts of /dev being read-only with Docker 25+ (#4997)

As described in #4996, Docker 25+ changes made sub-mounts of the /dev
filesystem to be mounted read-only. Revert to the previous behavior by
adjusting the ReadOnlyNonRecursive option. Cleaner way would be to
upstream support for setting this option via Mount class arguments, so
this change is meant to be rather a hotfix for the issue. Even better
approach would be mounting /dev non-recursively, and taking care of
creating all necessary filesystems when creating containers in
Supervisor.
This commit is contained in:
Jan Čermák 2024-04-02 21:07:53 +02:00 committed by GitHub
parent a9265afd4c
commit 906e400ab7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 17 additions and 8 deletions

View File

@ -74,6 +74,7 @@ MOUNT_DBUS = Mount(
type=MountType.BIND, source="/run/dbus", target="/run/dbus", read_only=True type=MountType.BIND, source="/run/dbus", target="/run/dbus", read_only=True
) )
MOUNT_DEV = Mount(type=MountType.BIND, source="/dev", target="/dev", read_only=True) MOUNT_DEV = Mount(type=MountType.BIND, source="/dev", target="/dev", read_only=True)
MOUNT_DEV.setdefault("BindOptions", {})["ReadOnlyNonRecursive"] = True
MOUNT_DOCKER = Mount( MOUNT_DOCKER = Mount(
type=MountType.BIND, type=MountType.BIND,
source="/run/docker.sock", source="/run/docker.sock",

View File

@ -1 +1,6 @@
"""Docker tests.""" """Docker tests."""
from docker.types import Mount
# dev mount with equivalent of bind-recursive=writable specified via dict value
DEV_MOUNT = Mount(type="bind", source="/dev", target="/dev", read_only=True)
DEV_MOUNT["BindOptions"] = {"ReadOnlyNonRecursive": True}

View File

@ -19,6 +19,7 @@ from supervisor.resolution.const import ContextType, IssueType
from supervisor.resolution.data import Issue from supervisor.resolution.data import Issue
from ..common import load_json_fixture from ..common import load_json_fixture
from . import DEV_MOUNT
@pytest.fixture(name="addonsdata_system") @pytest.fixture(name="addonsdata_system")
@ -66,11 +67,8 @@ def test_base_volumes_included(
coresys, addonsdata_system, "basic-addon-config.json" coresys, addonsdata_system, "basic-addon-config.json"
) )
# Dev added as ro # Dev added as ro with bind-recursive=writable option
assert ( assert DEV_MOUNT in docker_addon.mounts
Mount(type="bind", source="/dev", target="/dev", read_only=True)
in docker_addon.mounts
)
# Data added as rw # Data added as rw
assert ( assert (

View File

@ -9,6 +9,8 @@ from docker.types import Mount
from supervisor.coresys import CoreSys from supervisor.coresys import CoreSys
from supervisor.docker.manager import DockerAPI from supervisor.docker.manager import DockerAPI
from . import DEV_MOUNT
async def test_start(coresys: CoreSys, tmp_supervisor_data: Path, path_extern): async def test_start(coresys: CoreSys, tmp_supervisor_data: Path, path_extern):
"""Test starting audio plugin.""" """Test starting audio plugin."""
@ -26,8 +28,9 @@ async def test_start(coresys: CoreSys, tmp_supervisor_data: Path, path_extern):
assert run.call_args.kwargs["ulimits"] == [ assert run.call_args.kwargs["ulimits"] == [
{"Name": "rtprio", "Soft": 10, "Hard": 10} {"Name": "rtprio", "Soft": 10, "Hard": 10}
] ]
assert run.call_args.kwargs["mounts"] == [ assert run.call_args.kwargs["mounts"] == [
Mount(type="bind", source="/dev", target="/dev", read_only=True), DEV_MOUNT,
Mount( Mount(
type="bind", type="bind",
source=coresys.config.path_extern_audio.as_posix(), source=coresys.config.path_extern_audio.as_posix(),

View File

@ -12,6 +12,8 @@ from supervisor.docker.homeassistant import DockerHomeAssistant
from supervisor.docker.manager import DockerAPI from supervisor.docker.manager import DockerAPI
from supervisor.homeassistant.const import LANDINGPAGE from supervisor.homeassistant.const import LANDINGPAGE
from . import DEV_MOUNT
async def test_homeassistant_start( async def test_homeassistant_start(
coresys: CoreSys, tmp_supervisor_data: Path, path_extern coresys: CoreSys, tmp_supervisor_data: Path, path_extern
@ -42,7 +44,7 @@ async def test_homeassistant_start(
"HASSIO_TOKEN": ANY, "HASSIO_TOKEN": ANY,
} }
assert run.call_args.kwargs["mounts"] == [ assert run.call_args.kwargs["mounts"] == [
Mount(type="bind", source="/dev", target="/dev", read_only=True), DEV_MOUNT,
Mount(type="bind", source="/run/dbus", target="/run/dbus", read_only=True), Mount(type="bind", source="/run/dbus", target="/run/dbus", read_only=True),
Mount(type="bind", source="/run/udev", target="/run/udev", read_only=True), Mount(type="bind", source="/run/udev", target="/run/udev", read_only=True),
Mount( Mount(
@ -128,7 +130,7 @@ async def test_landingpage_start(
"HASSIO_TOKEN": ANY, "HASSIO_TOKEN": ANY,
} }
assert run.call_args.kwargs["mounts"] == [ assert run.call_args.kwargs["mounts"] == [
Mount(type="bind", source="/dev", target="/dev", read_only=True), DEV_MOUNT,
Mount(type="bind", source="/run/dbus", target="/run/dbus", read_only=True), Mount(type="bind", source="/run/dbus", target="/run/dbus", read_only=True),
Mount(type="bind", source="/run/udev", target="/run/udev", read_only=True), Mount(type="bind", source="/run/udev", target="/run/udev", read_only=True),
Mount( Mount(