Non-root execution
pod_non_root(boolean, default false): Run the Pod as non-root (UID/GID/FSGroup 65532). Applies pod-wide filesystem ownership.container_non_root(boolean, default false): Run the main container as non-root (UID 65532) and disallow privilege escalation.
- Enable both flags for consistent non-root behavior and fewer permission surprises when writing to volumes.
- Some package managers (e.g., Alpine
apk add) require root. To runapk addinside the container, you have options:- Use
before_scriptwith a base image that already includes needed tools, or - Temporarily run the main container with root by leaving
container_non_rootdisabled for setup, or - Build a custom image with dependencies pre-installed (recommended for production reproducibility).
- Use
Linux capabilities
Default policy: drop ALL capabilities. Add back only what you need. If you specifycap_drop explicitly, you override the default; to keep drop ALL and add back minimal caps, leave cap_drop unset and only use cap_add.
cap_drop(string[]): Capabilities to drop. If omitted,ALLis dropped by default.cap_add(string[]): Capabilities to add back.allow_privilege_escalation: always set tofalse.- Seccomp profile:
RuntimeDefault.
Common capability requirements
When you encounter permission errors inbefore_script or during container execution, you may need to add specific capabilities. Here are common use cases:
Package managers (apt-get, yum, dnf):
SETUID/SETGID: Required for package managers to drop privileges during installationCHOWN: Needed for changing file ownership during package installationDAC_OVERRIDE: Allows bypassing file permission checks (needed for installing packages)
NET_RAW or NET_ADMIN significantly reduces isolation. Only use when absolutely necessary for network debugging or specialized tools.
Best practice: pre-build custom images with dependencies installed rather than installing packages at runtime. This improves security, reproducibility, and startup time.
Network isolation and egress lockdown
Platform isolation (always on, Cilium)
A cluster-wide deny-only policy (k7-sandbox-platform-deny) stops every sandbox — in every egress mode, including open egress — from reaching the nodes, the Kubernetes API server, cloud metadata / link-local, and the kube-system / longhorn-system pods except CoreDNS. Nothing to configure; not available on --cni flannel.
Ingress isolation (Default: deny all, opt-in per sandbox)
All inter-VM communication is blocked by default to prevent sandbox-to-sandbox access. Key points:- Ingress blocking: VM sandboxes cannot communicate with each other unless a port is opened
- Administrative access preserved:
kubectl execandk7 shellstill work normally (they use the Kubernetes API, not pod networking) - System services allowed: Traffic from
kube-systemnamespace is permitted for cluster functionality
ingress_ports, scope the callers with ingress_from, and publish outside the cluster with expose_ports:
ingress_portswithoutingress_from= reachable from other sandboxes in the same namespace only. Reaching a sandbox from anywhere requires"cidr:0.0.0.0/0", spelled out.- Every entry in
expose_portsmust also be iningress_ports; theNodePortService usesexternalTrafficPolicy: Localso the pod sees the real client IP, and only answers on the node running the sandbox. The allocated NodePorts come back asnode_portson list / get. ingress_fromwithoutingress_ports, an unknown source prefix, or anexpose_portsentry missing fromingress_portsis a400.
Egress lockdown and whitelisting
Useegress_whitelist to control outbound traffic. The policy is applied after the container becomes Ready so before_script runs with open egress.
Behavior:
- Omit
egress_whitelist: egress open (external internet allowed). []: full egress block (no DNS resolution; no outbound IPs).["CIDR", ...]: allow only listed CIDR blocks; DNS is blocked.
Network Policy Details:
- Ingress: Blocked by default (inter-VM isolation) - system services and kubectl exec still work
- DNS: When egress is locked down, DNS resolution is blocked by default (no CoreDNS exception)
- Administrative access:
kubectl exec,k7 shell, and API operations bypass network policies
Mitigations when DNS is blocked
- Use IP/CIDR whitelisting only (no domains post-lockdown)
- Pre-resolve/fetch in
before_script(runs before lockdown with open egress) - If you must allow DNS temporarily, consider an operational override at cluster level (not provided by K7 config)
API key namespace scope
k7 generate-api-key NAME -n team-a stores an optional namespaces list on the key. The API enforces it on every namespace-bearing endpoint:
- Listed namespaces only — another namespace returns
403 Forbidden. - All-namespaces list / GC (
namespaceomitted orall_namespaces=true) is denied for scoped keys. - Cluster-scoped reads (
GET /api/v1/nodes/storage) are denied for scoped keys too. - Keys without a scope keep full cross-namespace access.
Transport
The API is served over HTTPS by default (Caddy sidecar, cluster CA at/etc/k7/tls/ca.crt unless --api-hostname or an operator certificate was used at install). Operators can additionally restrict the NodePort to known client CIDRs with k7 install --api-allow-cidr (Cilium only). Neither is rate limiting. See Security model.
docker: true
Turning on Docker changes what bounds the guest: on k7d / k7d-fc the sandbox container stays unprivileged but docker.sock is guest root; on kfd / kql a privileged docker-vehicle container runs in the same Kata VM. The isolation boundary is the VM either way — use k7d-fc when tenants do not trust each other. See Docker in a sandbox.
Image registry allowlist (SSRF)
When the control plane inspects a sandboximage (entrypoint/cmd resolution), the registry host is resolved before any fetch. Hosts must be on the allowlist (registry-1.docker.io, ghcr.io, quay.io, public.ecr.aws, plus K7_REGISTRY_ALLOWLIST) and every A/AAAA must be a public unicast address. Loopback, private, link-local, metadata, and reserved ranges are rejected. Redirects are not followed.