-
-
Notifications
You must be signed in to change notification settings - Fork 767
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect rootless mode #1484
Detect rootless mode #1484
Changes from all commits
7c16c3e
5c1cd81
a8887d2
4416c3d
818a909
79528ec
b0fc24f
da85e73
12a40fd
dff7d60
9d4fe3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import functools | ||
import hashlib | ||
import os | ||
from typing import Sequence | ||
|
@@ -9,6 +10,7 @@ | |
from pre_commit.prefix import Prefix | ||
from pre_commit.util import CalledProcessError | ||
from pre_commit.util import clean_path_on_failure | ||
from pre_commit.util import cmd_output | ||
from pre_commit.util import cmd_output_b | ||
|
||
ENVIRONMENT_DIR = 'docker' | ||
|
@@ -76,7 +78,28 @@ def install_environment( | |
os.mkdir(directory) | ||
|
||
|
||
def _docker_is_rootless() -> bool: # pragma: win32 no cover | ||
returncode, stdout, stderr = cmd_output( | ||
'docker', 'system', 'info', | ||
) | ||
for line in stdout.splitlines(): | ||
# rootless docker has "rootless" | ||
# rootless podman has "rootless: true" | ||
if line.strip().startswith('rootless'): | ||
if 'false' not in line: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: Technically, the case where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be useful as a code comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that what is causing the coverage error?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is weird. If tests include example outputs containing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @asottile any ideas about this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's no test for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would probably not catch docker's behavior. Better to just improve the test matrix |
||
return True | ||
break | ||
themr0c marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return False | ||
|
||
|
||
@functools.lru_cache(maxsize=1) | ||
def docker_is_rootless() -> bool: # pragma: win32 no cover | ||
return _docker_is_rootless() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: We had to do this to be able to test this with multiple mocked outputs. The cached version made that very hard. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could also be something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could. Not sure if more or less readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's an example of testing the inner part of an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
|
||
def get_docker_user() -> Tuple[str, ...]: # pragma: win32 no cover | ||
if docker_is_rootless(): | ||
return () | ||
try: | ||
return ('-u', f'{os.getuid()}:{os.getgid()}') | ||
except AttributeError: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,188 @@ | |
from pre_commit.languages import docker | ||
from pre_commit.util import CalledProcessError | ||
|
||
DOCKER_NOT_ROOTLESS_SYSTEM_INFO = ''' | ||
Client: | ||
Debug Mode: false | ||
|
||
Server: | ||
Containers: 8 | ||
Running: 0 | ||
Paused: 0 | ||
Stopped: 8 | ||
Images: 2 | ||
Server Version: 19.03.8 | ||
Storage Driver: overlay2 | ||
Backing Filesystem: <unknown> | ||
Supports d_type: true | ||
Native Overlay Diff: true | ||
Logging Driver: journald | ||
Cgroup Driver: systemd | ||
Plugins: | ||
Volume: local | ||
Network: bridge host ipvlan macvlan null overlay | ||
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog | ||
Swarm: inactive | ||
Runtimes: runc | ||
Default Runtime: runc | ||
Init Binary: /usr/libexec/docker/docker-init | ||
containerd version: | ||
runc version: fbdbaf85ecbc0e077f336c03062710435607dbf1 | ||
init version: | ||
Security Options: | ||
seccomp | ||
Profile: default | ||
selinux | ||
Kernel Version: 5.6.14-300.fc32.x86_64 | ||
Operating System: Fedora 32 (Thirty Two) | ||
OSType: linux | ||
Architecture: x86_64 | ||
CPUs: 8 | ||
Total Memory: 15.29GiB | ||
Name: carbon | ||
ID: IUGE:4L5B:VVTV:JGIA:IIJN:G7MG:TVGF:XBVW:YHF7:MYRK:L524:4HBK | ||
Docker Root Dir: /var/lib/docker | ||
Debug Mode: false | ||
Registry: https://index.docker.io/v1/ | ||
Labels: | ||
Experimental: false | ||
Insecure Registries: | ||
127.0.0.0/8 | ||
Live Restore Enabled: true | ||
''' # noqa | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's usually better to specify specific violation codes instead of ignoring everything |
||
|
||
DOCKER_ROOTLESS_SYSTEM_INFO = ''' | ||
Client: | ||
Debug Mode: false | ||
|
||
Server: | ||
Containers: 0 | ||
Running: 0 | ||
Paused: 0 | ||
Stopped: 0 | ||
Images: 0 | ||
Server Version: 19.03.11 | ||
Storage Driver: vfs | ||
Logging Driver: json-file | ||
Cgroup Driver: none | ||
Plugins: | ||
Volume: local | ||
Network: bridge host ipvlan macvlan null overlay | ||
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog | ||
Swarm: inactive | ||
Runtimes: runc | ||
Default Runtime: runc | ||
Init Binary: docker-init | ||
containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429 | ||
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd | ||
init version: fec3683 | ||
Security Options: | ||
seccomp | ||
Profile: default | ||
rootless | ||
Kernel Version: 5.6.16-300.fc32.x86_64 | ||
Operating System: Fedora 32 (Workstation Edition) | ||
OSType: linux | ||
Architecture: x86_64 | ||
CPUs: 8 | ||
Total Memory: 23.36GiB | ||
Name: laptop.centsix.taz | ||
ID: 6EMS:RSJ4:NXM3:3D56:CV4N:VEUX:LBFM:KWHK:P7XA:HTCS:GVSC:UVUE | ||
Docker Root Dir: /home/remote/user/.local/share/docker | ||
Debug Mode: false | ||
Registry: https://index.docker.io/v1/ | ||
Labels: | ||
Experimental: true | ||
Insecure Registries: | ||
127.0.0.0/8 | ||
Live Restore Enabled: false | ||
Product License: Community Engine | ||
''' # noqa | ||
|
||
PODMAN_SYSTEM_INFO = ''' | ||
host: | ||
arch: amd64 | ||
buildahVersion: 1.14.9 | ||
cgroupVersion: v2 | ||
conmon: | ||
package: conmon-2.0.17-1.fc32.x86_64 | ||
path: /usr/libexec/crio/conmon | ||
version: 'conmon version 2.0.17, commit: bb8e273f5925c1a51737644637ef65d094a67ab1' | ||
cpus: 8 | ||
distribution: | ||
distribution: fedora | ||
version: "32" | ||
eventLogger: file | ||
hostname: laptop.centsix.taz | ||
idMappings: | ||
gidmap: | ||
- container_id: 0 | ||
host_id: 1001 | ||
size: 1 | ||
- container_id: 1 | ||
host_id: 200000 | ||
size: 65536 | ||
uidmap: | ||
- container_id: 0 | ||
host_id: 105971 | ||
size: 1 | ||
- container_id: 1 | ||
host_id: 200000 | ||
size: 65536 | ||
kernel: 5.6.16-300.fc32.x86_64 | ||
memFree: 7256297472 | ||
memTotal: 25081421824 | ||
ociRuntime: | ||
name: crun | ||
package: crun-0.13-2.fc32.x86_64 | ||
path: /usr/bin/crun | ||
version: |- | ||
crun version 0.13 | ||
commit: e79e4de4ac16da0ce48777afb72c6241de870525 | ||
spec: 1.0.0 | ||
+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL | ||
os: linux | ||
rootless: true | ||
slirp4netns: | ||
executable: /usr/bin/slirp4netns | ||
package: slirp4netns-1.0.0-1.fc32.x86_64 | ||
version: |- | ||
slirp4netns version 1.0.0 | ||
commit: a3be729152a33e692cd28b52f664defbf2e7810a | ||
libslirp: 4.2.0 | ||
swapFree: 20372766720 | ||
swapTotal: 20372766720 | ||
uptime: 4h 46m 1.38s (Approximately 0.17 days) | ||
registries: {} | ||
store: | ||
configFile: /home/remote/user/.config/containers/storage.conf | ||
containerStore: | ||
number: 66 | ||
paused: 0 | ||
running: 0 | ||
stopped: 66 | ||
graphDriverName: overlay | ||
graphOptions: | ||
overlay.mount_program: | ||
Executable: /usr/bin/fuse-overlayfs | ||
Package: fuse-overlayfs-1.0.0-1.fc32.x86_64 | ||
Version: |- | ||
fusermount3 version: 3.9.1 | ||
fuse-overlayfs: version 1.0.0 | ||
FUSE library version 3.9.1 | ||
using FUSE kernel interface version 7.31 | ||
graphRoot: /home/remote/user/.local/share/containers/storage | ||
graphStatus: | ||
Backing Filesystem: extfs | ||
Native Overlay Diff: "false" | ||
Supports d_type: "true" | ||
Using metacopy: "false" | ||
imageStore: | ||
number: 23 | ||
runRoot: /tmp/105971 | ||
volumePath: /home/remote/user/.local/share/containers/storage/volumes | ||
''' # noqa | ||
|
||
|
||
def test_docker_is_running_process_error(): | ||
with mock.patch( | ||
|
@@ -21,3 +203,27 @@ def invalid_attribute(): | |
getgid=invalid_attribute, | ||
): | ||
assert docker.get_docker_user() == () | ||
|
||
|
||
def test_docker_is_not_rootless(): | ||
with mock.patch.object( | ||
docker, 'cmd_output', | ||
return_value=(0, DOCKER_NOT_ROOTLESS_SYSTEM_INFO, ''), | ||
): | ||
assert docker._docker_is_rootless() is False | ||
|
||
|
||
def test_docker_is_rootless(): | ||
with mock.patch.object( | ||
docker, 'cmd_output', | ||
return_value=(0, DOCKER_ROOTLESS_SYSTEM_INFO, ''), | ||
): | ||
assert docker._docker_is_rootless() is True | ||
|
||
|
||
def test_podman_is_rootless(): | ||
with mock.patch.object( | ||
docker, 'cmd_output', | ||
return_value=(0, PODMAN_SYSTEM_INFO, ''), | ||
): | ||
assert docker._docker_is_rootless() is True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be even faster and simplify both the logic below and the mock output used in testing to only query Docker/Podman for the field we care about. From the rootless-docker GitHub Action's README,
docker info --format "{{ .ClientInfo.Context }}"
outputsrootless
in Docker when in rootless mode, and other strings (most commonlydefault
, I believe) otherwise. I suspect, based on Podman's documentation, thatpodman info --format "{{ .host.security.rootless }}"
outputstrue
when in rootless mode andfalse
otherwise. Go templates are quite powerful, so we can say:docker info --format '{{ if .ClientInfo.Context }} {{ eq .ClientInfo.Context "rootless" }} {{ else }} {{ .host.security.rootless }} {{ end }}'
. This command outputstrue
when running in rootless mode andfalse
otherwise in both Docker and Podman, although I have only tested it on Docker. I don't have Podman installed, so hopefully someone who does can straighten me out if I'm mistaken. Disclosure: I am the author of the rootless-docker GitHub Action.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here on my machine (Linux Mint 19.3, Docker version 20.10.16, build aa7e414) your suggestion returns
default
for rootless docker, asrootless
seems to be part of theSecurity Options
underServer
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for letting me know. Bummer that the behavior isn't consistent across our machines even on the exact same build of Docker. I am on Ubuntu 22.04, but I wonder whether the difference has more to do with how we installed rootless Docker? It's possible to detect rootless mode under
SecurityOptions
too (e.g.,docker info --format '{{ range .SecurityOptions }}{{ if eq . "name=rootless" }}true{{ end }}{{ end }}'
), FWIW.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC I followed the official instructions for Ubuntu. Maybe you chose to set the client context explicitly where I went for the
DOCKER_HOST
environment variable? Both options are listed in the instructions: https://docs.docker.com/engine/security/rootless/#clientThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are correct. I suppose one could check the
DOCKER_HOST
environment variable before runningdocker info
.