Security Model#
This page describes the security measures in place across the cluster.
Authentication and access control#
SSH access#
All nodes use key-based SSH authentication only (password auth is disabled).
A dedicated
ansibleuser is created on each node with passwordless sudo.The SSH keypair is stored in
pub_keys/ansible_rsa.pub(public key only in Git).The private key lives on the operator’s workstation and is mounted into the devcontainer via a Docker volume.
Kubernetes RBAC#
The control plane uses the default K3s RBAC configuration.
Headlamp uses ServiceAccount token authentication with
cluster-adminprivileges via the Helm chart’s built-in ClusterRoleBinding.ArgoCD has the
kubernetesAppProject withsourceReposrestricted to the project’s GitHub repo and trusted Helm chart registries.
Service authentication#
Authentication uses a three-layer model: Cloudflare Access (edge gate), Dex OIDC or oauth2-proxy (ingress auth), and per-service RBAC. See Authentication Architecture for the full architecture with diagrams.
Service |
Layer 1 (Cloudflare) |
Layer 2 (Ingress) |
Layer 3 (App RBAC) |
|---|---|---|---|
ArgoCD |
Cloudflare Access |
Dex (native OIDC) |
email → admin / readonly |
argocd-monitor |
Cloudflare Access |
Dex (sidecar) |
Inherits ArgoCD RBAC |
Grafana |
Cloudflare Access |
Dex ( |
email → Admin / Viewer |
Open WebUI |
Cloudflare Access |
Dex (native OIDC) |
email → admin / user |
Headlamp |
Cloudflare Access |
oauth2-proxy + token |
— |
Supabase Studio |
Cloudflare Access |
oauth2-proxy |
Dashboard password |
Echo |
Cloudflare Access |
None |
None (public test) |
RKLlama |
— |
None |
Internal API (fronted by Open WebUI) |
See Set Up OAuth Authentication for setup instructions.
Secrets management#
Sealed Secrets#
Sensitive values (API tokens, tunnel credentials, OAuth client secrets) are
encrypted using Sealed Secrets and
stored in Git as SealedSecret resources. Only the cluster’s sealed-secrets
controller can decrypt them.
Current SealedSecrets:
kubernetes-services/additions/cloudflared/tunnel-secret.yaml— Cloudflare tunnel tokenkubernetes-services/additions/cert-manager/templates/cloudflare-api-token-secret.yaml— DNS API tokenkubernetes-services/additions/argocd/argocd-dex-secret.yaml— GitHub connector + all 5 Dex client secretskubernetes-services/additions/grafana/grafana-oauth-secret.yaml— Grafana’s Dex client secretkubernetes-services/additions/open-webui/open-webui-oauth-secret.yaml— Open WebUI’s Dex client secretkubernetes-services/additions/argocd-monitor/argocd-monitor-oauth-secret.yaml— argocd-monitor Dex client + cookie secretkubernetes-services/additions/oauth2-proxy/oauth2-proxy-secret.yaml— GitHub OAuth App credentials + cookie secret
Admin access#
Admin email addresses are configured in admin_emails in
kubernetes-services/values.yaml. All other authenticated users
(those who pass Cloudflare Access and Dex login) receive viewer/
read-only roles by default. See Authentication Architecture for details.
Devcontainer credential isolation#
The devcontainer is hardened to limit the blast radius of AI-assisted development (Claude Code) and prompt injection attacks:
Claude runs in a bwrap sandbox that strips
SSH_AUTH_SOCKand masks/root/.ssheven though VS Code forwards the host SSH agent into the devcontainer for use in your own terminals. The sandbox uses--clearenvplus a strict-under-/roottmpfs overlay with an explicit allowlist of dotfiles bound back;.sshis deliberately not on that list, so host GitHub keys are never reachable from a prompt-injection vector.Git credential helpers are blanked on container start. VS Code’s auto-injected OAuth helper is overridden, so remote pushes require an explicit fine-grained PAT via
gh auth login.GitHub CLI credentials are per-repo. A dedicated Docker volume (
gh-auth-<project>) stores the PAT, scoped to only the repositories needed.Claude Code is devcontainer-only. A
UserPromptSubmithook blocks execution outside the container.Network escape vectors require confirmation.
ssh,scp,rsync, and similar commands in Claude Code prompt for human approval.
See Using Claude Code for setup instructions.
Pod Security Standards#
Security contexts are applied at the workload level. See Security Hardening for a detailed breakdown of which workloads run as non-root, which have read-only root filesystems, and which require privileged access.
Container image pinning#
All container images are pinned to specific version tags. Renovate bot monitors for updates and raises PRs automatically. See Security Hardening for the full policy.
Network security#
Control plane taint#
In multi-node clusters, the control plane node has a NoSchedule taint. No regular
workloads run on it — only K3s system components (CoreDNS, metrics-server, etcd,
kube-proxy). For single-node clusters the taint is skipped so all workloads can schedule.
Cloudflare protection#
Services exposed via the Cloudflare tunnel benefit from:
WAF — Web Application Firewall with managed rulesets
DDoS protection — automatic layer 3/4/7 mitigation
Bot management — challenge suspicious traffic
Rate limiting — configurable per-hostname rules
No inbound firewall ports#
The Cloudflare tunnel uses an outbound-only connection from the cloudflared pod.
No inbound ports need to be opened on your router for public-facing services.
Cloudflare Access#
All services are routed through the Cloudflare tunnel and protected by a
Cloudflare Access email-gate policy. The only exception is supabase-api,
which uses a bypass policy (authenticated by x-brain-key instead).
NetworkPolicies#
NetworkPolicies are not deployed by default. See Network Policies for the rationale and guidance on implementing them if needed.
TLS everywhere#
All ingress endpoints use TLS certificates from Let’s Encrypt (production CA):
Certificates are automatically issued and renewed by cert-manager
DNS-01 validation via Cloudflare API (works for LAN-only services too)
HSTS headers are set by ingress-nginx by default
ArgoCD uses TLS passthrough (handles its own certificate)
Recommendations#
Rotate SealedSecrets periodically — re-seal with fresh values.
Back up the sealed-secrets key — without it, a cluster rebuild requires re-creating all secrets.
Keep nodes updated —
unattended-upgradeshandles security patches automatically.Monitor for alerts — Prometheus Alertmanager captures security-relevant events.
Review the production checklist — see Production Readiness Checklist.