From 906e400ab72e51067fb7f436d07bbd043b4508b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= Date: Tue, 2 Apr 2024 21:07:53 +0200 Subject: [PATCH] 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. --- supervisor/docker/const.py | 1 + tests/docker/__init__.py | 5 +++++ tests/docker/test_addon.py | 8 +++----- tests/docker/test_audio.py | 5 ++++- tests/docker/test_homeassistant.py | 6 ++++-- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/supervisor/docker/const.py b/supervisor/docker/const.py index 2a9740586e1..cb9a34e4f83 100644 --- a/supervisor/docker/const.py +++ b/supervisor/docker/const.py @@ -74,6 +74,7 @@ class PropagationMode(StrEnum): 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.setdefault("BindOptions", {})["ReadOnlyNonRecursive"] = True MOUNT_DOCKER = Mount( type=MountType.BIND, source="/run/docker.sock", diff --git a/tests/docker/__init__.py b/tests/docker/__init__.py index 65ac125c1ac..6437e7cf515 100644 --- a/tests/docker/__init__.py +++ b/tests/docker/__init__.py @@ -1 +1,6 @@ """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} diff --git a/tests/docker/test_addon.py b/tests/docker/test_addon.py index f9a52aa6825..3f18524db52 100644 --- a/tests/docker/test_addon.py +++ b/tests/docker/test_addon.py @@ -19,6 +19,7 @@ from supervisor.resolution.data import Issue from ..common import load_json_fixture +from . import DEV_MOUNT @pytest.fixture(name="addonsdata_system") @@ -66,11 +67,8 @@ def test_base_volumes_included( coresys, addonsdata_system, "basic-addon-config.json" ) - # Dev added as ro - assert ( - Mount(type="bind", source="/dev", target="/dev", read_only=True) - in docker_addon.mounts - ) + # Dev added as ro with bind-recursive=writable option + assert DEV_MOUNT in docker_addon.mounts # Data added as rw assert ( diff --git a/tests/docker/test_audio.py b/tests/docker/test_audio.py index 0b3e23b974d..9f8c0c3c7ce 100644 --- a/tests/docker/test_audio.py +++ b/tests/docker/test_audio.py @@ -9,6 +9,8 @@ from supervisor.coresys import CoreSys from supervisor.docker.manager import DockerAPI +from . import DEV_MOUNT + async def test_start(coresys: CoreSys, tmp_supervisor_data: Path, path_extern): """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"] == [ {"Name": "rtprio", "Soft": 10, "Hard": 10} ] + assert run.call_args.kwargs["mounts"] == [ - Mount(type="bind", source="/dev", target="/dev", read_only=True), + DEV_MOUNT, Mount( type="bind", source=coresys.config.path_extern_audio.as_posix(), diff --git a/tests/docker/test_homeassistant.py b/tests/docker/test_homeassistant.py index 97968369cbf..02fd2fe42a3 100644 --- a/tests/docker/test_homeassistant.py +++ b/tests/docker/test_homeassistant.py @@ -12,6 +12,8 @@ from supervisor.docker.manager import DockerAPI from supervisor.homeassistant.const import LANDINGPAGE +from . import DEV_MOUNT + async def test_homeassistant_start( coresys: CoreSys, tmp_supervisor_data: Path, path_extern @@ -42,7 +44,7 @@ async def test_homeassistant_start( "HASSIO_TOKEN": ANY, } 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/udev", target="/run/udev", read_only=True), Mount( @@ -128,7 +130,7 @@ async def test_landingpage_start( "HASSIO_TOKEN": ANY, } 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/udev", target="/run/udev", read_only=True), Mount(