The following cheatsheet is meant to show how to use Docker to sandbox untrusted code. The goal is to minimize the damage that could be made by malicius code downloaded from untrusted source.
Running a code this way allows to:
- prevent code from accessing the rest of the filesystem
- easily revert all changes
- run a code with near native performance
- control network access
- run processes as superuser without giving them controll of entire device
The main downside of using Docker is that it shares the same kernel as the host, which means that vulnerabilities of host’s kernel could be used to escape from container. There are other ways to sandbox code like virtualization, AppArmor, firejail.
Run a command in a disposible container Link to heading
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Example for python:
docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3 python script.py
It crates python3 container, runs a python script.py
command and removes it as soon as script finishes execution.
Arguments breakdown:
-it
runs a command with interactive shell. Allows you to provide input--rm
removes container as soon as it finishes executing-v "$PWD":/usr/src/myapp
points your current directory (on host machine) to path/usr/src/myapp
inside a container-w
sets working directory to/usr/src/myapp
python:3
is a base docker image, on which we are running our commandpython script.py
is a command we want to run inside a container
Effect (only seen when container is still running):
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3dc148f99f74 python:3 "python script.py" 5 seconds ago Up 4 seconds my-running-script
For multiple commands (runs sh
command which then executes chain of commands)
docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3 sh -c "python script.py && echo test"
Accessing running container’s filesystem Link to heading
If container is still running (docker ps -a
returns status “Up”):
$ docker exec -it b325ffe9fe18 /bin/bash
Where b325ffe9fe18
is a container id.
Accessing closed container’s filesystem Link to heading
If status of container is “Exited” we cannot enter shell in it. But if we still want to inspect files, processes and make use of stuff in it, we can make snapshot of it and run it independently.
Assuming we create non-disposable container like this:
docker run -it --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3 python script.py
And docker ps -a
command returns:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69b5fe1b8934 python:3 "python script.py" 5 seconds ago Exited (0) 4 seconds ago my-running-script
Commit the stopped container:
docker commit 69b5fe1b8934 image_name_of_snapshot
Where image_name_of_snapshot
is a name for newly created image.
Result:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
image_name_of_snapshot latest 692b940a0f2a 45 seconds ago 920MB
Then to enter shell:
docker run -it --rm --entrypoint /bin/bash image_name_of_snapshot
More complex scenarios Link to heading
Create Dockerfile:
FROM python:3
WORKDIR /usr/src/app
COPY script.py .
CMD [ "python", "./script.py" ]
Then build:
docker build -t test_base_image . --load
Then run and log in to shell:
docker run -it --entrypoint /bin/bash test_base_image