docs: explain per-installation DWH access
This commit is contained in:
@@ -277,6 +277,8 @@ le fasi e conferma ai gate: il modello proporrà lo schema-linking (tabelle/colo
|
||||
|
||||
## Dove trovare i dettagli tecnici
|
||||
|
||||
Per l'accesso DWH REST, la chiave è per installazione e vale solo per `rest_api`: il server PSD rimane `postgres_direct` e `ssh_tunnel` non usa questa chiave. Vedere [guida server DWH](install/dwh-auth-server.md), [enrollment client](install/dwh-auth-client-enrollment.md), [TLS](install/dwh-auth-tls.md) e [runbook PSD](operations/psd-dwh-auth-rollout.md).
|
||||
|
||||
- Contratto CLI: `docs/contracts/workspace-preprocessing-cli.md`
|
||||
- Contratto `.tht-dwh`: `docs/contracts/tht-dwh.md`
|
||||
- Evidence v3: `docs/contracts/workspace-evidence-v3.md`
|
||||
|
||||
@@ -14,6 +14,8 @@ Per installare l'applicazione in Docker nei quattro contesti operativi, usando i
|
||||
`compose.yaml`, l'overlay locale/server e il bundle di secret montato:
|
||||
[Installazione Docker nei quattro contesti](installazione-docker-4-contesti.md).
|
||||
|
||||
Per il DWH REST con una chiave revocabile per installazione: [guida server](install/dwh-auth-server.md), [enrollment client](install/dwh-auth-client-enrollment.md), [TLS](install/dwh-auth-tls.md) e [runbook PSD](operations/psd-dwh-auth-rollout.md). Il componente resta separato dallo stack Compose ThothII.
|
||||
|
||||
## Considerazioni Generali
|
||||
|
||||
Note operative e di configurazione che non sono specifiche del dominio ThothII ma riguardano l'ambiente di sviluppo condiviso con altri progetti — ad esempio come Pi (il coding agent) risolve i modelli a livello built-in, utente e progetto. Parte da qui: [Configurazione dei modelli in Pi](general/pi-configuration.md).
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Enrollment client per DWH REST
|
||||
|
||||
La credenziale `dwh-auth` appartiene a una installazione ThothII, non a una persona. Serve solo se
|
||||
il trasporto è `rest_api`; `postgres_direct` e `ssh_tunnel` non la usano.
|
||||
|
||||
| Trasporto | Chiave `dwh-auth` | Materiale locale |
|
||||
| --- | --- | --- |
|
||||
| `rest_api` | Sì, una per installazione. | URL HTTPS, `API_KEY_FILE`, eventuale `TLS_CA_FILE`. |
|
||||
| `postgres_direct` | No. | Credenziali PostgreSQL e TLS PostgreSQL. |
|
||||
| `ssh_tunnel` | No. | Credenziali PostgreSQL e materiali SSH; è diagnostico-only nel runtime corrente. |
|
||||
|
||||
Il ThothII server PSD resta `postgres_direct` read-only. Il Mac PSD e le installazioni remote
|
||||
usano `rest_api`; non introdurre un tunnel SSH per aggirare REST.
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
Ricevere chiave e CA, se necessaria, attraverso canali protetti separati. Confermare fuori banda il
|
||||
fingerprint TLS prima dell'uso: [guida TLS](dwh-auth-tls.md). Conservare la chiave nel vault o in
|
||||
un file protetto, mai Git, `.env` con il valore, argv, ambiente, log o evidenze. Annotare solo ID
|
||||
pubblico.
|
||||
|
||||
## Percorso GUI: vault dell'installazione
|
||||
|
||||
1. In **Workspace management**, eseguire **Update workspace repository** se necessario e
|
||||
selezionare il workspace.
|
||||
2. Scegliere `rest_api`, controllare URL/trust locali e usare **Validate workspace**.
|
||||
3. Inserire la chiave nel campo write-only **Data warehouse API key**, poi **Save runtime secrets**.
|
||||
La GUI la conserva nel vault cifrato `workspace-secrets`, non la rileggere né la restituisce.
|
||||
4. Eseguire **Test connections**. Il controllo innocuo è `/rpc/ping`: atteso 2xx e
|
||||
database/schema dichiarati.
|
||||
5. Comunicare al server solo ID pubblico, timestamp e risultato. **Forget** rimuove il valore e va
|
||||
usato soltanto dopo conferma di sostituzione o revoca.
|
||||
|
||||
## Percorso headless: binding reale
|
||||
|
||||
`API_KEY_FILE` significa che il valore è nel file, non nella variabile. Nel file PSD non tracciato
|
||||
`workspace-bindings.env` i binding effettivi sono:
|
||||
|
||||
```dotenv
|
||||
THT_WS_PSD_CLINICAL_DWH_TRANSPORT=rest_api
|
||||
THT_WS_PSD_CLINICAL_DWH_BASE_URL=https://supabase-aritmolab.policlinicosandonato.it/dwh/
|
||||
THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE=/run/secrets/psd-clinical-dwh-api-key
|
||||
THT_WS_PSD_CLINICAL_DWH_TLS_CA_FILE=/run/secrets/psd-clinical-dwh-ca.pem
|
||||
```
|
||||
|
||||
Nel file `operator.env` non tracciato, ogni suffisso `_SOURCE` indica solo il percorso assoluto del
|
||||
file protetto di origine. Il comando genera un override non tracciato che monta quei file nel
|
||||
`core`; non installa né avvia `dwh-auth` con Compose:
|
||||
|
||||
```bash
|
||||
bash scripts/generate-connector-secrets-override.sh \
|
||||
--bindings-env /absolute/protected/workspace-bindings.env \
|
||||
--operator-env /absolute/protected/operator.env \
|
||||
--output /absolute/protected/connector-secrets.override.yaml \
|
||||
--service core --role dwh
|
||||
```
|
||||
|
||||
La chiave sorgente è un file regolare `0600` per il solo account autorizzato. Per altri workspace,
|
||||
sostituire `PSD_CLINICAL` con ID immutabile maiuscolo (trattini in underscore). Vedere anche il
|
||||
[protocollo diagnostico](../workspace-diagnostic-protocol.md).
|
||||
|
||||
## Ping, rotazione e revoca
|
||||
|
||||
Usare solo **Test connections** su `/rpc/ping`: successo è 2xx con TLS verificato; il server
|
||||
conferma l'ID con `key status`. Durante rotazione, ricevere nuova generazione, aggiornare vault o
|
||||
file `API_KEY_FILE`, ripetere ping, attendere osservazione e far revocare la precedente. Dopo la
|
||||
revoca: nuova positiva, precedente `401`.
|
||||
|
||||
`401` non distingue chiave assente, scaduta o revocata. `503` è un guasto fail-closed di servizio,
|
||||
socket o registro: non usare connessione diretta e non ridurre TLS. Non riattivare una chiave
|
||||
revocata. Il percorso PSD è nel [runbook](../operations/psd-dwh-auth-rollout.md).
|
||||
@@ -0,0 +1,136 @@
|
||||
# `dwh-auth`: guida server
|
||||
|
||||
`dwh-auth` autentica la route REST `/dwh/` con una chiave per installazione. È un componente Linux
|
||||
opzionale e server-side: usa `systemd`, non `tht` né Docker Compose, non legge risultati clinici e
|
||||
non si collega a PostgreSQL. La chiave serve solo a `rest_api`; `postgres_direct` e `ssh_tunnel`
|
||||
non la usano.
|
||||
|
||||
## Prerequisiti e confini
|
||||
|
||||
- Usare un checkout revisionato, Docker per la build e un operatore autorizzato sul server DWH.
|
||||
- Una chiave identifica un'installazione, non una persona. L'`installation-id` è unico, non
|
||||
personale e senza dati clinici.
|
||||
- Chiavi, digest, file di consegna e backup restano in file protetti: mai Git, argv, variabili
|
||||
d'ambiente, log, JSON pubblico o evidenze.
|
||||
- Preparare backup e rollback prima di Nginx. Installare il servizio non autorizza una modifica
|
||||
della route pubblica.
|
||||
|
||||
## Percorsi, owner e mode
|
||||
|
||||
| Oggetto | Percorso | Owner e mode |
|
||||
| --- | --- | --- |
|
||||
| Binario | `/usr/local/sbin/dwh-auth` | `root:root`, `0755` |
|
||||
| Unit | `/etc/systemd/system/dwh-auth.service` | `root:root`, `0644` |
|
||||
| Tmpfiles | `/usr/lib/tmpfiles.d/dwh-auth.conf` | `root:root`, `0644` |
|
||||
| Registro, `active`, `revoked` | `/var/lib/dwh-auth/` | `root:dwh-auth`, `2750` |
|
||||
| Lock | `/var/lib/dwh-auth/.writer.lock` | `root:dwh-auth`, `0640` |
|
||||
| Record | `/var/lib/dwh-auth/{active,revoked}/<public-key-id>.json` | `root:dwh-auth`, `0640` |
|
||||
| Socket runtime | `/run/dwh-auth/verify.sock` | `dwh-auth:www-data`, `0660` |
|
||||
| Consegne e backup | `/root/dwh-auth-provision/` | directory `root:root` `0700`, file `0600` |
|
||||
|
||||
Il record conserva un digest interno (`secret_sha256`) e metadati, mai la chiave in chiaro. Non
|
||||
leggere, stampare, calcolare o mettere quel digest in una prova operativa.
|
||||
|
||||
## Build, installazione e avvio
|
||||
|
||||
Costruire dal commit congelato e registrare solo checksum del binario e SHA sorgente:
|
||||
|
||||
```bash
|
||||
cd /srv/thothii/app
|
||||
bash scripts/build-dwh-auth.sh --output /tmp/dwh-auth-release
|
||||
sha256sum /tmp/dwh-auth-release/dwh-auth-linux-amd64
|
||||
```
|
||||
|
||||
Scegliere l'architettura corretta. Il template PSD usa il gruppo Nginx `www-data`; confermarlo
|
||||
prima dell'installazione su un host diverso.
|
||||
|
||||
```bash
|
||||
sudo groupadd --system dwh-auth
|
||||
sudo useradd --system --no-create-home --shell /usr/sbin/nologin --gid dwh-auth dwh-auth
|
||||
sudo install -o root -g root -m 0755 /tmp/dwh-auth-release/dwh-auth-linux-amd64 /usr/local/sbin/dwh-auth
|
||||
sudo install -o root -g root -m 0644 deploy/dwh-auth/dwh-auth.service /etc/systemd/system/dwh-auth.service
|
||||
sudo install -o root -g root -m 0644 deploy/dwh-auth/dwh-auth.tmpfiles.conf /usr/lib/tmpfiles.d/dwh-auth.conf
|
||||
sudo install -d -o root -g root -m 0700 /root/dwh-auth-provision
|
||||
sudo systemd-tmpfiles --create /usr/lib/tmpfiles.d/dwh-auth.conf
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth check
|
||||
sudo systemd-analyze verify /etc/systemd/system/dwh-auth.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now dwh-auth
|
||||
sudo systemctl status dwh-auth --no-pager
|
||||
```
|
||||
|
||||
Controllare i mode con `stat`. Il servizio apre il registro in sola lettura e crea solo il socket.
|
||||
Non creare JSON, lock o socket a mano: oggetti insicuri devono fallire chiusi.
|
||||
|
||||
## Check, elenco e stato
|
||||
|
||||
Usare sempre un root assoluto. Questi comandi espongono solo ID pubblici, stato, date e scadenza:
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth check
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key list --json
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key status --key-id <public-key-id> --json
|
||||
```
|
||||
|
||||
Un errore di integrità, permessi, symlink o JSON malformato richiede ripristino da backup protetto,
|
||||
non una correzione manuale del record.
|
||||
|
||||
## Creazione, consegna, scadenza e revoca
|
||||
|
||||
Il comando crea la chiave una volta in un nuovo file assoluto `0600`; stdout contiene solo ID
|
||||
pubblico, installazione e percorso. Il file di output non deve esistere.
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key create \
|
||||
--installation-id <installation-id> \
|
||||
--description <metadato-non-segreto> \
|
||||
--output /root/dwh-auth-provision/<installation-id>-<generation>.key
|
||||
```
|
||||
|
||||
Aggiungere `--expires-at <RFC3339-UTC>` solo se la policy impone una scadenza; il default è nessuna
|
||||
scadenza. Consegnare il file solo con vault aziendale, secret manager, MDM o trasferimento
|
||||
autenticato ristretto. Mai email, chat, ticket, `cat` o copia-incolla. Il client conferma ID
|
||||
pubblico e ping, poi il materiale temporaneo viene rimosso secondo policy.
|
||||
|
||||
L'import legacy è temporaneo PSD: il file sorgente è già `root:root` `0600` e non viene mai letto o
|
||||
stampato dall'operatore.
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key import \
|
||||
--legacy-raw --installation-id legacy-shared \
|
||||
--from-file /root/dwh-auth-provision/legacy-shared.key
|
||||
```
|
||||
|
||||
Per rotare: creare seconda generazione, consegnarla, configurarla e provare `/rpc/ping`; confermare
|
||||
l'ID pubblico; attendere l'osservazione; poi revocare la precedente e provare nuova=successo,
|
||||
precedente=401.
|
||||
|
||||
```bash
|
||||
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key revoke \
|
||||
--key-id <previous-public-key-id> --reason <non-secret-reason>
|
||||
```
|
||||
|
||||
La revoca non è annullabile e un ID revocato non si ricrea.
|
||||
|
||||
## Backup, rollback e disinstallazione
|
||||
|
||||
Prima di mutare, creare un archivio cifrato e protetto del registro e copie protette delle sole
|
||||
configurazioni coinvolte. L'archivio contiene digest, quindi è riservato: evidenza ammessa solo
|
||||
percorso, owner, mode, timestamp e checksum dell'archivio. Il rollback dual-key ripristina la route
|
||||
e il servizio revisionati, esegue `nginx -t` e fa reload solo autorizzato; non ripristina chiavi
|
||||
revocate, PostgreSQL, sessioni legacy, indici Qdrant o cache Ollama.
|
||||
|
||||
La disinstallazione richiede autorizzazione esplicita, client REST migrati/revocati e rollback non
|
||||
più necessario. Solo allora disabilitare l'unità; conservare registro e backup fino alla retention
|
||||
approvata. Non inserire `dwh-auth` in Compose o in `tht start`/`tht stop`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Sintomo | Interpretazione e azione |
|
||||
| --- | --- |
|
||||
| `401` | Chiave assente, malformata, sconosciuta, scaduta, revocata o errata. Verificare trasporto, ID pubblico e consegna; non cercare dettagli nel messaggio. |
|
||||
| `503` | Servizio, socket o registro non disponibile/sicuro. Controllare `systemctl`, socket, mode e `check`; ripristinare il backup approvato. |
|
||||
| `check` fallisce | Integrità del registro non valida. Fermare le scritture, preservare stato e ripristinare; non editare JSON. |
|
||||
| TLS fallisce | CA o SAN non validi. Seguire [TLS](dwh-auth-tls.md), senza bypass. |
|
||||
|
||||
Per il rollout PSD con i due gate separati vedere il [runbook PSD](../operations/psd-dwh-auth-rollout.md).
|
||||
@@ -0,0 +1,49 @@
|
||||
# TLS per DWH REST
|
||||
|
||||
La chiave DWH è accettabile solo sopra TLS verificato. Un errore `401` o `503` non autorizza mai a
|
||||
ridurre la verifica del certificato.
|
||||
|
||||
## Stato PSD
|
||||
|
||||
L'origine REST PSD corrente usa il certificato self-issued/private di Nginx. Il SAN copre
|
||||
`supabase-aritmolab.policlinicosandonato.it`, l'origine `.it` approvata, e non copre un dominio
|
||||
`.com`. Non usare `.com` finché non è incluso esplicitamente nel SAN.
|
||||
|
||||
Chi non dispone già di trust equivalente approvato riceve la CA separatamente e configura
|
||||
`TLS_CA_FILE`. La CA non è una credenziale, ma la sua integrità è un confine di sicurezza: fuori da
|
||||
Git e non scrivibile da utenti non autorizzati.
|
||||
|
||||
## Fingerprint fuori banda
|
||||
|
||||
Calcolare localmente il fingerprint del file ricevuto:
|
||||
|
||||
```bash
|
||||
openssl x509 -noout -fingerprint -sha256 -in /absolute/protected/psd-dwh-ca.pem
|
||||
```
|
||||
|
||||
Confrontarlo con il responsabile autorizzato tramite un canale indipendente dalla consegna (vault
|
||||
aziendale o canale telefonico verificato). Nell'evidenza registrare solo conferma, approvatore e
|
||||
timestamp; mai corpo certificato, fingerprint completo o output grezzo.
|
||||
|
||||
## Binding e ping
|
||||
|
||||
Il binding headless PSD effettivo è:
|
||||
|
||||
```dotenv
|
||||
THT_WS_PSD_CLINICAL_DWH_TLS_CA_FILE=/run/secrets/psd-clinical-dwh-ca.pem
|
||||
```
|
||||
|
||||
Il file sorgente locale è collegato da file operatore non tracciato. Usare URL `.it`, poi
|
||||
**Test connections** su `/rpc/ping`. Non disabilitare TLS e non usare `curl -k`.
|
||||
|
||||
## Rinnovo coordinato
|
||||
|
||||
1. Preparare certificato e chain nuovi; verificare prima SAN `.it` e assenza di falsa copertura `.com`.
|
||||
2. Confermare fuori banda il nuovo fingerprint.
|
||||
3. Consegnare la CA/chain nuova ai client con `TLS_CA_FILE`, senza rimuovere ancora la precedente.
|
||||
4. Aggiornare vault/binding e verificare ping con TLS normale.
|
||||
5. Solo con gate Nginx approvato installare il certificato server e ripetere il ping.
|
||||
6. Ritirare il trust precedente dopo la finestra approvata.
|
||||
|
||||
Il rinnovo non modifica chiavi `dwh-auth`, record o ruoli PostgreSQL. TLS e rollback della route
|
||||
restano approvazioni e backup distinti.
|
||||
@@ -116,6 +116,10 @@ and fast-forward candidate checkout. It does not copy anything to the user's com
|
||||
|
||||
## Complete runtime secrets in Workspace management
|
||||
|
||||
### Chiavi DWH REST per installazione
|
||||
|
||||
Se il binding selezionato è `rest_api`, la chiave DWH è una credenziale per questa installazione e si salva nel vault tramite **Save runtime secrets** oppure in un file locale indicato da `API_KEY_FILE`. `postgres_direct` e `ssh_tunnel` non usano questa chiave. Per emissione, TLS, rotazione e verifica `/rpc/ping`, seguire [enrollment DWH REST](dwh-auth-client-enrollment.md).
|
||||
|
||||
Open Workspace management after the first successful repository update.
|
||||
|
||||
1. At the repository level, review the configured host, repository, branch, and current revision.
|
||||
|
||||
@@ -12,6 +12,8 @@ v3 + `tht`).
|
||||
|
||||
## Stato attuale (2026-08-13)
|
||||
|
||||
> Per la rotazione della credenziale DWH, fare riferimento al [runbook PSD](../operations/psd-dwh-auth-rollout.md): non autorizza modifiche finché i due gate non sono approvati. Il ThothII PSD server resta `postgres_direct`; il Mac e i client remoti usano `rest_api` con una chiave per installazione. `postgres_direct` e `ssh_tunnel` non usano chiavi `dwh-auth`.
|
||||
|
||||
- **Repository PSD pubblicato:** `https://github.com/mptyl/tht-workspace-psd` (privato), branch
|
||||
`main`, commit `d4f9185`. Layout P1.1 già migrato e validato.
|
||||
- **Deploy key SSH** (sola lettura, senza passphrase) in
|
||||
|
||||
@@ -79,6 +79,10 @@ candidate on the server; it does not transfer workspace files to the operator wo
|
||||
|
||||
## Complete runtime secrets in Workspace management
|
||||
|
||||
### Chiavi DWH REST per installazione
|
||||
|
||||
Un'installazione server che seleziona `rest_api` usa una chiave DWH nel vault cifrato o nel file `API_KEY_FILE`; `postgres_direct` e `ssh_tunnel` non usano chiavi `dwh-auth`. Il servizio `dwh-auth` del DWH ha lifecycle `systemd` separato e non appartiene al Compose di ThothII. Vedere [enrollment client](dwh-auth-client-enrollment.md) e [guida server DWH](dwh-auth-server.md).
|
||||
|
||||
After repository activation, an authenticated user can:
|
||||
|
||||
1. Review the configured repository identity and update it without selecting a workspace.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# PSD — rollout controllato DWH REST
|
||||
|
||||
Questo runbook rispecchia i Task 9–10 del [piano](../superpowers/plans/2026-08-20-dwh-rest-per-installation-auth.md). È descrittivo: non autorizza mutazioni ora. Activity 1 PSD resta `IN_DISCUSSION` fino a due consensi espliciti separati.
|
||||
|
||||
## Invarianti
|
||||
|
||||
- ThothII sul server PSD: `postgres_direct` read-only, senza chiave `dwh-auth`.
|
||||
- Mac PSD e client remoti: `rest_api`, una chiave per installazione, HTTPS `.it` verificato.
|
||||
- `dwh-auth` è `systemd` indipendente, non Compose; non fermare o sostituire il vecchio stack ora.
|
||||
- Sessioni legacy, indici Qdrant e cache Ollama sono dati test: nessuna migrazione o backup per il cutover. Il vecchio stack resta comunque fino a cutover/rollback approvati.
|
||||
- Usare solo `/dwh/rpc/ping`, mai risultati clinici o catture Nginx grezze.
|
||||
|
||||
## Gate A — Task 9, servizio locale senza Nginx pubblico
|
||||
|
||||
Richiedere prima autorizzazione per SHA congelato, target, rollback e impatto legacy. Senza consenso, fermarsi e registrare solo `IN_DISCUSSION`.
|
||||
|
||||
1. Verificare in sola lettura architettura, gruppo `www-data`, nomi liberi, systemd, `nginx -t`, ping attuale e file legacy regolare `root:root` `0600`; non leggerlo, stamparlo o calcolarne hash.
|
||||
2. Costruire con `bash scripts/build-dwh-auth.sh --output /tmp/dwh-auth-release`; registrare solo SHA sorgente e checksum binario.
|
||||
3. Installare binario/unit/tmpfiles come nella [guida server](../install/dwh-auth-server.md): registry `root:dwh-auth` `2750`, lock/record `0640`, socket `dwh-auth:www-data` `0660`.
|
||||
4. Importare una sola legacy `legacy-shared` dal file protetto e creare `psd-mac-primary` in nuovo file `0600` sotto `/root/dwh-auth-provision/`; mai segreti in argv, ambiente, log o evidenze.
|
||||
5. Eseguire `dwh-auth check`, `systemd-analyze verify`, avviare l'unità e testare sul socket Unix con config curl protette `0600`: nuova=204, legacy=204, casuale=401, assente=401.
|
||||
6. Salvare solo ID pubblici, owner/mode, stato unit/socket, timestamp, checksum binario/config e rollback. Non modificare Nginx in questo gate.
|
||||
|
||||
## Gate B — Task 10, Nginx e client
|
||||
|
||||
Serve un secondo consenso: presentare file, backup, canale consegna Mac, osservazione ed esiti 204/401/503.
|
||||
|
||||
1. Creare copie timestampate `root:root` `0600` di `/etc/nginx/sites-available/policlinicosandonato` e file coinvolti; non allegare configurazioni Nginx grezze alle evidenze.
|
||||
2. Aggiungere solo `/etc/nginx/conf.d/dwh-auth-rate-limit.conf` e route DWH; preservare upstream `http://127.0.0.1:3001/`, mantenere byte-identiche le location vector e rimuovere la chiave prima di PostgREST.
|
||||
3. Eseguire checker strutturale, scansione con solo esito, diff limitato e `sudo nginx -t`. Se uno fallisce, ripristinare backup prima di reload e registrare FAIL sanitizzato.
|
||||
4. Dopo consenso fare reload, poi HTTPS `.it` con CA e config curl protette: legacy=204, nuova=204, casuale=401, assente=401 su `/dwh/rpc/ping`; guasto autenticatore=503, mai accesso permissivo.
|
||||
5. Consegnare al Mac chiave e CA separatamente, verificare fingerprint fuori banda, configurare vault GUI o `API_KEY_FILE`, poi **Validate workspace** e **Test connections**.
|
||||
6. Dopo osservazione revocare `legacy-shared` con ragione `shared-credential-rotation`; nuova=204, legacy=401 e journal limitato senza chiavi/digest.
|
||||
|
||||
## Rollback e chiusura
|
||||
|
||||
Durante dual-key il rollback ripristina solo route/servizio revisionati, verifica `nginx -t` e fa reload autorizzato. Non ripristina chiavi revocate, PostgreSQL, dati legacy o stack. Scatta per TLS, risposte inattese, salute degradata o assenza di consenso.
|
||||
|
||||
Activity 1 diventa `PASS` solo con nuova positiva, legacy 401, servizio/Nginx validi, log sanitizzati, rollback leggibile e accettazione owner; poi avanza a Activity 2, con programma ancora `SURVEY_NO_GO`. Compilare [evidenza](../testing/evidence/psd-dwh-auth-rollout-report-template.md) e [collaudo](../testing/dwh-auth-manual-acceptance.md).
|
||||
@@ -0,0 +1,39 @@
|
||||
# Collaudo manuale `dwh-auth`
|
||||
|
||||
Eseguire questo collaudo soltanto con consenso ai gate PSD e con dati sintetici. Non eseguire ora
|
||||
mutazioni server e non registrare chiavi, digest, certificate body, output Nginx grezzo o risultati
|
||||
clinici.
|
||||
|
||||
## Precondizioni
|
||||
|
||||
- SHA sorgente e checksum binario approvati; registry, lock e record hanno owner/mode attesi.
|
||||
- `dwh-auth check`, unit `systemd` e socket Unix sono sani; Nginx viene toccato solo al Gate B.
|
||||
- CA `.it` e fingerprint sono confermati fuori banda; nessun `.com` è usato senza SAN valido.
|
||||
- Il server ThothII PSD è `postgres_direct`; il Mac/remoti sono `rest_api`.
|
||||
|
||||
## Matrice di accettazione
|
||||
|
||||
| Caso | Azione autorizzata | Atteso | Evidenza ammessa |
|
||||
| --- | --- | --- | --- |
|
||||
| Registro | `check`, `key list`, `key status` | Stato e soli ID pubblici | ID, status, owner/mode, timestamp |
|
||||
| Socket locale | Config curl protetta, nuova/legacy | `204` durante dual-key | codice, unit/socket status |
|
||||
| Negativo locale | Config casuale e richiesta senza header | `401` | codice, nessun valore header |
|
||||
| Guasto controllato | Autenticatore/registro non disponibili nel test approvato | `503`, mai accesso | codice e rollback |
|
||||
| HTTPS reale | `/dwh/rpc/ping` con CA approvata | `204`/2xx, TLS valido | ID, esito e approvazione fingerprint |
|
||||
| Mac | **Validate workspace**, **Test connections** | Ping positivo | timestamp e stato GUI |
|
||||
| Revoca | Chiave precedente dopo osservazione | `401`; nuova ancora positiva | ID pubblico e codici |
|
||||
| Trasporti | Server PSD diretto e SSH diagnostico | nessuna chiave `dwh-auth` | trasporto selezionato |
|
||||
|
||||
## Sequenza
|
||||
|
||||
1. Fare il Gate A: verificare localmente 204/401 e che il servizio resti indipendente dal vecchio
|
||||
stack. Nessun reload Nginx.
|
||||
2. Al Gate B, fare backup protetti, `nginx -t`, reload autorizzato e ping `.it` con CA verificata.
|
||||
3. Configurare il Mac nel vault GUI o con `API_KEY_FILE`; verificare ping e ID pubblico.
|
||||
4. Dopo la finestra approvata, revocare legacy, ripetere nuova positiva/legacy 401 e controllare
|
||||
solo un journal bounded sanitizzato.
|
||||
5. Verificare rollback: backup leggibili, scope limitato a route/unit; nessuna migrazione di
|
||||
sessioni legacy, indici Qdrant o cache Ollama.
|
||||
|
||||
Il collaudo passa solo con tutti i casi attesi, owner acceptance e template evidenza completato. `ssh_tunnel` non usa chiavi `dwh-auth` e resta fuori dal runtime sessione.
|
||||
Per la diagnostica seguire [guida server](../install/dwh-auth-server.md) e [TLS](../install/dwh-auth-tls.md).
|
||||
@@ -0,0 +1,40 @@
|
||||
# Template evidenza — rollout PSD `dwh-auth`
|
||||
|
||||
Compilare dopo i gate autorizzati. Questa evidenza contiene solo metadati pubblici e sanitizzati.
|
||||
Non inserire chiavi, digest di credenziali, corpo/fingerprint completo del certificato, output Nginx
|
||||
grezzo, config curl, stringhe di connessione o risultati clinici.
|
||||
|
||||
## Identità e approvazioni
|
||||
|
||||
| Campo | Valore sanitizzato |
|
||||
| --- | --- |
|
||||
| SHA sorgente / checksum binario | `<sha-e-checksum>` |
|
||||
| Proprietario e approvazione Gate A | `<owner-e-timestamp>` |
|
||||
| Proprietario e approvazione Gate B | `<owner-e-timestamp>` |
|
||||
| ID pubblici interessati | `<public-key-ids>` |
|
||||
| Conferma fingerprint fuori banda | `<approvatore-e-timestamp>` |
|
||||
|
||||
## Stato e permessi
|
||||
|
||||
| Oggetto | Percorso | Owner/mode | Stato |
|
||||
| --- | --- | --- | --- |
|
||||
| Registro | `/var/lib/dwh-auth/` | `root:dwh-auth` `2750` | `<pass-fail>` |
|
||||
| Lock e record | `active` / `revoked` | `root:dwh-auth` `0640` | `<pass-fail>` |
|
||||
| Socket | `/run/dwh-auth/verify.sock` | `dwh-auth:www-data` `0660` | `<pass-fail>` |
|
||||
| Backup configurazione | `<protected-path>` | `root:root` `0600` | `<checksum-e-stato>` |
|
||||
|
||||
## Test e decisione
|
||||
|
||||
| Test | Esito atteso | Esito registrato |
|
||||
| --- | --- | --- |
|
||||
| Servizio/socket | 204 nuova e legacy nel dual-key | `<status-e-timestamp>` |
|
||||
| Negativi | 401 casuale, assente e legacy revocata | `<status-e-timestamp>` |
|
||||
| Guasto infrastruttura | 503 fail-closed | `<status-e-timestamp>` |
|
||||
| TLS `.it` | Ping verificato, SAN e conferma fuori banda | `<status-e-timestamp>` |
|
||||
| Mac | Ping positivo e vault/file configurato | `<status-e-timestamp>` |
|
||||
| Journal e scansioni | Nessuna chiave/digest esposti | `<solo-pass-fail>` |
|
||||
| Rollback | Backup leggibile, scope confermato | `<status-e-timestamp>` |
|
||||
|
||||
Decisione Activity 1: `<PASS o stato non conclusivo>`. L'avanzamento a Activity 2 richiede nuova
|
||||
positiva, legacy 401, servizi validi, rollback e accettazione owner; il programma rimane
|
||||
`SURVEY_NO_GO`.
|
||||
@@ -50,6 +50,13 @@ nav:
|
||||
- Guida utente: guida-utente.md
|
||||
- Accettazione autenticazione: testing/authentication-manual-acceptance.md
|
||||
- Setup Policlinico San Donato: install/psd-workspace-setup.md
|
||||
- DWH REST per installazione:
|
||||
- Server dwh-auth: install/dwh-auth-server.md
|
||||
- Enrollment client DWH: install/dwh-auth-client-enrollment.md
|
||||
- TLS DWH REST: install/dwh-auth-tls.md
|
||||
- Rollout PSD DWH: operations/psd-dwh-auth-rollout.md
|
||||
- Collaudo manuale DWH: testing/dwh-auth-manual-acceptance.md
|
||||
- Template evidenza DWH: testing/evidence/psd-dwh-auth-rollout-report-template.md
|
||||
- Programma deploy server PSD: plans/2026-08-20-psd-server-deployment-program.md
|
||||
- Collaudo PSD Progetto A: testing/psd-server-project-a-manual.md
|
||||
- Collaudo PSD Progetto B: testing/psd-server-project-b-manual.md
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
verify="$root/scripts/verify-dwh-auth-docs.sh"
|
||||
temp_root=
|
||||
|
||||
report_pass() {
|
||||
printf 'case=%s status=PASS\n' "$1"
|
||||
}
|
||||
|
||||
report_fail() {
|
||||
printf 'case=%s status=FAIL\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ "$temp_root" == /tmp/thothii-dwh-auth-docs.* && -d "$temp_root" ]]; then
|
||||
rm -rf -- "$temp_root"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
[[ -x "$verify" ]] || report_fail verifier_missing
|
||||
|
||||
temp_root=$(mktemp -d /tmp/thothii-dwh-auth-docs.XXXXXXXX) || report_fail fixture_root
|
||||
fixture_root="$temp_root/fixture"
|
||||
mkdir -p "$fixture_root/docs/install" "$fixture_root/docs/operations" \
|
||||
"$fixture_root/docs/testing/evidence" "$fixture_root/scripts"
|
||||
|
||||
for relative in \
|
||||
docs/install/dwh-auth-server.md \
|
||||
docs/install/dwh-auth-client-enrollment.md \
|
||||
docs/install/dwh-auth-tls.md \
|
||||
docs/operations/psd-dwh-auth-rollout.md \
|
||||
docs/testing/dwh-auth-manual-acceptance.md \
|
||||
docs/testing/evidence/psd-dwh-auth-rollout-report-template.md \
|
||||
docs/install/local-workspace-registry.md \
|
||||
docs/install/server-workspace-registry.md \
|
||||
docs/install/psd-workspace-setup.md \
|
||||
docs/guida-utente.md \
|
||||
docs/index.md \
|
||||
mkdocs.yml; do
|
||||
mkdir -p "$fixture_root/$(dirname "$relative")"
|
||||
cp "$root/$relative" "$fixture_root/$relative"
|
||||
done
|
||||
cp -a "$root/docs/." "$fixture_root/docs/"
|
||||
|
||||
"$verify" --root "$fixture_root" || report_fail positive_source
|
||||
report_pass positive_source
|
||||
|
||||
expect_rejected() {
|
||||
local name=$1 target=$2 addition=$3
|
||||
local case_root="$temp_root/$name"
|
||||
cp -a "$fixture_root" "$case_root"
|
||||
printf '\n%s\n' "$addition" >>"$case_root/$target"
|
||||
if "$verify" --root "$case_root" >/dev/null 2>&1; then
|
||||
report_fail "$name"
|
||||
fi
|
||||
report_pass "$name"
|
||||
}
|
||||
|
||||
# Build synthetic only-in-fixture text at runtime: it is never a provisioned credential.
|
||||
fake_key="thtdwh_v1.$(printf 'A%.0s' {1..16}).$(printf 'A%.0s' {1..43})"
|
||||
fake_digest="$(printf 'A%.0s' {1..43})"
|
||||
|
||||
expect_rejected credential_literal docs/install/dwh-auth-client-enrollment.md "$fake_key"
|
||||
expect_rejected credential_digest_literal docs/testing/evidence/psd-dwh-auth-rollout-report-template.md "secret_sha256: $fake_digest"
|
||||
expect_rejected curl_insecure docs/install/dwh-auth-tls.md 'curl -k https://example.invalid/dwh/rpc/ping'
|
||||
expect_rejected tls_disabled docs/install/dwh-auth-tls.md 'verify_tls=false'
|
||||
expect_rejected secret_in_environment docs/install/dwh-auth-client-enrollment.md "DWH_API_KEY=$fake_key"
|
||||
expect_rejected secret_in_argv docs/install/dwh-auth-client-enrollment.md "curl -H 'X-API-Key: $fake_key' https://example.invalid/dwh/rpc/ping"
|
||||
expect_rejected world_readable_secret docs/install/dwh-auth-server.md 'chmod 0644 /root/dwh-auth-provision/client.key'
|
||||
expect_rejected raw_nginx_capture docs/operations/psd-dwh-auth-rollout.md 'nginx -T > /tmp/nginx-full.conf'
|
||||
expect_rejected compose_coupling docs/install/dwh-auth-server.md 'docker compose up dwh-auth'
|
||||
|
||||
report_pass summary
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
if [[ $# -gt 0 ]]; then
|
||||
[[ $# -eq 2 && $1 == --root && -d $2 ]] || { echo 'usage: verify-dwh-auth-docs.sh [--root DIRECTORY]' >&2; exit 2; }
|
||||
root=$(cd "$2" && pwd -P)
|
||||
fi
|
||||
|
||||
python3 - "$root" <<'PY'
|
||||
import pathlib, re, sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
docs = {
|
||||
"server": "docs/install/dwh-auth-server.md",
|
||||
"client": "docs/install/dwh-auth-client-enrollment.md",
|
||||
"tls": "docs/install/dwh-auth-tls.md",
|
||||
"rollout": "docs/operations/psd-dwh-auth-rollout.md",
|
||||
"manual": "docs/testing/dwh-auth-manual-acceptance.md",
|
||||
"evidence": "docs/testing/evidence/psd-dwh-auth-rollout-report-template.md",
|
||||
"local": "docs/install/local-workspace-registry.md",
|
||||
"server_registry": "docs/install/server-workspace-registry.md",
|
||||
"psd": "docs/install/psd-workspace-setup.md",
|
||||
"guide": "docs/guida-utente.md",
|
||||
"index": "docs/index.md",
|
||||
"nav": "mkdocs.yml",
|
||||
}
|
||||
text = {}
|
||||
for label, relative in docs.items():
|
||||
path = root / relative
|
||||
if not path.is_file():
|
||||
raise SystemExit(f"dwh-auth docs: missing {relative}")
|
||||
text[label] = path.read_text(encoding="utf-8")
|
||||
|
||||
requirements = {
|
||||
"server": ["/var/lib/dwh-auth", "root:dwh-auth", "2750", ".writer.lock", "0640", "/run/dwh-auth/verify.sock", "0660", "systemd", "key create", "key list", "key status", "key revoke", "check", "backup", "rollback", "disinstallazione", "rest_api", "postgres_direct", "ssh_tunnel"],
|
||||
"client": ["Workspace management", "Save runtime secrets", "API_KEY_FILE", "THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE", "TLS_CA_FILE", "/rpc/ping", "rest_api", "postgres_direct", "ssh_tunnel", "401", "503", "rotazione", "revoca"],
|
||||
"tls": ["self-issued", ".it", ".com", "SAN", "TLS_CA_FILE", "openssl x509 -noout -fingerprint -sha256", "fuori banda", "rinnovo", "curl -k"],
|
||||
"rollout": ["Task 9", "Task 10", "IN_DISCUSSION", "postgres_direct", "rest_api", "legacy-shared", "nginx -t", "204", "401", "503", "Qdrant", "Ollama", "rollback"],
|
||||
"manual": ["204", "401", "503", "TLS", "registry", "postgres_direct", "ssh_tunnel"],
|
||||
"evidence": ["ID pubblici", "owner", "mode", "timestamp", "checksum", "approvazione"],
|
||||
}
|
||||
for label, tokens in requirements.items():
|
||||
lowered = text[label].lower()
|
||||
for token in tokens:
|
||||
if token.lower() not in lowered:
|
||||
raise SystemExit(f"dwh-auth docs: {docs[label]} lacks required topic: {token}")
|
||||
|
||||
for path in [root / docs[k] for k in ("server", "client", "tls", "rollout", "manual", "evidence", "local", "server_registry", "psd", "guide", "index")]:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
for target in re.findall(r"(?<!!)\[[^]]*\]\(([^)#]+)(?:#[^)]+)?\)", source):
|
||||
if "://" in target or target.startswith("mailto:"):
|
||||
continue
|
||||
candidate = (path.parent / target).resolve()
|
||||
if not candidate.is_file() or root.resolve() not in candidate.parents:
|
||||
raise SystemExit(f"dwh-auth docs: broken local link {path.relative_to(root)} -> {target}")
|
||||
|
||||
nav = text["nav"]
|
||||
for relative in (docs["server"], docs["client"], docs["tls"], docs["rollout"], docs["manual"], docs["evidence"]):
|
||||
nav_relative = relative.removeprefix("docs/")
|
||||
if nav.count(nav_relative) != 1:
|
||||
raise SystemExit(f"dwh-auth docs: navigation must include once: {nav_relative}")
|
||||
|
||||
corpus = "\n".join(text.values())
|
||||
for pattern, label in [
|
||||
(r"thtdwh_v1\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]{43}", "credential literal"),
|
||||
(r"(?mi)^\s*secret_sha256\s*[:=]\s*[A-Za-z0-9_-]{16,}", "credential digest literal"),
|
||||
(r"(?mi)^\s*[A-Z][A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|PASSWORD)\s*=\s*(?!/|<)[^\s#]+", "secret in environment"),
|
||||
(r"(?i)(?:curl|dwh-auth)[^\n]{0,240}(?:-H\s+['\"][^'\"]*X-API-Key\s*:|--(?:api-key|token|password)\b)", "secret in argv"),
|
||||
(r"(?im)^(?!.*(?:non usare|mai usare)).*curl\s+(?:[^\n]*\s)?(?:-k|--insecure)\b|verify_tls\s*=\s*false|insecure_skip_verify", "TLS bypass"),
|
||||
(r"(?i)chmod\s+0?[0-7][0-7][4-7]\s+[^\n]*(?:\.key|secret|provision)", "world-readable secret"),
|
||||
(r"(?m)^\s*nginx\s+-T\b", "raw Nginx capture"),
|
||||
(r"(?i)docker\s+compose[^\n]*\bdwh-auth\b", "Compose coupling"),
|
||||
]:
|
||||
if re.search(pattern, corpus):
|
||||
raise SystemExit(f"dwh-auth docs: forbidden {label}")
|
||||
|
||||
print("dwh-auth documentation contract passed")
|
||||
PY
|
||||
Reference in New Issue
Block a user