When a Security Alert Sent Me Down the Docker Rabbit Hole
Listen to this article· 3 min

It started with a message from the SOC team.
"We found a file detected by SentinelOne on several devices, including Kubernetes pods."
One of the machines was a CI/CD runner.
The file was called:
pam_exec.so
And the reported path looked like this:
/var/lib/docker/overlay2/.../diff/usr/lib/security/pam_exec.so
At first glance, that sounds bad.
A security tool found a suspicious file on a machine used for builds and deployments.
My first thought was basically:
"Okay... where did this come from?"
That question turned into a surprisingly useful Docker lesson.
The First Clue Was the Path
The first thing I noticed was that the file wasn't reported under a normal Linux system path like:
/usr/lib/security/pam_exec.so
It was under:
/var/lib/docker/overlay2/
That changed the investigation completely.
Docker uses overlay2 to store filesystem data for images and containers.
(That's the classic overlay2 storage driver, which long-lived hosts like our runner still use. Fresh installs of Docker Engine 29 and later default to the containerd image store instead, which keeps layers under /var/lib/containerd.)
So instead of assuming:
I started thinking:
- 01Security alert
- 02File inside Docker storage
- 03Which image or container does it belong to?
We checked the same path on the host itself, and the file wasn't present there.
So the investigation had already moved from "something was installed on the server" to "something exists somewhere inside Docker's filesystem."
Then I Had to Understand What a Docker Layer Actually Is
This was probably the most useful Docker concept I took away from the investigation.
A Docker image isn't one giant filesystem.
It is built from layers.
For example:
FROM debian
RUN apt-get update
RUN apt-get install -y curl
COPY app /app
The base image brings its own layers, and each filesystem-changing instruction (RUN, COPY, ADD) adds one more. Conceptually, you can think of it as:
- 01Layer 1base OS
- 02Layer 2package updates
- 03Layer 3curl
- 04Layer 4application files
The layers are stacked together so that the container sees what looks like one normal filesystem.
When a container starts, Docker adds a writable layer on top of those image layers for changes made by the running container.
So the mental model becomes:
- Writable container layer
- Image layer
- Image layer
- Image layer
- Base image layer
Docker stores this underlying filesystem data under:
/var/lib/docker/overlay2/
and the diff/ directory contains the actual filesystem content for a layer. That explains why the SOC alert contained a path such as:
overlay2/<layer-id>/diff/usr/lib/security/pam_exec.so
In other words:
The path itself was already telling us that the file belonged to Docker storage and potentially to an image layer.
This sounds obvious after you understand it.
It wasn't quite so obvious before.
Image, Container, Layer, Cache
I also had to stop mixing these concepts together.
A simple mental model is:
- 01Host
- 02Docker
- 03ImageLayerLayerLayer
- 04ContainerImage layersWritable layer
The image is the packaged blueprint.
The container is the running instance.
The layers are the filesystem pieces that make up the image.
And Docker can keep images and layers around as cached artifacts even after containers have stopped.
That last part is especially important on CI/CD runners.
A runner can accumulate:
- Images
- Containers
- Layers
- Build cache
- Temporary artifacts
So a security scan can find a file somewhere in Docker's stored data even when that file isn't part of the host operating system and isn't being executed by a running application.
But What Exactly Is pam_exec.so?
The filename certainly didn't help.
pam_exec.so is a legitimate Linux PAM module. PAM, or Pluggable Authentication Modules, is part of Linux authentication, and pam_exec.so can be used to execute commands or scripts during authentication.
So the presence of the file alone wasn't enough to conclude that something malicious had happened.
That gave me another important security lesson:
A detection is the beginning of an investigation, not the conclusion.
The real questions became:
- Where is the file?
- Where did it come from?
- Which image contains it?
- Was that image expected?
- Is the file actually being used?
- Is the host itself affected?
Then I Started Following the Docker Layers
The SOC team had provided the Docker layer ID associated with the alert.
That was the key.
Instead of searching every file manually, I could trace the layer back toward its source:
- 01pam_exec.so
- 02overlay2
- 03Layer ID
- 04Docker image
This was probably the most useful thing I learned from the whole incident.
When something suspicious appears inside Docker, don't stop at the file.
Trace its provenance.
In the development environment, we found the file inside one of the standard CI images used by the runner.
In production, we went a step further and mapped the exact layer reported by SOC to a security-agent image already expected on the environment.
And suddenly the story looked very different.
The Difference Between "Found" and "Compromised"
This was probably the most important lesson.
A scanner can tell you:
"I found this file."
That doesn't automatically mean:
"An attacker installed this file."
And it definitely doesn't automatically mean:
"The server is compromised."
We still needed to establish the file's origin and context.
That is why tracing:
is so useful.
You want to move from:
- Unknown file
to:
- 01Known file
- 02Known layer
- 03Known image
- 04Expected component
The more you can establish that chain, the stronger your conclusion becomes.
Then Docker Gave Me Another Lesson
During the investigation, I also ran into something much less glamorous:
permission denied while trying to connect to the Docker daemon socket
The famous:
/var/run/docker.sock
appeared.
Using elevated Docker access allowed the inspection commands to run.
That was another reminder that access to the Docker daemon is powerful.
If you can control the Docker daemon, you are dealing with something much more privileged than simply being able to start a container.
So even while investigating a security alert, Docker was quietly teaching me another security lesson.
Very efficient teacher.
CI/CD Runners Make Investigations Messier
The machine involved wasn't a normal application server.
It was a CI/CD runner.
That means Docker tends to get messy:
- Build images
- Test images
- Helper images
- Cached layers
- Temporary containers
- Docker-in-Docker
- Security tooling
- Dangling images
- Old artifacts
All of that can accumulate over time.
So a security scanner looking through Docker storage may find files that are:
- inside an image
- inside an old layer
- cached locally
- part of a build environment
- not currently running at all
That doesn't mean alerts should be ignored.
It means the environment needs context.
A CI runner is naturally much noisier than a clean application VM.
The Mental Model I Took Away
Before this incident, my Docker mental model was mostly:
After it:
- 01Host
- 02Docker
- 03Image
- 04Layers
- 05Container
- 06Application
And when Kubernetes enters the picture:
- 01Cloud
- 02VM / Node
- 03Container Runtime
- 04Image Layers
- 05Container
- 06Pod
- 07Application
That extra depth matters when something goes wrong.
Because troubleshooting often means moving down the stack until you find the source of the problem.
What Started as a Malware Alert
The original message was basically:
What I eventually had to understand was:
- 01Security alert
- 02Linux filesystem
- 03Docker storage
- 04overlay2
- 05Layer
- 06Image
- 07Container / CI environment
- 08Provenance
That was much more valuable than simply finding the file.
I learned how Docker actually stores things.
I learned how images and layers relate to each other.
I learned that CI/CD runners can contain a lot of Docker artifacts.
And I learned that security investigations often depend less on the filename and more on the context surrounding the filename.
Final Thought
The most useful question in this whole investigation wasn't:
"Is this file malicious?"
It was:
"Where did this file come from?"
Once you start answering that question systematically, the investigation becomes much clearer.
- 01Where is it?
- 02Host or container?
- 03Which layer?
- 04Which image?
- 05Where did the image come from?
- 06Is it expected?
- 07Is it actually being used?
That is a mindset I want to keep as I move deeper into DevOps and platform engineering.
Because infrastructure isn't just about deploying things.
Sometimes it's about figuring out why a file exists three layers underneath a container on a machine you didn't even realize was storing it.
And yes...
I now look at /var/lib/docker/overlay2 a little differently.
…
Up to 50 claps per reader