4.2 KiB
Back to our app: worker.py
Let's follow our app from code to deployment in a container.
During each step we'll analyze the software and show some highlights.
Step 1: The code
import logging
import os
from redis import Redis
import requests
import time
...
redis = Redis("redis")
...
def work_loop(interval=1):
deadline = 0
loops_done = 0
while True:
if time.time() > deadline:
log.info("{} units of work done, updating hash counter"
.format(loops_done))
redis.incrby("hashes", loops_done)
loops_done = 0
deadline = time.time() + interval
work_once()
loops_done += 1
if __name__ == "__main__":
while True:
try:
work_loop()
except:
log.exception("In work loop:")
log.error("Waiting 10s and restarting.")
time.sleep(10)
The code is not so important here
We will distribute our app in a docker container.
The following base images are used:
- python:alpine
- python:3.9.18-slim
- python:latest
The container build is done with a small Dockerfile.
The only thing that changes is the FROM line where different base images are specified:
FROM python:latest
RUN pip install redis
RUN pip install requests
COPY worker.py /
CMD ["python", "worker.py"]
FROM python:alpine
RUN pip install redis
RUN pip install requests
COPY worker.py /
CMD ["python", "worker.py"]
SBOM creation
SBOM's are create from the source code and the images for further analyses.
The tool used is syft, but it could have been another tool as well.
Analysis is done with grype because it produces output that fits nice in this presentation.
Let's see how each step adds vulnerabilities.
Source code analysis:
The source code is quite clean. One one CVE is reported:
grype --add-cpes-if-none sbom-worker.py.json
✔ Vulnerability DB [no update available]
✔ Scanned for vulnerabilities [1 vulnerability matches]
├── by severity: 0 critical, 0 high, 1 medium, 0 low, 0 negligible
└── by status: 1 fixed, 0 not-fixed, 0 ignored
Let test default: python:latest
This is the most tempting image. It seems to be very complete:
grype --add-cpes-if-none sbom-python-latest.json
✔ Vulnerability DB [no update available]
✔ Scanned for vulnerabilities [1700 vulnerability matches]
├── by severity: 21 critical, 359 high, 519 medium, 73 low, 721 negligible (7 unknown)
└── by status: 448 fixed, 1252 not-fixed, 0 ignored
Wow... We went from only 1 CVE to 1700 at the time of writing...
Can we do better: python:3.9.18-slim
A slim image with more than enough to run our application:
grype --add-cpes-if-none sbom-python-3.9.18-slim.json
✔ Vulnerability DB [no update available]
✔ Scanned for vulnerabilities [101 vulnerability matches]
├── by severity: 1 critical, 11 high, 28 medium, 3 low, 55 negligible (3 unknown)
└── by status: 14 fixed, 87 not-fixed, 0 ignored
That's already a huge difference. Especially when you pay attention to the critical and high rated CVE's
Let's try one more image: python:alpine
grype --add-cpes-if-none sbom-python-alpine.json
✔ Vulnerability DB [no update available]
✔ Scanned for vulnerabilities [21 vulnerability matches]
├── by severity: 0 critical, 1 high, 18 medium, 0 low, 0 negligible (2 unknown)
└── by status: 9 fixed, 12 not-fixed, 0 ignored
Summary
| Source | Critial | High | Medium | Low |
|---|---|---|---|---|
| worker.py | 0 | 0 | 1 | 0 |
| python-latest | 21 | 359 | 519 | 73 low |
| python:3.9.18-slim | 1 | 11 | 28 | 3 low |
| python:alpine | 0 | 1 | 18 | 0 low |
Any idea which one I prefer?
Storing SBOM files
If you store these SBOM's files you can quickly evaluate if new CVE's are introduced without scanning every component or image again.
Or you can store them in a database like Dependency Track which will periodically evaluate the vulnerabilities.
Combined with the Alerts function it will help you to fix vulnerabilities as soon as they are discovered.