fixuid must be owned by user 'root' and include the setuid bit
koxu1996 opened this issue · 13 comments
I am unable to run fixuid with simple Dockerfile:
FROM alpine:latest
RUN addgroup -g 1000 docker && \
adduser -u 1000 -G docker -h /home/docker -s /bin/sh -D docker
RUN apk update && \
apk add ca-certificates wget && \
update-ca-certificates
RUN USER=docker && \
GROUP=docker && \
wget -qO- https://github.com/boxboat/fixuid/releases/download/v0.3/fixuid-0.3-linux-amd64.tar.gz | tar -C /usr/local/bin -xzf - && \
chown root:root /usr/local/bin/fixuid && \
chmod 4755 /usr/local/bin/fixuid && \
mkdir -p /etc/fixuid && \
printf "user: $USER\ngroup: $GROUP\n" > /etc/fixuid/config.yml
USER docker:docker
ENTRYPOINT ["fixuid"]
Result:
$ docker build -t test-fixuid .
$ docker run -it test-fixuid
fixuid: fixuid should only ever be used on development systems. DO NOT USE IN PRODUCTION
fixuid: fixuid must be owned by user 'root' and include the setuid bit 'chmod u+s /path/to/fixuid'
I checked it with $ docker run -it --entrypoint sh test-fixuid
and it looks good:
/ $ cd /usr/local/bin/
/usr/local/bin $ ls -al
total 3616
drwxr-xr-x 1 root root 12 Mar 17 23:13 .
drwxr-xr-x 1 root root 22 Jan 9 19:37 ..
-rwsr-xr-x 1 root root 3699694 Jan 15 16:08 fixuid
Am I doing something wrong?
What is your host operating system? Is the host running selinux
by chance? If so can you setenforce=permissive
on the host and try?
I am using Arch Linux, without selinux.
Odd, I copied your Dockerfile to my system Ubuntu 17.10 w/ Kernel 4.13.0-37-generic running Docker 17.10.0-ce and it works fine:
fixuid: fixuid should only ever be used on development systems. DO NOT USE IN PRODUCTION
fixuid: runtime UID '1000' already matches container user 'docker' UID
fixuid: runtime GID '1000' already matches container group 'docker' GID
The error that you're seeing means that the geteuid check is returning a non-zero value
What is your kernel version from uname -r
and your Docker version from docker info
?
$ uname -r
4.15.9-1-ARCH
$ docker info
Containers: 26
Running: 0
Paused: 0
Stopped: 26
Images: 110
Server Version: 18.02.0-ce
Storage Driver: btrfs
Build Version: Btrfs v4.15
Library Version: 102
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 9b55aab90508bd389d7654c4baf173a981477d55
runc version: 9f9c96235cc97674e935002fc3d78361b696a69e
init version: 949e6fa
Security Options:
seccomp
Profile: default
Kernel Version: 4.15.9-1-ARCH
Operating System: Arch Linux
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.853GiB
Name: archPC
ID: 3YY6:CPQ7:QVET:BWBB:SF6N:76KA:2SXF:F4KN:HNSM:HJOR:QDPR:UNEM
Docker Root Dir: /run/media/docker
Debug Mode (client): false
Debug Mode (server): false
Username: koxu1996
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
I checked and running getuid() returns 1000 in suided bin. Dockerfile:
FROM alpine:latest
RUN addgroup -g 1000 docker && \
adduser -u 1000 -G docker -h /home/docker -s /bin/sh -D docker
RUN apk update && \
apk add build-base
RUN echo $'#include <stdio.h>\n#include <unistd.h>\n\
int main()\n\
{\n\
printf("UID: %d", getuid());\n\
return 0;\n\
}\n' > /usr/local/bin/testuid.c
RUN cd /usr/local/bin && \
gcc testuid.c -o testuid && \
chown root:root testuid && \
chmod 4755 testuid
USER docker:docker
ENTRYPOINT ["testuid"]
Result:
$ docker build -t test-uid .
$ docker run -it test-uid .
UID: 1000
BTW I found similar program: https://github.com/schmidigital/permission-fix/blob/master/tools/permission_fix Could you tell me if fixuid is better than it?
The syscall you should check it geteuid
for get effective user ID. Can you rerun the testuid binary with geteuid
instead of getuid
The permission fix script you linked just uses usermod
and groupmod
to change user/group IDs and looks like it would need to run as root. fixuid
aims to be distribution agnostic, does not require running a container as root, and handles changing file permissions on specified mounts.
Yep, that should be geteuid(), but even changing it result is still the same: UID: 1000
. Same situation occurs when I run it on my host.
My best guess is that you are hitting this:
https://www.projectatomic.io/blog/2016/03/no-new-privs-docker/
Moby has a test case that
Does this allow it to work?
docker run -it --security-opt "no-new-privileges=false" test-uid
Since you are not explicitly setting --security-opt "no-new-privileges=true"
, my best guess is that it is being enabled by your default seccomp
profile. I'm not that familiar with seccomp
but there is some mention of it implying no-new-privileges=true
in this issue: opencontainers/runtime-spec#290 (comment):
It would be nice to set this to be enabled by default, or at least note that passing a Seccomp configuration implies that it is enabled, given that it has a beneficial relationship with applications in a container that also use Seccomp.
Setting flag does not help:
$ docker build --no-cache -t test-uid .
...
$ docker run -it --security-opt "no-new-privileges=false" test-uid
UID: 1000
This is definetly problem with my host, but not docker or fixuid. I will try to figure it out and let you know.
Thanks, if it's an upcoming change to a default or security profile it'd be good to document the workaround
Finally I found reason why it does not work: https://unix.stackexchange.com/questions/150972/why-setuid-does-not-work-on-executable
My docker storage is on external drive, which is automatically mounted with nosuid option:
mount | grep docker | grep nosuid
/dev/sdb1 on /run/media/docker type btrfs (rw,nosuid,nodev,relatime,space_cache,subvolid=5,subvol=/)
/dev/sdb1 on /run/media/docker/plugins type btrfs (rw,nosuid,nodev,relatime,space_cache,subvolid=5,subvol=/plugins)
/dev/sdb1 on /run/media/docker/btrfs type btrfs (rw,nosuid,nodev,relatime,space_cache,subvolid=5,subvol=/btrfs)
I do not know Go language, but it would be nice if you could add additional check which ensure that filesystem is mounted without nosuid.
I will probably just edit the error message to be more verbose. Something like:
fixuid: not running as root, ensure that the following criteria are met:
fixuid: - fixuid binary is owned by root: 'chown root:root /path/to/fixuid'
fixuid: - fixuid binary has the setuid bit: 'chmod u+s /path/to/fixuid'
fixuid: - NoNewPrivileges is disabled in container security profile
fixuid: - volume containing fixuid binary does not have the 'nosuid' mount option
It is okay for me.