Skip to content

Latest commit

 

History

History

deploy

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

Deploy — sync server (Fase 2)

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.

What is running

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

Hardening

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.

Segreti

Due Secret nel namespace, creati a mano (non sono nel repo):

  • pm-db — password e url di Postgres
  • pm-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_setup

Aggiornare il server

L'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 riapplica

Un registry privato toglierebbe questo giro; vale la pena quando i deploy diventano frequenti.

Accesso

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.1

Lo 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 -- --ignored

Dominio

sync.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.