Single-node k3s on OpenStack. Nothing here is production-grade yet: one Postgres instance on local-path storage, one server replica, no backups. It is a place to run the cloud while we build it.
| Pezzo | Dove |
|---|---|
| Cluster | k3s su apps-k3s-1, IP pubblico 185.155.178.113 |
| Namespace | passmanager (più passmanager-dev, ingress, monitoring) |
| Database | StatefulSet postgres, PVC 10 GiB local-path |
| Server | Deployment pm-server, immagine side-loaded in containerd |
| Endpoint | https://sync.besana.dev |
| TLS | cert-manager + Let's Encrypt, rinnovo automatico |
Applicato con deploy/k8s/hardening/ (i file sono in git perché la
configurazione del cluster dev'essere leggibile e ripetibile):
| Misura | Effetto |
|---|---|
secrets-encryption |
I secret sono cifrati in etcd, non solo base64 |
| Audit log | Chi ha fatto cosa sul control plane, 30 giorni, ruotato |
Pod Security restricted |
Niente container privilegiati o root (verificato: un pod privilegiato viene rifiutato) |
| NetworkPolicy default-deny | Solo il server parla con Postgres; il database non apre connessioni verso l'esterno |
protect-kernel-defaults |
k3s si rifiuta di partire con un kernel mal configurato |
| unattended-upgrades | Patch di sicurezza automatiche |
| fail2ban + sshd | Niente password, niente root, 3 tentativi |
Porte esposte a Internet: solo 22 (SSH), 80 e 443. La 6443 dell'API Kubernetes resta chiusa.
Due Secret nel namespace, creati a mano (non sono nel repo):
pm-db—passwordeurldi Postgrespm-opaque—setup, la chiave OPRF di OPAQUE
pm-opaque non è recuperabile: perderla significa che nessun
account riesce più a fare login. Va salvata dove si salvano le chiavi
private. Se ne genera una nuova con:
cargo run -q -p pm-server --example gen_setupL'immagine non passa da un registry: si costruisce e si inietta nel containerd del nodo.
podman build -f crates/pm-server/Dockerfile -t localhost/pm-server:VERSIONE .
podman save --format oci-archive -o /tmp/pm-server.tar localhost/pm-server:VERSIONE
scp -i ~/.ssh/apps-k3s.pem /tmp/pm-server.tar ubuntu@185.155.178.113:/tmp/
ssh -i ~/.ssh/apps-k3s.pem ubuntu@185.155.178.113 \
'sudo k3s ctr images import /tmp/pm-server.tar && rm /tmp/pm-server.tar'
# poi aggiorna il tag in deploy/k8s/pm-server.yaml e riapplicaUn registry privato toglierebbe questo giro; vale la pena quando i deploy diventano frequenti.
La porta 6443 (API Kubernetes) è deliberatamente chiusa verso
Internet: esporla è il modo più comune di farsi bucare un cluster. Per
kubectl da fuori si passa da un tunnel SSH:
ssh -f -N -i ~/.ssh/apps-k3s.pem -L 6443:127.0.0.1:6443 ubuntu@185.155.178.113
KUBECONFIG=~/.kube/apps-k3s.yaml kubectl get pods -A # server: 127.0.0.1Lo stesso vale per provare il server prima che esista il dominio:
CIP=$(ssh ... "sudo k3s kubectl -n passmanager get svc pm-server -o jsonpath='{.spec.clusterIP}'")
ssh -f -N -i ~/.ssh/apps-k3s.pem -L 8100:$CIP:80 ubuntu@185.155.178.113
PM_E2E=http://127.0.0.1:8100 cargo test -p pm-server --test e2e -- --ignoredsync.besana.dev → record A verso 185.155.178.113, senza proxy
(nuvoletta grigia): la verifica HTTP-01 deve poter raggiungere il nodo.
Il certificato lo emette e lo rinnova cert-manager; non c'è niente da
fare a mano.
Per un altro sottodominio: record A verso lo stesso IP e un secondo
Ingress con quel nome. Se i sottodomini diventano molti conviene passare
a un certificato jolly *.besana.dev, che richiede un solver DNS-01 con
un token API Cloudflare limitato alla zona.