Security¶
We provide here some tips based on our experience.
Warning
We are by no mean security experts. Cybersecurity is a very active domain and unfortunately suffers from a serious shortage of talent/expertise. Do not hesitate to request audit from certified experts.
Use multi-stage build¶
Docker Multi stage build functionality has two main benefits:
- Reduce the image size (sometimes up to a factor 10)
- Reduce the surface attack: only what is absolutely necessary is used at runtime, not what was used to build packages.
Example of a simple multi-stage build script would be
###########
# BUILDER #
###########
FROM python:3.11-slim as builder
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install packages
RUN python3 -m pip install --upgrade pip
COPY ./requirements.txt /requirements.txt
RUN python3 -m pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
#########
# FINAL #
#########
FROM python:3.11-slim
# Install build packages
COPY --from=builder /app/wheels /wheels
RUN python3 -m pip install --no-cache /wheels/*
## Ship code
COPY app/ /app/app
WORKDIR /app
#Opened ports
EXPOSE 80
ENV PYTHONPATH=/app
CMD ["uvicorn", "app.dash_app:server", "--host", "0.0.0.0", "--port", "80", "--timeout-keep-alive", "300", "--ws-ping-timeout", "300" ]
The use of PYTHONDONTWRITEBYTECODE
and PYTHONUNBUFFERED
is well explained here
(very good site and a great source of inspiration for us)
Use Open source image scanners¶
We can recommend syft that works hand in hand with grype.
After installation, using syft is as simple as
syft <YOUR_DOCKER_IMAGE> -o cyclonedx-json=sbom.json
Go here to learn what is a Software Bill Of Materials (SBOM).
Then in the folder containing your sbom.json
, using grype (after installation) is as simple as:
grype sbom:sbom.json
Rebuild regularly your docker images¶
Most of the critical vulnerabilities of popular python packages/Ubuntu components are patched very fast.
You should therefore regularly rebuild your docker image.
Important
You should use the no-cache
option.
Otherwise a latest
image won't be pulled for instance.
For instance, on an image not built since some weeks, here is the grype
report
Before rebuilding
And for the same image, rebuilt with the no-cache
After rebuilding
The resource that triggered our interest on the topic can be found here
Use Open source code scanners¶
There are numerous tools that allow you to directly scan your code base (by contrast with scanning docker images).
This allow for a more precise analysis, very precious in an era where cyberattacks are all the more present.
To at least inspect how you perform with respect to the OWASP Top Ten, you can use SonarQube.
It comes with Github integration, but you can even use it locally, following these steps. First run
docker run -d \
--name sonarqube \
-e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true \
-p 9001:9000 \
sonarqube:latest
Then go to http://127.0.0.1:9001. After login (admin admin by default) you should create a project like so
Create project
Use global settings
Local project
Generate token
Copy token
Once all of this is done, you can launch
sudo docker run \ ─╯
--rm \
-e SONAR_HOST_URL="http://127.0.0.1:9001" --network=host \
-e SONAR_SCANNER_OPTS="-Dsonar.projectKey=YOUR_PROJECT_NAME" \
-e SONAR_LOGIN="YOUR_API_KEY" \
-v ./app:/usr/src \
sonarsource/sonar-scanner-cli
YOUR_PROJECT_NAME
and YOUR_API_KEY
accordingly. This command should be launched in a code repo where the actual code is in app
(look at the mounted volume).
Once the command has finished running, you can inspect in the web interface the results
Damn you funky police in typography.css. Only "bugs" for a python project...
Use IP whitelisting¶
If all what is described here does not suffice, you can add IP white sourcing: only listed IPs will be allowed to connect to your app.
Setting it up is quite simple thanks to Traefik. In the service label section of the docker-compose stack you want to protect, just add
- traefik.http.middlewares.NAME.ipwhitelist.sourcerange=1.2.3.4/32, 5.6.7.8
- traefik.http.routers.SERVICE_NAME-https.middlewares=NAME
Where NAME
is the name you want to give to the ipwhitelist
middleware, and SERVICE_NAME
the service defined in the docker-compose stack section you are
currently editing.
Subtlety
if you create all of your stacks with our template, there is always a middleware to redirect http to https. This is why we create the ipwhitelist middleware at the https level. Otherwise it would not be applied 😁