Skip to main content
Katakate is built for executing untrusted code — model output, third-party scripts, anonymous CI jobs. The threat model assumes the workload inside a sandbox is hostile and tries to escape, exfiltrate data, or attack other tenants. This page summarizes the layered defenses; for hands-on configuration see the API security & networking reference.

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-devmapper uses 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-longhorn uses 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-system traffic is allowed for cluster services. kubectl exec / k7 shell are 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_used timestamp recorded for audit.
  • Authenticate via X-API-Key: <key> or Authorization: Bearer <key>.
The API runs as a K3s Deployment with its own ServiceAccount and a scoped ClusterRole — no admin kubeconfig is ever mounted.

Operational guidance

  1. Always lock down egress for untrusted workloads. Prefer FQDN allowlists (Cilium) — much less error-prone than tracking CIDR ranges that change.
  2. Don’t whitelist public DNS resolvers (1.1.1.1, 8.8.8.8) — that re-enables DNS exfiltration.
  3. Rotate API keys with k7 revoke-api-key + k7 generate-api-key. Treat them as credentials.
  4. Set a TLS reverse proxy in front of the NodePort if you expose the API publicly (the NodePort itself is plain HTTP).
  5. Pin image tags:latest is 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.
See SECURITY.md for the full disclosure policy and reporting channel (security@katakate.org).