Stop Running Containers as Root: A Survival Guide for 2024
If you are still deploying containers with USER root in your Dockerfiles in late 2024, you aren't deploying software; you are deploying liabilities. I've spent the last decade watching "secure" environments crumble because a developer assumed a container was a magical sandbox. It isn't. It's just a process with some cgroup namespaces and a false sense of security.
The kernel is shared. If an attacker escapes the container—and with the recent runc vulnerabilities, that is not a theoretical risk—they own the host. In a shared hosting environment, that usually means they own your neighbors too. This is why at CoolVDS, we enforce strict KVM virtualization at the infrastructure level. We don't trust your containers, and neither should you.
1. The Base Image: Trust Nothing
Supply chain attacks are the defining security challenge of 2024. Remember the xz backdoor? That was a wakeup call. Pulling node:latest is asking for trouble. You need minimal attack surfaces.
Use distroless images or highly minimal Alpine variants. They lack shells, package managers, and the tools attackers use to expand their foothold.
The "Golden" Dockerfile Pattern
Here is how we build rigid, production-grade images for high-security Norwegian fintech clients:
# Build Stage
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o main .
# Final Stage - Distroless
FROM gcr.io/distroless/static-debian12
# NON-ROOT IS MANDATORY
USER nonroot:nonroot
COPY --from=builder /app/main /
# Drop all capabilities
# This is done at runtime, but document it here
# docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...
ENTRYPOINT ["/main"]
Pro Tip: Never leaveapkoraptusable in the final layer. If an attacker gets RCE (Remote Code Execution), don't give them a package manager to install `nmap` or `curl`. Make them work for it.
2. Runtime Security: If It Spawns a Shell, Kill It
Static analysis (trivy, grype) is fine for the pipeline. But what happens at 3:00 AM when a zero-day hits? You need runtime detection. In 2024, eBPF is the standard. Tools like Falco monitor kernel syscalls with negligible overhead.
If a web server container suddenly spawns /bin/bash or tries to modify /etc/passwd, that process should die immediately.
Here is a standard Falco rule we deploy on our Kubernetes clusters:
- rule: Terminal shell in container
desc: A shell was used as the entrypoint for the container entry
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
and container_entrypoint
output: >
Shell executed in container (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
priority: WARNING
3. Data Residency & The "Norwegian Fortress"
Let's talk about the legal reality. The NIS2 directive is now in full force across Europe, and Datatilsynet (The Norwegian Data Protection Authority) is not lenient. If you are processing personal data of Norwegian citizens, hosting it on a hyper-scaler with opaque data replication policies is risky.
Latency is physics. Compliance is law. You need both.
When you deploy on CoolVDS, your data sits on NVMe arrays physically located in Oslo. We don't replicate to Frankfurt or Iowa unless you explicitly configure it. This simplifies your GDPR Article 44 assessments significantly. Furthermore, local peering via NIX (Norwegian Internet Exchange) ensures that traffic between your users and your API often never leaves the country, keeping latency often below 5ms for local users.
4. Kubernetes Policy Enforcement
Pod Security Policies (PSP) are dead. In 2024, we use admission controllers like Kyverno or OPA Gatekeeper. You must block insecure workloads before they are ever scheduled.
This Kyverno policy ensures no container runs as root. If a developer tries to apply a bad YAML, the cluster rejects it.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root-user
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-runasnonroot
match:
resources:
kinds:
- Pod
validate:
message: "Running as root is not allowed. Set runAsNonRoot to true."
pattern:
spec:
securityContext:
runAsNonRoot: true
containers:
- =(securityContext):
=(runAsNonRoot): true
5. Network Policies: Zero Trust Inside the Cluster
By default, Kubernetes allows all pods to talk to all pods. This is a flat network. If your frontend is compromised, the attacker has a direct line to your database.
Lock it down. Use a CNI that supports NetworkPolicies (Cilium or Calico). Here is a policy that isolates a database so only the backend service can talk to it:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-access-policy
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend-api
ports:
- protocol: TCP
port: 5432
6. The Infrastructure Layer: KVM vs. The Rest
All the container hardening in the world doesn't matter if the hypervisor is weak. Many "cloud" providers use lightweight container-based virtualization (like LXC/LXD) for their VPS offerings to oversell resources. This leads to "noisy neighbor" issues and potential kernel exploits leaking between customers.
CoolVDS uses KVM (Kernel-based Virtual Machine). You get a dedicated kernel. Your memory is reserved. Your NVMe I/O is yours. This provides the hard isolation required for NIS2 compliance while maintaining the raw IOPS needed for high-traffic databases.
Conclusion
Security is not a product; it's a process of reducing risk. By stripping your images, enforcing non-root execution, and locking down network paths, you make an attacker's life miserable. But you must build on solid ground.
Don't risk your compliance status or your uptime on overselled, insecure infrastructure. Spin up a hardened KVM instance on CoolVDS today and see what stable, local hosting feels like.