Threat Model & Philosophy
Before you defend something, you have to be honest about who you’re defending against — and what you’re willing to lose.
Security isn’t a product you install; it’s a set of bets about who will attack you, how, and what happens when one of your defenses fails. This chapter states those bets plainly. Every later decision in this guide traces back to something here. If you only read one page of theory, read this one.
The core philosophy: assume breach
Most home setups are built on hope — “nobody will bother attacking me.” That is not a security model. This guide is built on the opposite assumption:
Assume every layer will eventually be breached, and design so that a breach of one layer is contained by the next. We never rely on a single wall. We build seven, each independent, so that defeating one buys an attacker almost nothing.
Imagine a bank. The cash isn’t protected by one locked door — it’s a locked building, inside it a locked vault room, inside that a vault, inside that a safe-deposit box. A thief who picks the front door is still standing in a hallway. We’re building the same thing for your computer.
This is called defense-in-depth, and it has a measurable payoff: the chance an attacker defeats every independent layer is the product of defeating each one. Ten percent times ten percent times ten percent gets very small, very fast.
Who we’re defending against
We design for four realistic adversaries, from most to least likely.
| Adversary | What they want | Their typical move |
|---|---|---|
| Automated bots | Any exposed service to hijack (crypto-mining, botnets) | Mass-scan the internet for open ports & known bugs |
| A compromised container image | To run their code on your hardware | You pull a poisoned image; it tries to escape the container |
| A vulnerable app you deployed | A foothold to pivot deeper | Exploit your app, then attack the cluster from inside |
| Someone with physical access | The data on the disk | Steal the machine or the USB drive and read it elsewhere |
We are not trying to stop a nation-state with unlimited budget and physical access to your running, unlocked machine. That’s an honest boundary, not a weakness — see “What’s out of scope” below. Everything short of that, we defend against hard.
The trust boundaries (where the walls are)
A trust boundary is a line where data or control passes from something less trusted to something more trusted. Every boundary is a place to put a guard.
- Internet → Host. The host firewall (nftables) drops everything except one WireGuard port. There is no open SSH, no open Kubernetes API, nothing to scan.
- Host → VM. k3s runs inside a virtual machine. A Kubernetes compromise is trapped in a disposable VM that can’t see the host’s secrets.
- VM → Pod. Each pod runs in a sandbox (gVisor/Kata) with dropped privileges, a read-only filesystem, and its own user namespace. A breakout hits a wall.
- Pod → Pod. The network denies all traffic by default; pods can only reach what you’ve explicitly allowed, and every packet between them is encrypted.
- Anything → Disk. Every byte at rest — the OS, the container layers, the object store, the volumes — is encrypted with LUKS2. A stolen drive is noise.
- Anything → Cluster control. Only signed, scanned images run. Only an authenticated admin over WireGuard can issue commands, and every command is logged immutably.
How each layer maps to a threat
| If an attacker… | …they are stopped/contained by |
|---|---|
| Scans your IP for open ports | L0 host firewall (default-deny, only WireGuard open) |
| Breaks Kubernetes itself | L1 immutable VM isolates them from the host |
| Escapes a container | L3 sandbox + dropped caps + user namespaces wall them in |
| Tries to spread to other pods | L4 default-deny network policy blocks lateral movement |
| Steals the machine or USB drive | L5 LUKS2 encryption makes the data unreadable |
| Sneaks in a malicious image | ✦ image signing + admission control refuses to run it |
| Quietly does damage from inside | L6 eBPF runtime sensor detects & can kill it in real time |
| Tampers with the running OS | L1 immutable, read-only root with dm-verity rejects changes |
Notice that no single failure is fatal. Every row above is backed by at least one other layer. That redundancy is the entire point.
The assume-breach mindset in practice
Three habits follow from “assume breach”, and the whole architecture enforces them:
- Least privilege everywhere. Nothing gets a permission it doesn’t strictly need — not a pod, not a service account, not a network connection, not you.
- Everything is logged. If you can’t see it, you can’t defend it. Layer 6 records metrics, logs, and kernel-level events continuously, and alerts on the ones that matter.
- Recoverable by design. Because the OS is immutable and the config is in Git, a compromised node is not a crisis — you wipe it and rebuild from scratch in minutes. We treat machines as cattle, not pets.
What’s out of scope (the honest part)
A guide that claims to stop everything is lying. Here is what this design does not fully solve, stated plainly so you can make your own call:
- A running machine with the disk already unlocked. Encryption protects data at rest. If an attacker has root on the live, booted host, they’re inside the walls. Physical security of the powered-on machine is on you.
- A backdoor in the silicon or firmware. We harden the firmware we can reach; we can’t audit the CPU’s microcode. Measured boot detects tampering it can see.
- Your own mistakes. If you paste a malicious script as root, or hand out your WireGuard key, no architecture saves you. The guide minimizes how often you must act as root, but discipline is still required.
- Zero-day bugs in the tools themselves. We reduce the blast radius (sandbox, network policy, immutability) so one unknown bug rarely reaches the core — but “rarely” is not “never.”
This is not hedging — it’s the map of where the walls end, so you know exactly what you’re trusting.
What this chapter bought you
You now know the four adversaries, the six trust boundaries, and the single idea that drives every command in this guide: assume breach, contain blast radius, log everything, rebuild fearlessly. Next we turn this philosophy into a concrete architecture you can see and build.