Mellow Root

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 executed
  • nosuid - ignores the setuid and setgid bits on binaries, which prevents privilege escalations
  • nodev - 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/directory
  • C - changed file/directory
  • D - 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.

#docker #security #containers #TIL

- 6 toasts