The image that was fine last year
Auditing a container image means checking which version it runs, who its processes run as and whether any package inside it is out of date. Old packages carry known flaws that attackers look for first.
A team built a web server image a year ago and has run it ever since. It worked, so nobody touched it. In that year, three flaws were found in programs inside it. The team never looked.
By the end of this lesson
Note
I can check what an image runs, which versions it holds and whether any are out of date.
Key words
| Word | What it means | Example |
|---|---|---|
| Image | a packed copy of a program and what it needs, from which containers start | |
| Tag | the label after the colon in an image name | 1.27.5-alpine |
| Pin | to name one exact version so it never changes by surprise | |
| Package | one program or library that was installed into the image | |
| Advisory | a notice that says which version fixed a known flaw | |
| Scan | comparing what is installed with a list of known flaws |
The cells students fill in are left blank in this preview.
A tag says which version you run
A container starts from an image. The image's name ends with a tag, such as nginx:1.27.5-alpine.
A tag called latest means whatever is newest today. It changes under you. A pinned tag names one exact version, so you know what you run and can decide when to move.
Which image is the web service running?
docker inspect web --format '{{.Config.Image}}'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Which nginx is inside it?
docker exec web nginx -vShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the two Runs printed
The first printed nginx:1.27.5-alpine. The tag is a version, so this image is pinned. The second printed nginx version: nginx/1.27.5.
These two answers should agree. If a tag says one thing and the program says another, somebody has changed the image.
Who runs the processes?
docker exec web ps -o user,args | head -n 3Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Root and the worker
The Run printed a heading, then a line for root running the master process, then a line for nginx running a worker.
The master is root so it may open port 80. The workers, which handle visitors, run as the plain nginx account. A flaw in a worker gives an attacker much less. If everything ran as root, the same flaw would give the whole container.
An inventory comes before a scan
A scanner does three things. It lists the packages, it reads a list of flaws and their fixes, and it compares the two.
You can do the first and third in the lab. A real scanner needs a list of flaws from the internet, and the lab has no way out, so you will type two invented advisories.
How many packages are installed?
docker exec web apk info 2>/dev/null | wc -lShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Show the versions of two of them
docker exec web apk info -v 2>/dev/null | grep -E '^(zlib|musl)-[0-9]'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The inventory
When this was tried, the count was 68, and the two lines were musl-1.2.5-r9 and zlib-1.3.1-r2. Each line is a name, a version and a build number.
Now an invented advisory: "zlib is fixed in 1.3.2-r0". Alpine can compare two versions for you, offline.
Is the installed zlib older than the fix?
docker exec web apk version -t 1.3.1-r2 1.3.2-r0Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Compare musl with an equal fix
docker exec web apk version -t 1.2.5-r9 1.2.5-r9Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the symbols mean
The first Run printed <. The installed version is older than the fix, so this package would need an update. The second printed =, so it is already at the fixed version.
A symbol > would mean newer than the fix. This is the whole of a scan: an inventory, an advisory and a comparison.
Worked example: what a scan decides
| Package | Installed | Fixed in | Result |
|---|---|---|---|
| zlib | 1.3.1-r2 | 1.3.2-r0 | Older, so update |
| musl | 1.2.5-r9 | 1.2.5-r9 | Equal, so fixed |
| curl | 8.12.1-r1 | 8.12.0-r0 | Newer, so fixed |
The cells students fill in are left blank in this preview.
What does the image carry in its environment?
docker inspect web --format '{{json .Config.Env}}'Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people scan once, see nothing, and never scan again. New flaws are found every week, so an image that was clean last month may not be today. Scan on a schedule, and again whenever the image changes.
What the lab cannot show
A lab cannot pull, build or scan images, so you inspected the running service, not the image file. On the machine this was tried on, the environment Run printed only values the image sets itself, such as the version. That is what it is meant to hold.
A password in that list would be a leak. Anybody who can inspect the container can read it.
Going further: after your fixes
Finish the lab's fixes, then run docker exec web lab-check. Run this lesson's tag, user and environment Runs again and write one sentence: did any of them change?
The configuration is what you hardened. The image and its packages are not, because a configuration fix does not update a package.