Secure filesystem in docker
I wanted to explore docker container security, so I started out with looking at filesystem security. This is what I learned!
Read-only filesystem in docker
One can start a container with docker run --read-only
to make the whole container read-only. This is great if you know the container won't need to write anything so use this whenever you can.
Here is an example:
$ docker run \
--rm \
--read-only \
busybox touch /tmp/hello
touch: /tmp/hello: Read-only file system
Allow write on specific paths
Some containers require writable paths for temporary data, for that, we can use the --tmpfs
flag to mount a writable path.
Allow writes to /tmp
:
$ docker run \
--rm \
--read-only \
--tmpfs /tmp \
busybox touch /tmp/hello
No errors this time, so it works as intended!
Limit write size
A malicious actor could put fishy stuff in the writable paths, so no reason to allow for more space than we need.
Set size=64K
to limit the writable disk space to 64 KB:
$ docker run \
--rm \
--read-only \
--tmpfs /tmp:size=64k \
busybox sh -c "dd if=/dev/zero of=/tmp/hello bs=1MB count=1; du -h /tmp/hello"
dd: error writing '/tmp/hello': No space left on device
1+0 records in
0+0 records out
65536 bytes (64.0KB) copied, 0.000302 seconds, 207.0MB/s
64.0K /tmp/hello
As you can see the command fails and the file is only 64KB rather than the 1MB we asked for.
By default docker mounts tmpfs with:
noexec
- forbids any binaries in the mount point to be executednosuid
- ignores thesetuid
andsetgid
bits on binaries, which prevents privilege escalationsnodev
- ignores device files
You can verify this by running mount
in the container.
Figure out where write access is needed
Start a container without --read-only
and run docker diff
on it:
$ docker diff $(docker run -d busybox sh -c "mktemp; rm /bin/df")
C /bin
D /bin/df
C /tmp
A /tmp/tmp.gi3M65
A
- added file/directoryC
- changed file/directoryD
- deleted file/directory
This shows what has changed on the filesystem since the container started. This method isn't foolproof though. It doesn't catch modified files even if it seems to indicate so.
$ docker diff $(docker run -d busybox sh -c "echo hello > /etc/hosts")
This won't show any diff at all. If we instead delete a file and create a new file with the same name, it'll show up as changed.
$ docker diff $(docker run -d busybox sh -c "rm /bin/df; touch /bin/df")
C /bin
C /bin/df
Another way is the good ol' trial and horror! Run the container with a read-only filesystem and see what makes your program crash, and iterate that way.