You Are One Kernel Exploit Away from Disaster
I audited a Kubernetes cluster for a mid-sized fintech in Oslo last week. They thought they were secure. They had firewalls, they had complex passwords, and they had "cloud-native" architecture. Yet, within 15 minutes, I had root access to their host node.
How? A simple container running as root, a mounted host volume, and a lack of seccomp profiles. They treated containers like lightweight VMs. They are not. Containers are just fancy processes with a few kernel namespace hacks. If you rely on default Docker settings in production, you are negligent. Period.
In this guide, we are going to fix your messy configurations. We aren't talking about theoretical "best practices" that no one implements. We are talking about the configurations that stop you from appearing in the next Datatilsynet breach report.
1. Stop Running as Root (Seriously)
The default user in a Docker container is root (UID 0). If an attacker breaks out of the container runtime—and historically, runc vulnerabilities like CVE-2019-5736 prove this is possible—they are root on your host server. Game over.
You must force your containers to run as an unprivileged user. If your application tries to bind to port 80 or 443, it will fail as a non-root user. Good. Use a reverse proxy or bind to high ports (like 8080).
The Fix:
Don't just add a USER instruction. Create a specific group and user with a high UID to avoid conflicts with host users.
# BAD
FROM node:14
CMD ["npm", "start"]
# GOOD
FROM node:14-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# Switch to user
USER appuser
# Ensure we own the directory
WORKDIR /home/appuser/app
COPY --chown=appuser:appgroup . .
EXPOSE 8080
CMD ["node", "index.js"]
2. The Supply Chain Nightmare: Scan Before You Ship
Pulling FROM python:latest is Russian Roulette. You have no idea what layers are inside that image or when it was last patched. In 2021, supply chain attacks are the primary vector for crypto-mining malware injection.
We integrate Trivy (by Aqua Security) into our CI/CD pipelines. It’s fast, stateless, and catches OS package vulnerabilities effectively.
$ trivy image python:3.9-alpine
2021-12-07T10:00:00.000+0100 INFO Detecting Alpine vulnerabilities...
2021-12-07T10:00:00.000+0100 INFO Trivy skips scanning programming language libraries...
python:3.9-alpine (alpine 3.14.2)
=================================
Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 0, CRITICAL: 0)
+---------+------------------+----------+-------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION |
+---------+------------------+----------+-------------------+
| apk-tools | CVE-2021-36159 | MEDIUM | 2.12.6-r0 |
| ssl_client| CVE-2021-36159 | MEDIUM | 1.33.1-r3 |
+---------+------------------+----------+-------------------+
Pro Tip: Never deploy an image with "Critical" or "High" CVEs. Break the build. It is better to delay a release by an hour than to spend a month explaining a breach to the Board.
3. Runtime Hardening: Read-Only Filesystems
If an attacker manages to execute a remote code execution (RCE) exploit, their next step is usually to download a payload or modify a config file. If your container's filesystem is read-only, their script fails.
This requires your application to be stateless. Logs should go to stdout/stderr (collected by a driver), and temporary files should go to an emptyDir volume mounted at /tmp.
Kubernetes SecurityContext Example
This configuration snippet is what separates professional deployments from amateur hour. It enforces non-root execution, prevents privilege escalation (sudo), and locks the filesystem.
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
containers:
- name: my-app
image: my-registry/app:v1.2
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
volumeMounts:
- mountPath: /tmp
name: tmp-volume
volumes:
- name: tmp-volume
emptyDir: {}
4. The Infrastructure Layer: Why Shared Kernels Fail
This is the part many hosting providers gloss over. Containers on a host share the kernel. If you are on a cheap "Container-as-a-Service" platform or a budget VPS using OpenVZ or LXC, you are sharing a kernel with your neighbors. If their container causes a kernel panic, your service goes down.
Isolation is not optional.
This is why we strictly use KVM (Kernel-based Virtual Machine) for all CoolVDS instances. KVM provides hardware-level virtualization. Your kernel is yours. Your memory space is yours. If a neighbor on the physical node gets DDoS'd or hacked, your environment remains strictly encapsulated.
| Feature | Container (Shared Kernel) | CoolVDS (KVM) |
|---|---|---|
| Isolation Level | Process (Namespaces) | Hardware (Virtualization) |
| Kernel Panic Risk | Takes down all containers | Isolated to single VM |
| Security Boundary | Weak (Software) | Strong (Hardware-assisted) |
| Custom Kernel Modules | Impossible | Fully Supported |
5. Network Policies and Data Residency
Norway is not in the EU, but we follow GDPR strictures closely via the EEA agreement. The Schrems II ruling has made relying on US-based cloud providers legally risky for handling Norwegian citizen data. Data sovereignty is now a technical requirement.
When you host on CoolVDS, your data sits in Oslo. Latency to NIX (Norwegian Internet Exchange) is often under 2ms. But speed means nothing without control. By default, Kubernetes allows all pods to talk to all pods. This is dangerous. Use NetworkPolicies to whitelist traffic.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-access
namespace: default
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend-api
ports:
- protocol: TCP
port: 5432
6. Host-Level Defense
Your containers are only as secure as the host they run on. On a CoolVDS instance, you have full control over the host OS. Don't leave SSH open to the world. We recommend a "Default Deny" strategy using `ufw` immediately after provisioning.
# Reset UFW to default deny incoming
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH only from your office VPN IP
sudo ufw allow from 192.168.1.50 to any port 22
# Allow HTTP/HTTPS for the ingress controller
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Final Thoughts: Performance vs. Security
There is always a trade-off. running `seccomp` filters costs CPU cycles. Encrypting overlay networks in Kubernetes adds overhead. But in 2021, computing power is cheap; trust is expensive.
At CoolVDS, we provide the raw power—high-frequency CPUs and NVMe storage—so you can afford the overhead of encryption and security scanning without your application feeling sluggish. We hand you the keys to a secure, isolated KVM environment. What you build inside it is up to you, but if you follow the rules above, you'll survive the night.
Stop gambling with shared kernels. Deploy your hardened containers on a dedicated CoolVDS KVM instance today.