Container Security is a Lie: Hardening Kubernetes in a Hostile Environment
Let’s get one thing straight: a container is just a process with a curfew. If you believe that a standard Docker container provides a solid security boundary, you are operating on hope, not engineering. In 2025, with supply chain attacks targeting the software registry layer and kernel exploits like Dirty Pipe variants still echoing in our logs, default configurations are negligence.
I recently audited a cluster for a fintech client in Oslo. They had impeccable CI/CD pipelines, but their pods were running as root, mounting the host filesystem, and communicating freely with the entire VPC. They weren't hacked because they were smart; they weren't hacked because they were lucky. Luck runs out.
We are going to lock this down. No fluff. Just the raw config and architectural decisions required to run compliant, hardened workloads in Northern Europe.
1. The Base Image is Your First Vulnerability
Most developers treat FROM node:22 as a benign starting point. It isn't. It is a massive surface area containing shells, package managers, and libraries you do not need. If /bin/sh exists in your production container, an attacker has a keyboard. If apt or apk exists, they have an arsenal.
We use Distroless or chaotic-stripped Alpine images. But even then, we never trust tags. Tags are mutable. A tag pointing to a clean image today can point to a compromised one tomorrow.
The Immutable Build Pattern
Always pin by SHA256 digest. This ensures that the binary definition of your environment never shifts without a git commit.
# BAD: Mutable tag
# FROM alpine:3.21
# GOOD: Immutable digest
FROM alpine@sha256:a8560b36e8b8210634f77d9f7f942750877a1104e578b97d2685713e254e26ef AS builder
WORKDIR /build
COPY . .
RUN go build -ldflags="-w -s" -o app main.go
# FINAL STAGE: Scratch (No shell, no users)
FROM scratch
COPY --from=builder /build/app /app
# Import certificates for external API calls (essential for Datatilsynet compliance checks)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 10001
ENTRYPOINT ["/app"]
By using FROM scratch, we remove the OS entirely. There is no shell to spawn. If an attacker manages a Remote Code Execution (RCE), they have nowhere to go.
2. Runtime Defense: The Kernel is the Battlefield
Containers share the host kernel. This is the fundamental trade-off of containerization versus virtualization. If a process in a container crashes the kernel, the whole node goes down. If a process exploits a syscall, they own the host.
This is where infrastructure choice becomes critical. At CoolVDS, we enforce strict KVM (Kernel-based Virtual Machine) isolation for our instances. Unlike legacy OpenVZ or LXC providers where you share a kernel with noisy neighbors, a CoolVDS NVMe instance gives you your own kernel. You can enable SELinux, load eBPF probes, and tune sysctl flags without asking for permission.
Enforcing Security Contexts
Kubernetes allows us to define the blast radius. You must forbid root execution and ensure the filesystem is read-only. If your app needs to write logs, mount a emptyDir volume.
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-payment-gateway
namespace: finance
spec:
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
containers:
- name: gateway
image: private-registry.coolvds.com/gateway@sha256:...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Dropping ALL capabilities and only adding back NET_BIND_SERVICE (if you bind low ports) neutralizes a vast majority of privilege escalation exploits.
3. Network Policies and Data Sovereignty
In Norway, data sovereignty isn't just a buzzword; it's legal dogma. Under GDPR and the continued fallout of Schrems II, you must guarantee that traffic doesn't accidentally route through non-compliant regions. By default, Kubernetes allows all pods to talk to all pods. This is insane.
We use NetworkPolicies to create a "Default Deny" posture. Nothing moves unless explicitly whitelisted.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: finance
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: finance
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
This configuration ensures that a compromised frontend pod cannot scan your database pod unless you explicitly write a policy allowing that specific TCP flow.
Pro Tip: When hosting in Oslo, latency to the NIX (Norwegian Internet Exchange) matters. Misconfigured ingress controllers often route traffic through centralized load balancers in Frankfurt or Amsterdam before returning to Norway. Using CoolVDS local instances ensures your internal traffic stays within the national grid, reducing latency and simplifying compliance audits.
4. Supply Chain Verification
In 2025, we don't trust; we verify. Implementing SBOM (Software Bill of Materials) scanning is mandatory. We integrate tools like Trivy or Grype into the CI pipeline. The build fails if a 'Critical' CVE is detected.
Here is a snippet for your pipeline configuration:
# Scan the image before pushing
trivy image --exit-code 1 --severity CRITICAL --no-progress private-registry.coolvds.com/gateway:v1.2.5
# Verify the signature (using Cosign)
cosign verify --key k8s://finance/cosign-key private-registry.coolvds.com/gateway:v1.2.5
5. The Underlying Host: Sysctl Hardening
You cannot have a secure container on an insecure host. Because CoolVDS provides full KVM virtualization, you have access to the /etc/sysctl.conf of your node. You should be tuning this to prevent network stack exploits.
Apply these settings to your host nodes:
# Prevent IP Spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP Redirects (Prevent Man-in-the-Middle)
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# Log Martians (Packets with impossible addresses)
net.ipv4.conf.all.log_martians = 1
# Protect against SYN flood attacks
net.ipv4.tcp_syncookies = 1
Conclusion
Security is not a product; it is a process of reducing risk to an acceptable level. By stripping your base images, enforcing read-only runtimes, locking down the network, and hardening the host kernel, you make your infrastructure hostile to attackers.
However, all this configuration is useless if the underlying hardware is oversold or the hypervisor is insecure. You need raw, predictable I/O and true isolation.
Stop fighting noisy neighbors. Deploy your hardened Kubernetes nodes on CoolVDS NVMe instances today and keep your data strictly within Norwegian jurisdiction.