Passa al contenuto principale

Control Plane (MON)

flo-control è il demone di flotta che detiene tutte le chiavi SSH di flotta e risponde all'API autenticata del control-plane. Gira su Mon, fuori banda rispetto alla flotta tenant, in un bundle di deploy dedicato (cli/deploy/control/) separato dalle immagini tenant. I tenant vincolati al lease dipendono da MON per il rinnovo dell'autorità di scrittura, così un'interruzione di MON può isolarli (fencing) — per questo il demone è volutamente piccolo e indurito.

Dove gira​

  • Bundle dedicato su Mon, montato da ~/.flo-control, creato da flo control bootstrap. Contiene solo profili VPS sicuri e una chiave control-plane per target — mai il master.key dell'operatore, l'intero config store, i segreti tenant o le credenziali Cloudflare.
  • Riutilizza il Traefik esistente dell'host (flo-seo-traefik) su una rete Docker condivisa; non pubblica alcuna porta host. Ogni byte arriva tramite Traefik.
  • Indurimento container: rootfs in sola lettura, tutte le capability Linux rimosse, no-new-privileges, utente non-root corrispondente al proprietario del bundle, limiti CPU/memoria e restart: unless-stopped.

Due porte, un demone​

PortaHostPercorso
Browser (gate Google)control.useflo.netTraefik → oauth2-proxy → demone :8787
CLI / macchinaapi-control.useflo.netTraefik → demone (token di sessione opaco validato in-process)
  • La porta browser inoltra l'ID token OIDC Google al demone; il demone lo scambia in POST /auth/exchange con un token di sessione Flo opaco. I token Google sono accettati solo su quella rotta.
  • stripSpoofableHeaders elimina gli header in ingresso X-Forwarded-* / X-Auth-* così un bypass non può falsificare l'identità; le rotte macchina (lease) hanno propri rate limit.
  • Entrambe le porte applicano l'allow-list operatori (whitelist.txt): firma Google + aud + email_verified + email in allow-list. Il sign-in limitato al workspace è una modalità alternativa (FLO_CONTROL_HOSTED_DOMAIN).

Sessioni e token​

  • Sessioni operatore: emesse da flo control login (sign-in Google nel browser) e conservate in locale a 0600. Scadono 7 giorni dopo il login, non slittano con l'attività, e il logout le revoca immediatamente.
  • Token in sola lettura: flo control login --read-only --token-file <path> emette una sessione solo-GET per agent osservatori (bot chat, dashboard da parete). Il demone rifiuta ogni rotta non-GET con 403 auth.read_only prima di leggere un body; l'unico POST consentito è /auth/logout. I token in sola lettura hanno TTL 30 giorni con rinnovo scorrevole: mentre sono usati, la scadenza avanza (una scrittura al massimo una volta per emivita), così un osservatore attivo non scade mai, mentre un token inutilizzato muore comunque.
  • Endpoint di lettura utili: GET /fleet/data.json (snapshot dashboard, ~4 KB gzip), GET /api/history (sparkline 24 h), GET /vps/:name/stats, GET /audit.

Dashboard e chiosco /fleet​

flo control dashboard start # dashboard locale full-write su 127.0.0.1
flo control dashboard start --port 4173 --poll-seconds 10

La dashboard locale fa da proxy al demone col token CLI detenuto lato server.

https://control.useflo.net/fleet è una vista chiosco in sola lettura servita dal demone stesso dietro il gate Google: nessuna azione di scrittura nel DOM, ricaricamento automatico quando i dati diventano stantii o la sessione scade, tema scuro a token, manifest PWA, cookie oauth2-proxy a 30 giorni per tablet da parete. Mostra:

  • sparkline memoria/disco per host da un ring buffer SQLite (metrics.db, campionato alla costruzione dello snapshot con throttle a 60 s);
  • postura DR per tenant (primario/standby, stato lease, incidente attivo/ultimo) da dr.db;
  • freschezza backup con probe R2 in cache con TTL;
  • deriva versioni e stati "non in esecuzione" corretti, con filtro persistente dei cloni di test.

Avvisi Telegram​

flo control alerts mon # mostra canale + stato
cat token.txt | flo control alerts mon --provision --token-stdin --chat <id> -y
flo control alerts mon --test --token-stdin < token.txt
flo control alerts mon --disable -y
  • Il demone valuta le soglie di produzione ogni minuto: host irraggiungibile, disco/memoria, certificati, container di produzione fermi, istanza non in esecuzione, web e backup — con chiavi issue stabili per dedup/cooldown.
  • Un messaggio batch per ciclo (CRITICAL/WARNING), una riga di recovery quando un problema rientra, e un cooldown per issue (predefinito 6 h) così un problema attivo non è rinotificato prima della scadenza.
  • Supportati chat multiple e bot multipli (--chat CSV o --targets-stdin con array JSON); basta un target riuscito. Lo stato vive in metrics.db, così i riavvii non lo perdono. I cloni di test non sono mai avvisati.
  • Il token bot è letto da stdin (mai da argv) e scritto nel .env di MON.

Lettore backup​

flo control backup-reader mon # mostra lo stato corrente
flo control backup-reader mon --provision -y

Emette un token R2 in sola lettura limitato al bucket, lo memorizza e inietta FLO_S3_* nel bundle MON, poi ricrea il demone. La dashboard interroga il bucket backup di ogni tenant una volta ogni 30 minuti e mostra l'età reale del backup. Senza queste credenziali la scheda Backup resta semplicemente vuota — mai un falso avviso. Ri-provisiona dopo aver creato un nuovo bucket così il lettore può vederlo (la forma remota backup schedule gira di per sé senza chiavi R2 di scrittura).

Audit (WORM su R2)​

Ogni scrittura contro il control plane produce una riga di audit concatenata via hash e redatta; la catena è replicata fuori dal box su R2 sotto una regola Bucket Lock (l'equivalente object-lock di R2), così la cronologia non può essere riscritta.

flo control audit --provision -y # crea bucket + token dedicato + lock
flo control audit --apply-lock -y # aggiunge la regola lock a un target esistente
flo control audit # verifica la configurazione

Predefiniti: bucket flo-control-audit, prefisso audit, retention 3650 giorni (--retention-days). Le credenziali S3 dedicate sono cifrate nel flo config locale dell'operatore e sono le uniche credenziali audit inviate a Mon. Gli operatori possono leggere i propri record con GET /audit.

Registry comandi remoti​

flo --vps <name> … non ripiega mai su SSH: sono accettate solo le forme registrate, ed eseguite dal demone.

  • Lettura: vps stats|test, vps journal status, instance ls|test list, instance info|inspect|health|stats <id>, logs app|errors|db|days <id>, logs day <id> <date>, deploy history <id>, config env show/flags show, config ssl status, instance db tables <id>, backup status [<id>], watchdog status, failover status, secrets status <id>.
  • Scrittura: deploy <id> --branch <branch> -y, deploy rollback <id> -y, instance start|stop|restart <id>, config env set <id> KEY=VALUE [--restart] -y, backup schedule <id> --bucket … --cron … --run-now -y, vps journal vacuum -y (policy fissa 500M/30 giorni), più i comandi finiti del ciclo di vita DR.
  • Ogni account in allow-list riceve lo stesso pieno accesso in scrittura a queste azioni. Il registry non espone shell arbitraria, SQL, trasferimento file o percorsi locali; usa --local per un'operazione locale intenzionale. I job DR lunghi hanno un timeout di due ore.

Comandi​

ComandoDescrizioneOpzioni principali
flo control loginSign-in Google da browser; memorizza il token di sessione--api, --app, --read-only, --token-file <path>
flo control logout / whoamiRevoca la sessione locale / mostra identità e sessioni live
flo control dashboard startDashboard locale full-write in proxy verso il demone--port, --poll-seconds, --api, --no-open
flo control snapshotSnapshot read-model di flotta non-segreto (JSON)--out <file>, --check-backups
flo control buildRende l'HTML statico della dashboard in sola lettura da uno snapshot--out, --from <file>, --check-backups
flo control serverEsegue il demone (usato dentro il container)--port, --bind
flo control bootstrap [intermediary]Crea il bundle sicuro + chiavi SSH ristrette per target--fleet <names> (obbligatorio), --source, --bundle-home, --dry-run, -y
flo control deploy [vps]Deploy a freddo di MON con recovery durevole--recover <id>, --repair-worker, --local-build, --no-build, --force-env, --force-whitelist, --enable/--disable-dr-automation, --maintenance, --shared-network, domini, -y
flo control doctor [vps]Triage in sola lettura dell'host di controllo (disco, journal, transazioni, demone)
flo control auditConfigura/verifica il target audit WORM--provision, --apply-lock, --retention-days, --replace, --bucket, --prefix, -y
flo control alerts [vps]Mostra/configura gli avvisi Telegram--provision, --disable, --test, --chat, --token-stdin, --targets-stdin, --dir, -y
flo control backup-reader [vps]Mostra/provisiona le credenziali backup R2 in sola lettura--provision, --dry-run, --dir, -y
flo control agent-config <tenant>Prepara un config agent privato; solo l'hash del token raggiunge MON--node (obbligatorio), --output (obbligatorio), --init, --rotate-token, -y
flo control command status <job> / attach <job>Ispeziona o riaggancia un job di comando remoto ancora in esecuzione su MON

Rollout e recovery​

flo vps test mon
flo control audit --provision -y
flo control bootstrap mon --fleet production,production-uk -y
flo control deploy mon --enable-dr-automation -y
  • Gate di deploy: ogni deploy richiede tutti i tenant MON completamente manuali, nessun incidente attivo e nessun lease firmato pendente; il gate rifiuta stato ignoto o non sicuro prima di fermare il demone. --maintenance ammette una coppia armata live con stato DR inattivo; --disable-dr-automation non sostituisce un disarmo completo.
  • --local-build compila sulla postazione per la piattaforma target e trasferisce l'immagine verificata; --no-build richiede la stessa versione engine.
  • Deploy interrotto: conserva l'ID operazione stampato ed esegui flo control deploy mon --recover <operation-id> -y. La recovery verifica il nuovo container o ripristina lo snapshot posseduto; non usare mai docker compose down né eliminare i file operazione.
  • Cambio IP di MON: flo control bootstrap mon --fleet … --source <new-ip> -y poi flo control deploy mon --no-build -y (vedi README del bundle in cli/deploy/control/README.md).
  • Prima di ogni mutazione VPS remota esegui il preflight in sola lettura: flo --vps <name> vps stats.

Vedi Failover e Disaster Recovery per il ciclo di vita DR coordinato da MON, e Backup per i dati di freschezza letti dalla dashboard.