Container Security in 2021: Stop Running as Root and Start Fearing the Kernel
Most developers treat Docker containers like lightweight Virtual Machines. They aren't. They are just processes lying to themselves about how much access they have to the host kernel. If you are deploying containers in production today—especially here in Norway where Datatilsynet is watching—and you haven't hardened your runtime, you are one kernel exploit away from a total compromise.
I have seen production clusters taken down by simple cryptojacking scripts because someone left the Docker socket exposed or ran a container as --privileged. It's messy. It's expensive. And it is entirely preventable.
1. The "Root" of All Evil
By default, processes inside a Docker container run as root. If a malicious actor breaks out of the container (which happens more often than the brochure admits), they are root on your host node. Game over.
The fix is boring but mandatory: Create a specific user in your Dockerfile. Do not rely on the default.
# The Wrong Way
FROM node:14-alpine
WORKDIR /app
COPY . .
CMD ["node", "index.js"]
# The Right Way (March 2021 Standard)
FROM node:14-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY . .
USER appuser
CMD ["node", "index.js"]
When you switch to a non-root user, you will immediately hit permission errors. Good. That means the system is working. Fix the permissions explicitly rather than granting blanket access.
2. Drop Capabilities Like They’re Hot
Even if you are not root, the Linux kernel grants specific "capabilities" to processes. Does your Nginx container really need CAP_SYS_ADMIN or CAP_NET_ADMIN? Absolutely not.
We operate on a principle of least privilege. In 2021, the best practice is to drop all capabilities and add back only what is strictly necessary. Here is how you do it in a standard docker run command:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-web-server
For Kubernetes (v1.19+), you define this in your Pod's securityContext. This is non-negotiable for any workload exposed to the public internet.
apiVersion: v1
kind: Pod
metadata:
name: secure-nginx
spec:
containers:
- name: nginx
image: nginx:1.19
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsNonRoot: true
readOnlyRootFilesystem: true
Pro Tip: noticereadOnlyRootFilesystem: true? This prevents attackers from writing malicious binaries or modifying configuration files inside the container. If they get in, they can't stay. To make this work, you'll need to mount/tmpas anemptyDirvolume so the app can still write temporary data.
3. The Supply Chain Trap
The SolarWinds hack late last year taught us that you can't trust your vendors blindly. Docker Hub is full of images containing vulnerabilities. If you are pulling node:latest, you are asking for trouble.
Use a scanner like Trivy or Clair in your CI/CD pipeline. If the scan fails, the build breaks. No exceptions.
# Scan an image with Trivy (v0.16.0)
trivy image coolvds-app:v1.0.0
If you see High or Critical CVEs, you fix them or you don't deploy. It is that simple.
4. Infrastructure Matters: The CoolVDS Isolation Layer
Here is the dirty secret about cheap VPS providers: many use container-based virtualization (like OpenVZ/LXC) to oversell resources. In that environment, your "server" is just a container sharing a kernel with the "server" next door. If your neighbor gets DDoS'd or exploits a kernel bug, you go down with them.
This is why we architect CoolVDS on strict KVM (Kernel-based Virtual Machine) hypervisors. KVM provides hardware-level virtualization. Your kernel is your kernel. This isolation is critical for security compliance, especially when handling sensitive data.
Comparison: Shared vs. Dedicated Kernels
| Feature | Container Virtualization (Common) | KVM (CoolVDS Standard) |
|---|---|---|
| Kernel Isolation | Shared (High Risk) | Dedicated (High Security) |
| Noisy Neighbors | Common | Eliminated via strict resource limits |
| Docker Support | Often requires tricks | Native, behaves like bare metal |
5. Norway, Schrems II, and Data Residency
We need to talk about the legal side. Since the Schrems II ruling last July, transferring personal data to US-owned cloud providers has become a legal minefield. The Privacy Shield is dead.
If your servers are physically located in Frankfurt but owned by a US entity, you are in a gray area that makes compliance officers sweat. Hosting on CoolVDS keeps your data physically in Norway, under Norwegian jurisdiction, and compliant with European standards. Our NVMe storage arrays are located in Oslo datacenters, ensuring that your data doesn't accidentally take a trip across the Atlantic.
6. Network Policies: Don't Talk to Strangers
By default, all pods in a Kubernetes cluster can talk to each other. Your frontend has no business talking to your Redis cache directly if there is a backend API in the middle.
Use NetworkPolicies to lock down traffic. Deny everything, then allow only specific paths.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
This applies to your VPS firewall too. If you are running Docker directly on a CoolVDS instance, use ufw or iptables to restrict access to the Docker daemon port (2375/2376). Never expose that to the public internet.
Final Thoughts
Security isn't a product you buy; it's a process you suffer through. But the suffering pays off when you are the only one online during a massive exploit campaign. By hardening your container runtime and running on true KVM virtualization, you reduce your attack surface by 99%.
Don't let a shared kernel be your single point of failure. Deploy your hardened containers on CoolVDS today. Our NVMe-backed KVM instances in Oslo are ready for your production workload.