Skip to main content

Docker and Dumb-Init

· 4 min read

when it comes to containerized environment graceful shutdown, process management and reducing attack surface, I believe we can't leave dumb-init out of it.

What is Dumb-init

Dumb-init, is a tool developed by Yelp which is a simple and non-heavy process management tool with less footprint, so that way we don't end up exposing our applications to more issues through the tooling security vulnerabilities.

dumb-init can also be described as a tool that serves as a parent process to another process in your docker or containers.

Why use dumb-init with your docker or containers

  1. Signal Forwarding

when you have the following CMD ["npm start"] in your Dockerfile, then if the npm start runs in your container, it will take the process PID 1 which is an init process, the first process that runs at the boot time of any Unix system.

PID   USER     TIME  COMMAND
1 node 0:00 npm start
6 node 0:01 node /app/node_modules/.bin/ts-node-dev --poll src/index.ts
17 node 4:09 sh
24 node 0:58 ps

Knowing that Node.js is not designed to run as PID 1, so running it as PID 1 would make it give some unexpected behaviors such as the app not responding to the SIGTERM and other signals.

or if you have the following ENTRYPOINT ["source -c env && ./app.sh"] in your docker file, then source -c env && ./app.sh will become PID 1 and then the started application itself becomes a subprocess and

PID   USER     TIME  COMMAND
1 node 0:00 source -c env && ./app.sh
6 node 0:01 app.sh -- Listening on PORT 9000

since the app itself is the one configured to handle SIGINT and SIGTERM, the shell script won't be able to pass those signals to the app.

  1. Reduced attack surface

Since the container process is wrapped around dumb-init and other processes are now child of the dumb-init process which is now PID 1 process, this shields your app process against responsibilities that comes with PID 1

for example, if you want to make sure your app process doesn't assume new privileges in the container, you can simply set prctl(PR_SET_NO_NEW_PRIVS, 1) or use securityContext: allowPrivilegeEscalation=false, but this Linux kernel feature doesn't work with PID 1 process, instead, it works with the child processes.

So letting your main app process assume PID 1 in the container they are running can be risky.

Using Dumb-Init with your Dockerfile

You will have to install dumb-init in the Dockerfile, also it's advisable to call this before other command layers.

now you can do this in two ways, using ENTRYPOINT, CMD or yaml file if you are working with Kubernetes.

with ENTRYPOINT, you can call the dumb-init to be the PID 1

FROM node:latest
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init
....
ENTRYPOINT ["dumb-init","npm start"]

with ENTRYPOINT and CMD, you can call the dumb-init to be the PID 1

FROM node:latest
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init
....
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm start"]

but in a situation where you are experiencing issues getting dumb-init in your docker images, there are other ways to get on depending on the system.

generalized containers with dumb-init binary

FROM node:latest
RUN wget -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
chmod +x /usr/local/bin/dumb-init
....
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm start"]

Ubuntu

FROM ubuntu:latest
RUN apt install -y dumb-init
....
ENTRYPOINT ["dumb-init", "--"]

Alphine

FROM alphine:latest
RUN apk add dumb-init
....
ENTRYPOINT ["dumb-init", "--"]

Python

FROM python:latest
RUN pip3 install dumb-init
....
ENTRYPOINT ["dumb-init", "--"]

Using dumb-init with Kubernetes yaml file

      containers:
- image: ourdockerfile:latest-dumb-init-installed
name: container-name
command:
['dumb-init', 'sh', '-c']
args:
['npm start']

Also if you pay closer attention to the dockerfile layer above, you will see they are not meeting the production/security readiness for creating Dockerfiles.

It's better to use multistage building and change user ownership from root to node.

Well, that's it, folks! I hope you find this piece insightful and helpful.

References


Comments