Container Security: Stop Treating Docker Like a VM
Let’s be honest. Most of you are running Docker containers as root. I see it every day. You pull a standard image, maybe ubuntu:16.04 or node:6, map a few ports, and think you've achieved "isolation."
You haven't.
Containers are not Virtual Machines. They are fancy processes using cgroups and namespaces to lie to the application about what resources it can see. If you are running a container as root on a shared kernel without dropping capabilities, you are one kernel exploit away from handing over your entire server to an attacker. In the Norwegian hosting market, where data privacy (Datatilsynet is watching) is paramount, this negligence is unacceptable.
I’ve spent the last week auditing a client's infrastructure in Oslo. They mounted /var/run/docker.sock into a Jenkins container to build images. A junior dev wrote a script that accidentally escaped the container and wiped the host's /etc directory. This isn't theoretical. It happens.
The Root of the Problem (Literally)
By default, the process inside the container runs as the root user (UID 0). Since the container shares the host kernel, UID 0 inside the container is UID 0 outside the container, just namespaced. If a hacker breaks out, they are root on your host.
Here is the first thing you need to change in your Dockerfile. Today.
1. Create a User
# BAD PRACTICE
FROM node:6
COPY . /app
CMD ["node", "/app/index.js"]
# GOOD PRACTICE
FROM node:6
RUN groupadd -r app && useradd -r -g app app
WORKDIR /app
COPY . /app
RUN chown -R app:app /app
USER app
CMD ["node", "index.js"]
This simple change forces the process to run as app. Even if they exploit a buffer overflow in Node, they drop into a shell with limited permissions.
Kernel Capabilities: Drop 'Em All
The Linux kernel breaks down root privileges into distinct units called "capabilities." Does your Nginx web server need to load kernel modules? No. Does it need to change system time? Absolutely not.
Yet, Docker gives containers a massive list of capabilities by default. In a production environment, you should adopt a whitelist approach. Drop everything, then add back only what you need.
Run your containers like this:
docker run --d -p 80:80 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--name secure-nginx \
nginx:1.10
The --read-only flag is a favorite of mine. It mounts the container's root filesystem as read-only. Attackers can't download scripts, compile malware, or modify configurations if they can't write to the disk. If your app needs to write logs or temp files, mount a specific volume for that.
Pro Tip: Use Docker Bench for Security. It's a script that checks for dozens of common best practices around deploying Docker containers in production. It was updated heavily this year and it's essential for compliance.
The Host Layer: KVM vs. The World
This is where infrastructure choice becomes critical. Many "cheap" VPS providers in Europe still use container-based virtualization like OpenVZ or LXC to slice up their servers. This means you are running your Docker containers inside their container.
This is a security nightmare.
You cannot effectively modify kernel parameters or rely on strict isolation if your "server" is just a container sharing a kernel with 50 other noisy neighbors. You need hardware virtualization.
At CoolVDS, we exclusively use KVM (Kernel-based Virtual Machine). When you spin up an instance with us, you get your own dedicated kernel. This provides a hard isolation boundary. Even if a container breakout occurs within your VM, the attacker is trapped inside your VM, not roaming our physical hypervisor.
Optimizing the Host Kernel for Containers
Since you control the kernel on CoolVDS, you should tune it for container density and network security. Here are the /etc/sysctl.conf settings I deploy on every Docker host:
# Enable IP forwarding (Required for Docker networking)
net.ipv4.ip_forward = 1
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
# Log packets with impossible addresses to kernel log
net.ipv4.conf.all.log_martians = 1
# Disable ICMP broadcasting
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Increase connection tracking table (Crucial for high container count)
net.netfilter.nf_conntrack_max = 524288
Apply these with sysctl -p.
Network Segregation
With Docker 1.9 (released late last year) and the new 1.12 Swarm mode, we finally have decent networking overlays. Stop using the default bridge (docker0) for everything. If you use the default bridge, containers can communicate via IP addresses indiscriminately.
Create user-defined networks for isolation. If your web container gets hacked, it shouldn't be able to ping your database unless explicitly allowed.
docker network create --driver bridge backend-net
docker run -d --net=backend-net --name db mysql:5.7
docker run -d --net=backend-net --name api my-api:latest
Legacy links (--link) are deprecated. Don't use them. They modify /etc/hosts in a way that creates a dependency mess.
Resource Limits (The DDoS Defense)
One compromised container can consume 100% of your CPU, starving critical system processes like sshd. Suddenly, you can't even login to fix the problem. This is a classic Denial of Service.
Always set limits. It's not optional.
version: '2'
services:
web:
image: nginx
cpus: '0.5'
mem_limit: 512m
restart: always
In a docker-compose.yml (using version 2 syntax), this ensures the web service never eats more than half a core or 512MB of RAM. Stability is about predictability.
Data Privacy in the North
With the EU General Data Protection Regulation (GDPR) looming on the horizon (adopted this April, enforcement coming soon), data sovereignty is a hot topic for Norwegian CTOs. You cannot afford to have data traversing insecure pipes.
When you host on CoolVDS, your data stays in our Oslo data center. We own the hardware. But remember, we secure the perimeter; you must secure the process.
Don't be the admin who explains to the CEO why the customer database was leaked because of a default root user in a Dockerfile.
Final Thoughts
Containerization is the future. It speeds up deployment and ensures consistency from dev to prod. But in 2016, the tooling is still maturing, and the defaults are dangerous.
Security is not a product you buy; it's a configuration you maintain. It starts with a solid foundation—NVMe storage for fast I/O and KVM for isolation—and ends with your discipline in managing capabilities and user permissions.
Need a safe sandbox to test your hardened Docker images? Deploy a KVM instance on CoolVDS in under 55 seconds. Low latency, high security, no nonsense.