Console Login

Container Security in the Wild West: Hardening Docker 1.9 for Production

Container Security in the Wild West: Hardening Docker 1.9 for Production

Let’s be honest: 2015 has been a chaotic year for infrastructure. We’ve seen the rise of microservices, the explosion of Docker usage, and—as of October—the complete legal collapse of the Safe Harbor agreement. If you are a Systems Architect or CTO in Europe, you aren't just fighting packet loss; you are fighting international legal ambiguity.

And then there’s Docker. We all love it. It solves the "works on my machine" problem. But let’s talk about the elephant in the server room: Security. By default, Docker is a loaded gun pointed at your foot. If you are running docker run as root on a shared kernel without thinking, you are one kernel panic away from a total outage.

I’ve spent the last month migrating a high-traffic Magento cluster to Docker 1.9, and I’ve seen things that would make a junior sysadmin cry. Here is how we lock it down, keep the Datatilsynet (Norwegian Data Inspectorate) happy, and why the underlying hardware matters more than you think.

The "Root" of All Evil

The most dangerous misconception in our industry right now is that a container is a "lightweight VM." It is not. A container is just a process with a fancy mask (cgroups and namespaces). If that process runs as root inside the container, it potentially has root access to your kernel.

In a standard VPS environment using OpenVZ or LXC (which many budget hosts still use), your "root" user is often dangerously close to the host node’s root. This is why we never deploy production containers on anything less than KVM virtualization.

Step 1: Stop Being Root

The easiest win is to stop running your applications as root. I see Dockerfile examples all over GitHub that look like this:

FROM node:4.2 COPY . /app CMD ["node", "index.js"]

This runs as UID 0. If an attacker exploits a buffer overflow in Node, they are root. Change it. Create a user. Be boring.

FROM node:4.2
RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY . /app
USER appuser
CMD ["node", "index.js"]

The KVM Safety Net (Why CoolVDS Wins)

Here is the hard truth about the hosting market in 2015: Noisy Neighbors kill containers.

If you run Docker on a shared hosting platform that over-commits resources (using OpenVZ), you are sharing the kernel with the guy next door running a poorly optimized Minecraft server. If his process triggers a kernel panic, your Docker daemon dies. Game over.

This is why at CoolVDS, we strictly use KVM (Kernel-based Virtual Machine). With KVM, your VPS has its own dedicated kernel. Docker can crash, burn, and panic inside your instance, and it won't affect the host node—and more importantly, nobody else's crash will take you down. For mission-critical workloads in Norway, where uptime is legally mandated by some SLAs, KVM isn't a luxury; it's a requirement.

Hardening the Runtime: --cap-drop is Your Friend

Linux "capabilities" divide the power of root into small slices. Most web apps don't need to change system time or load kernel modules. Yet, Docker gives them these powers by default. Take them away.

When you start your Nginx or Python container, strip it naked and only give it what it needs:

docker run --d -p 80:80 \ --cap-drop=ALL \ --cap-add=NET_BIND_SERVICE \ --read-only \ nginx:1.9

This command does three critical things:

  1. Drops all privileges: The container can't do anything system-level.
  2. Adds networking: Allows binding to port 80.
  3. Read-Only: Makes the container's filesystem immutable. Hackers can't install backdoors if they can't write to disk.
Pro Tip: Never pass secrets (API keys, DB passwords) as environment variables (-e DB_PASS=secret). Anyone who runs docker inspect can see them. Instead, mount a read-only volume containing a config file restricted to the container user.

Network Isolation with Docker 1.9

Docker 1.9 (released just last month) finally gave us proper Overlay Networking. Before this, linking containers across hosts was a nightmare of port mapping and HAProxy glue.

Now, you can create isolated networks. Your database should never be public. Create a backend network:

# Create a private network
docker network create --driver bridge backend-net

# Run MySQL in the private network
docker run -d --name db --net backend-net mysql:5.6

# Run Web App attached to both networks
docker run -d --name web --net backend-net -p 80:80 my-web-app

In this setup, the database has NO public port exposed to the internet. Only the web container can talk to it. This simple architectural change mitigates 90% of remote database exploits.

The "Safe Harbor" Fallout: Data Sovereignty

Since the ECJ invalidated the Safe Harbor agreement in October, storing European user data on US-controlled servers is a legal minefield. If you are hosting customer data for Norwegian clients, you need to know exactly where those bits live.

Latency is another factor. If your users are in Oslo or Bergen, routing traffic to a data center in Frankfurt or (god forbid) Virginia adds 30-100ms of latency. In the age of instant page loads, that's unacceptable.

CoolVDS infrastructure is optimized for the Nordic region. By hosting on our NVMe-accelerated KVM instances, you aren't just getting raw I/O performance (though you definitely get that); you are ensuring your data stays within the EEA, compliant with the Data Protection Directive and upcoming regulations.

Summary: The 2015 Survival Checklist

Feature Standard VPS (OpenVZ) CoolVDS (KVM)
Kernel Isolation Shared (Risky) Dedicated (Secure)
Docker Compatibility Limited / Hacky Native / Full
Storage Speed Standard SATA NVMe SSD
Data Location Often Unknown Norway/Europe

Security isn't a product; it's a process. But that process starts with a solid foundation. You can write the best Dockerfile in the world, but if your underlying host is unstable or insecure, it doesn't matter.

Stop fighting with noisy neighbors and shared kernels. Build your fortress on iron.

Deploy your hardened Docker stack on a CoolVDS KVM instance today. Spin up time: 55 seconds.