Hey folks, Sedat here. Today, we’re going to air out some of the container world’s dirty laundry. But first, let’s start with that one big misconception that has become almost a religion in our industry: 'Just use Alpine Linux and your attack surface drops to zero.'
Look, I’m sorry, but that is pure 'security by obscurity' comfort food. Everyone raves about Alpine images, flexing with 'Look, it’s only 5MB, it doesn’t even have a shell.' Cool story, but when your app starts acting weird in prod because of those tiny incompatibilities between musl-libc and glibc, or when you can't keep up with security updates in package management, that 5MB won't be your shield. In fact, most of the time, an image stripped of troubleshooting tools is just a way to shoot yourself in the foot during incident response. If security were just about shrinking an image, the cyber world would be a very boring place.
The Image is Just a Shell, the Poison is Inside
In Red Team operations, what we encounter most aren't 'bloated' images, but clean, tiny images with secrets hardcoded right into them. My developer friend, when you write ENV DB_PASSWORD=testPassword123 in a Dockerfile, the moment you layer that image, that password is etched into history (and every layer log).
During a penetration test, extracting those 'old' env values by inspecting the image layers is a gold mine for us. I don't care if the image size is 2 KB; if that password is there, we aren't talking about container security anymore.
Our 'Root' Obsession and the Capabilities Curse
The original sin of the container world: USER root.
Too many people think, 'I don't want to deal with permission errors, and anyway, aren't containers isolated?' No, buddy. A container is not a virtual machine. It shares the kernel with the host machine. If you run a process as root inside a container and there is an 'escape' vulnerability, congrats; you’ve just handed us the entire host.
Running a container with the --privileged flag is essentially giving a thief your house keys and saying, 'I’m going to the market, make yourself at home.' Let’s look at a bad Dockerfile example and how we can fix it:
Bad Example (Red Team Favorite):
# testCompany - Bad Practice
FROM node:14
WORKDIR /app
COPY . .
# App is running as root!
RUN npm install
CMD ["node", "app.js"]
Good Example (Hardened):
# testCompany - Secure Practice
FROM node:14-slim
# Clean up and create a dedicated user
RUN groupadd -r nodejs && useradd -r -g nodejs nodejs
WORKDIR /app
COPY --chown=nodejs:nodejs . .
USER nodejs
# Only expose the necessary port
EXPOSE 3000
CMD ["node", "app.js"]
That USER nodejs line makes it incredibly difficult for an attacker to escalate privileges on the system, even if they manage to execute code inside the container.
Docker Socket: The Devil's Backdoor
Sometimes system...
