Skip to content

Security: macel94/belacca-infrastructure

Security

docs/SECURITY.md

Host and cluster security contract

This document records the security boundaries for native k3s production. The parent migration plan defines hardening gates; this file does not authorize broad firewall or credential changes. The executable policy and recovery/audit procedure are in HOST-HARDENING-RECOVERY.md and ansible/inventories/production/hardening-policy.yml.

Current exposure boundary

The three-node native k3s control plane and embedded-etcd quorum are complete. Native Traefik is Flux-managed on .73, .41, and .42 and owns public application traffic. Cloudflare DNS-only records include all three native application addresses and use direct round-robin; this is not a health-aware load balancer. The native API is reached directly at https://169.58.143.41:6443, with k3s-api.belacca.com resolving to .41 and .42.

Native certificates use cert-manager Cloudflare DNS-01 and namespace-local Secrets. Do not make API, etcd, kubelet, overlay, or Longhorn ports public to solve an ingress or failover problem.

Host access

Native-node access is key-based and must use the dedicated belacca-infra_ed25519 key. After access is verified on a host:

  • set PermitRootLogin prohibit-password;
  • set PasswordAuthentication no and KbdInteractiveAuthentication no;
  • verify the effective policy with sshd -T, not only the drop-in file;
  • restrict SSH with provider firewall rules or a documented administrator CIDR;
  • retain a tested provider-console and native recovery path before tightening access; and
  • do not infer that native production authorizes disruptive SSH or firewall changes outside a reviewed maintenance window.

The completion gate is python3 scripts/validate_hardening.py --require-runtime-evidence; it must remain failing while provider, console, or per-host recovery evidence is absent. See the recovery runbook for the emergency key rotation and sanitized audit procedure.

The two temporary VPS passwords supplied for onboarding were used only for key installation and must be rotated or invalidated after bootstrap hardening. Never place credentials in Ansible inventory, this repository, shell history, or repository history.

Network boundaries

  • TCP 6443 is limited to the three cluster nodes and explicitly approved operator access.
  • TCP 2379-2380 is limited to native server peers for embedded etcd.
  • TCP 10250 is limited to cluster peers for kubelet access.
  • UDP 51820 is limited to cluster peers for the Flannel WireGuard-native overlay.
  • Longhorn frontend and replica/control ports are limited to storage-node addresses.
  • TCP 80/443 are public only on the intentionally exposed native Traefik edges .73, .41, and .42.

The absence of a health-aware load balancer is a hardening limitation, not permission to open cluster-internal services or bypass the provider firewall.

Secret and ownership boundaries

  • Flux reconciles Kubernetes resources from belacca-gitops.
  • SOPS-encrypted Secret manifests and the age public recipient may be stored in the public GitOps repository; only ciphertext belongs there.
  • The age private key is installed only as the Flux decryption Secret in flux-system and in independent offline backups. It must not be committed, copied into this infrastructure repository, or printed in CI/operator logs.
  • This infrastructure repository contains host configuration and network posture, not passwords, kubeconfigs, join tokens, private keys, OAuth credentials, or application data.

Rotate application credentials without committing plaintext values. Preserve Git history, native Longhorn state, and the post-cutover external-backup/failure-drill plan.

There aren't any published security advisories