Defense in depth
Each boundary below has to be defeated before an attacker reaches the next one. The untrusted workload is at the center; the cluster is the outermost ring. A successful escape from the workload has to defeat every layer above before it can reach another tenant or the host.VM isolation
Every sandbox is a Kata Containers pod, which means a real VM with its own kernel running on KVM. The workload sees a virtio block device for its rootfs, a virtio NIC, and not much else. The virtual hardware surface is tiny compared to a shared host kernel.kata-firecracker-devmapperuses Firecracker — ~50k LoC Rust microVMM, ~5MB resident, ~125ms boot. The Firecracker process runs inside the jailer: a chroot jail with an empty filesystem, dropped capabilities, a restrictive seccomp filter, and an unprivileged UID. An integration test (tests/integration/test_firecracker.py::test_jailer_active) verifies the jailer is active on every install.kata-qemu-longhornuses QEMU — bigger surface than Firecracker but still hardware-isolated, and Kata applies its own sandboxing primitives.
Container hardening
Inside the VM, the user container is further locked down:Some package managers (
apk add, apt-get install) require root inside the container. Either run them in before_script with the defaults, or prebuild your image with the dependencies baked in.Network isolation
- Ingress is denied by default — pods cannot talk to each other, even within the same namespace.
kube-systemtraffic is allowed for cluster services.kubectl exec/k7 shellare unaffected (they use the K8s API). - Egress is configurable per sandbox via
egress_whitelist: open by default, blocked with[], restricted by CIDRs, or restricted by FQDNs (with Cilium). DNS is blocked by default when egress is locked down — see the Networking page.
API authentication
- API keys are generated with
secrets.token_urlsafe(32)and only shown once at creation. - Stored in
/etc/k7/api_keys.json(mode 0600) as SHA-256 hashes — the plaintext key is never written to disk. - Comparison uses
secrets.compare_digest(constant-time, timing-attack resistant). - Optional expiry; expired keys are purged opportunistically on every authenticated request.
last_usedtimestamp recorded for audit.- Authenticate via
X-API-Key: <key>orAuthorization: Bearer <key>.
Operational guidance
- Always lock down egress for untrusted workloads. Prefer FQDN allowlists (Cilium) — much less error-prone than tracking CIDR ranges that change.
- Don’t whitelist public DNS resolvers (
1.1.1.1,8.8.8.8) — that re-enables DNS exfiltration. - Rotate API keys with
k7 revoke-api-key+k7 generate-api-key. Treat them as credentials. - Set a TLS reverse proxy in front of the NodePort if you expose the API publicly (the NodePort itself is plain HTTP).
- Pin image tags —
:latestis convenient but non-reproducible. K7 itself pins every image and Kubernetes manifest version it ships; do the same in your sandbox configs.
Known limitations
- Pre-1.0 software under active security review. Avoid for hyper-sensitive workloads until 1.0.
- No API rate limiting yet — put a proxy with rate limits in front for public exposure.
- AppArmor profiles are on the roadmap (not yet shipped).
- TEE (Trusted Execution Environment) support is on the roadmap.
