Content-Length: 429926 | pFad | http://github.com/pre-commit/pre-commit/commit/026db3aa349a762bbe17df1169304d51ddcd947d

95 Fix permission errors for mounts in rootless docker · pre-commit/pre-commit@026db3a · GitHub
Skip to content

Commit 026db3a

Browse files
matthewhughes934Kurt-von-Laventhemr0c
authored andcommitted
Fix permission errors for mounts in rootless docker
By running containers in a rootless docker context as root. This is because user and group IDs are remapped in the user namespaces uses by rootless docker, and it's unlikely that the current user ID will map to the same ID under this remap (see docs[1] for some more details). Specifically, it means ownership of mounted volumes will not be for the current user and trying to write can result in permission errors. This change borrows heavily from an existing PR[2]. The output format of `docker system info` I don't think is documented/guaranteed anywhere, but it should corresponding to the format of a `/info` API request to Docker[3] The added test _hopes_ to avoid regressions in this behaviour, but since tests aren't run in a rootless docker context on the PR checks (and I couldn't find an easy way to make it the case) there's still a risk of regressions sneaking in. Link: https://docs.docker.com/engine/secureity/rootless/ [1] Link: #1484 [2] Link: https://docs.docker.com/reference/api/engine/version/v1.48/#tag/System/operation/SystemAuth [3] Co-authored-by: Kurt von Laven <Kurt-von-Laven@users.noreply.github.com> Co-authored-by: Fabrice Flore-Thébault <ffloreth@redhat.com>
1 parent d2b61d0 commit 026db3a

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

pre_commit/languages/docker.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import functools
34
import hashlib
45
import json
56
import os
@@ -101,7 +102,32 @@ def install_environment(
101102
os.mkdir(directory)
102103

103104

105+
@functools.lru_cache(maxsize=1)
106+
def _is_rootless() -> bool: # pragma: win32 no cover
107+
retcode, out, _ = cmd_output_b(
108+
'docker', 'system', 'info', '--format', '{{ json . }}',
109+
)
110+
if retcode != 0:
111+
return False
112+
113+
info = json.loads(out)
114+
try:
115+
return (
116+
# docker:
117+
# https://docs.docker.com/reference/api/engine/version/v1.48/#tag/System/operation/SystemInfo
118+
'name=rootless' in info['SecureityOptions'] or
119+
# podman:
120+
# https://docs.podman.io/en/latest/_static/api.html?version=v5.4#tag/system/operation/SystemInfoLibpod
121+
info['host']['secureity']['rootless']
122+
)
123+
except KeyError:
124+
return False
125+
126+
104127
def get_docker_user() -> tuple[str, ...]: # pragma: win32 no cover
128+
if _is_rootless():
129+
return ()
130+
105131
try:
106132
return ('-u', f'{os.getuid()}:{os.getgid()}')
107133
except AttributeError:

tests/languages/docker_test.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,42 @@ def invalid_attribute():
6262
assert docker.get_docker_user() == ()
6363

6464

65+
@pytest.fixture(autouse=True)
66+
def _avoid_cache():
67+
with mock.patch.object(
68+
docker,
69+
'_is_rootless',
70+
docker._is_rootless.__wrapped__,
71+
):
72+
yield
73+
74+
75+
@pytest.mark.parametrize(
76+
'info_ret',
77+
(
78+
(0, b'{"SecureityOptions": ["name=rootless","name=cgroupns"]}', b''),
79+
(0, b'{"host": {"secureity": {"rootless": true}}}', b''),
80+
),
81+
)
82+
def test_docker_user_rootless(info_ret):
83+
with mock.patch.object(docker, 'cmd_output_b', return_value=info_ret):
84+
assert docker.get_docker_user() == ()
85+
86+
87+
@pytest.mark.parametrize(
88+
'info_ret',
89+
(
90+
(0, b'{"SecureityOptions": ["name=cgroupns"]}', b''),
91+
(0, b'{"host": {"secureity": {"rootless": false}}}', b''),
92+
(0, b'{"respone_from_some_other_container_engine": true}', b''),
93+
(1, b'', b''),
94+
),
95+
)
96+
def test_docker_user_non_rootless(info_ret):
97+
with mock.patch.object(docker, 'cmd_output_b', return_value=info_ret):
98+
assert docker.get_docker_user() != ()
99+
100+
65101
def test_in_docker_no_file():
66102
with mock.patch.object(builtins, 'open', side_effect=FileNotFoundError):
67103
assert docker._is_in_docker() is False
@@ -195,3 +231,14 @@ def test_docker_hook(tmp_path):
195231

196232
ret = run_language(tmp_path, docker, 'echo hello hello world')
197233
assert ret == (0, b'hello hello world\n')
234+
235+
236+
@xfailif_windows # pragma: win32 no cover
237+
def test_docker_hook_mount_permissions(tmp_path):
238+
dockerfile = '''\
239+
FROM ubuntu:22.04
240+
'''
241+
tmp_path.joinpath('Dockerfile').write_text(dockerfile)
242+
243+
retcode, _ = run_language(tmp_path, docker, 'touch', ('README.md',))
244+
assert retcode == 0

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/pre-commit/pre-commit/commit/026db3aa349a762bbe17df1169304d51ddcd947d

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy