fix: harden DWH auth operator guidance

This commit is contained in:
User
2026-08-21 04:28:20 +02:00
parent 7b9b8b308d
commit 707c13d781
14 changed files with 196 additions and 56 deletions
+6 -7
View File
@@ -23,18 +23,17 @@ pubblico.
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**.
2. Il trasporto `rest_api` è una precondizione amministrativa del binding locale, non una scelta della GUI. Controllare URL/trust locali e usare **Validate workspace source**.
3. Inserire la chiave nel campo write-only **Data warehouse API key**, poi **Save entered 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
4. Eseguire **Test workspace 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
5. Comunicare al server solo ID pubblico, timestamp e risultato. **Forget stored value** 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:
`API_KEY_FILE` significa che il valore è nel file, non nella variabile. Questo è l'esempio Mac/local/remoto nel file PSD non tracciato `workspace-bindings.env`; non è il binding del server PSD Project A, che resta `postgres_direct`. I binding REST sono:
```dotenv
THT_WS_PSD_CLINICAL_DWH_TRANSPORT=rest_api
@@ -61,7 +60,7 @@ sostituire `PSD_CLINICAL` con ID immutabile maiuscolo (trattini in underscore).
## Ping, rotazione e revoca
Usare solo **Test connections** su `/rpc/ping`: successo è 2xx con TLS verificato; il server
Usare solo **Test workspace 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`.
+96 -6
View File
@@ -69,7 +69,8 @@ Usare sempre un root assoluto. Questi comandi espongono solo ID pubblici, stato,
```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
key_id=public-key-id
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key status --key-id "$key_id" --json
```
Un errore di integrità, permessi, symlink o JSON malformato richiede ripristino da backup protetto,
@@ -81,13 +82,16 @@ Il comando crea la chiave una volta in un nuovo file assoluto `0600`; stdout con
pubblico, installazione e percorso. Il file di output non deve esistere.
```bash
installation_id=psd-mac-primary
description=operatore-mac-primario
key_output="/root/dwh-auth-provision/$installation_id-primary.key"
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
--installation-id "$installation_id" \
--description "$description" \
--output "$key_output"
```
Aggiungere `--expires-at <RFC3339-UTC>` solo se la policy impone una scadenza; il default è nessuna
Aggiungere `--expires-at "YYYY-MM-DDTHH:MM:SSZ"` 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.
@@ -106,8 +110,10 @@ l'ID pubblico; attendere l'osservazione; poi revocare la precedente e provare nu
precedente=401.
```bash
previous_key_id=public-key-id
revocation_reason=shared-credential-rotation
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key revoke \
--key-id <previous-public-key-id> --reason <non-secret-reason>
--key-id "$previous_key_id" --reason "$revocation_reason"
```
La revoca non è annullabile e un ID revocato non si ricrea.
@@ -124,6 +130,90 @@ La disinstallazione richiede autorizzazione esplicita, client REST migrati/revoc
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`.
## Procedure riproducibili e secret-safe
Eseguire soltanto nel gate autorizzato. Le variabili seguenti contengono percorsi, timestamp e codici, mai una chiave. Il manifest e l'archivio del registro sono `0600`; l'archivio resta materiale riservato.
```bash
run_id=$(date -u +%Y%m%dT%H%M%SZ)
backup_root=/root/dwh-auth-provision
registry_backup="$backup_root/registry-$run_id.tar"
manifest="$backup_root/registry-$run_id.manifest"
sudo install -o root -g root -m 0600 /dev/null "$registry_backup"
sudo install -o root -g root -m 0600 /dev/null "$manifest"
sudo tar --acls --xattrs -C /var/lib -cf "$registry_backup" dwh-auth
sudo sh -c 'sha256sum "$1" > "$2"' sh "$registry_backup" "$manifest"
if sudo sha256sum -c "$manifest" >/dev/null; then printf 'registry_manifest=PASS\n'; else printf 'registry_manifest=FAIL\n' >&2; exit 1; fi
```
Per ripristinare, fermare prima il servizio, verificare il manifest senza stamparne il contenuto, estrarre l'archivio solo nel root autorizzato, rieseguire `dwh-auth check` e avviare l'unità. Conservare archivio e manifest per la retention approvata; non sovrascrivere né cancellare record per correggere un errore.
```bash
if ! sudo sha256sum -c "$manifest" >/dev/null; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
sudo systemctl stop dwh-auth
sudo tar --acls --xattrs -C /var/lib -xf "$registry_backup"
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth check
sudo systemctl start dwh-auth
printf 'registry_restore=PASS\n'
```
Per i test socket, creare la configurazione curl `0600` leggendo il file chiave direttamente nel file di configurazione: il valore non passa in argv, ambiente o stdout.
```bash
key_file=/root/dwh-auth-provision/psd-mac-primary.key
curl_cfg=/root/dwh-auth-provision/dwh-auth-new.curl
random_cfg=/root/dwh-auth-provision/dwh-auth-random.curl
sudo install -o root -g root -m 0600 /dev/null "$curl_cfg"
sudo install -o root -g root -m 0600 /dev/null "$random_cfg"
sudo sh -c '{ printf "%s" "header = X-API-Key: "; tr -d "\r\n" < "$1"; printf "\n"; } > "$2"' sh "$key_file" "$curl_cfg"
sudo sh -c 'printf "%s\n" "header = X-API-Key: invalid-test" > "$1"' sh "$random_cfg"
```
Il socket `/verify` deve restituire 204 per il file nuovo e 401 per configurazione casuale e richiesta senza header; stampare soltanto il codice.
```bash
status=$(sudo curl --config "$curl_cfg" --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 204 ] && printf 'socket_new=PASS\n' || { printf 'socket_new=FAIL\n' >&2; exit 1; }
status=$(sudo curl --config "$random_cfg" --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 401 ] && printf 'socket_random=PASS\n' || { printf 'socket_random=FAIL\n' >&2; exit 1; }
status=$(sudo curl --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 401 ] && printf 'socket_missing=PASS\n' || { printf 'socket_missing=FAIL\n' >&2; exit 1; }
```
Per HTTPS reale usare la configurazione protetta e la CA approvata contro `/dwh/rpc/ping`: PostgREST può restituire qualsiasi 2xx, non si pretende 204. La prova 401 usa solo configurazione casuale protetta.
```bash
ping_url=https://supabase-aritmolab.policlinicosandonato.it/dwh/rpc/ping
ca_file=/root/dwh-auth-provision/psd-dwh-ca.pem
status=$(sudo curl --config "$curl_cfg" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
case "$status" in 2??) printf 'https_new=PASS\n' ;; *) printf 'https_new=FAIL\n' >&2; exit 1 ;; esac
status=$(sudo curl --config "$random_cfg" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
[ "$status" = 401 ] && printf 'https_random=PASS\n' || { printf 'https_random=FAIL\n' >&2; exit 1; }
```
Per provare 503 in una finestra approvata, registrare l'orario, fermare temporaneamente l'unità, eseguire il ping con timeout e trap di ripristino; il comando deve stampare solo PASS/FAIL.
```bash
was_active=$(sudo systemctl is-active dwh-auth || true)
[ "$was_active" = active ] || { printf 'https_auth_down=FAIL\n' >&2; exit 1; }
restore_auth() { sudo systemctl start dwh-auth; }
trap restore_auth EXIT INT TERM
sudo systemctl stop dwh-auth
status=$(sudo curl --config "$random_cfg" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url" || true)
[ "$status" = 503 ] && printf 'https_auth_down=PASS\n' || { printf 'https_auth_down=FAIL\n' >&2; exit 1; }
sudo systemctl start dwh-auth
trap - EXIT INT TERM
```
Lo scan journal non salva righe grezze: emette solo PASS/FAIL.
```bash
since=$(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%SZ)
if sudo journalctl -u dwh-auth --since "$since" --no-pager --output=cat | grep -E 'thtdwh_v1|secret_sha256' >/dev/null; then printf 'journal_secret_scan=FAIL\n' >&2; exit 1; else printf 'journal_secret_scan=PASS\n'; fi
```
Dopo rollback verificato e migrazione/revoca di ogni client REST, la disinstallazione resta condizionata all'approvazione: eseguire `sudo systemctl disable --now dwh-auth`, ma mantenere registro, backup e manifest protetti per la retention; non cancellarli durante il rollback.
## Troubleshooting
| Sintomo | Interpretazione e azione |
+1 -1
View File
@@ -34,7 +34,7 @@ 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`.
**Test workspace connections** su `/rpc/ping`. Non disabilitare TLS e non usare `curl -k`.
## Rinnovo coordinato
+6 -6
View File
@@ -60,9 +60,9 @@ After the installation is started, use Workspace management from the authenticat
clone.
2. Confirm that the installation-owned `workspace-secrets` storage remains outside the source
repository and contains no credentials in the workspace descriptors.
3. Select the workspace and run **Validate workspace** to verify the active descriptor, catalog,
3. Select the workspace and run **Validate workspace source** to verify the active descriptor, catalog,
Evidence, annotations, and runtime bindings.
4. Run **Test connections** only with the approved read-only DWH/Evidence test configuration.
4. Run **Test workspace connections** only with the approved read-only DWH/Evidence test configuration.
Results are redacted and the workspace source remains unchanged.
<!-- workspace-descriptor-contract:start -->
@@ -118,7 +118,7 @@ and fast-forward candidate checkout. It does not copy anything to the user's com
### 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).
Se il binding selezionato è `rest_api`, la chiave DWH è una credenziale per questa installazione e si salva nel vault tramite **Save entered 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.
@@ -127,13 +127,13 @@ Open Workspace management after the first successful repository update.
tests do.
3. Review the runtime fields derived from the selected DWH transport and Evidence authentication
mechanism.
4. Enter or rotate the required values and choose **Save runtime secrets**.
5. Run **Validate workspace** and then **Test connections**.
4. Enter or rotate the required values and choose **Save entered secrets**.
5. Run **Validate workspace source** and then **Test workspace connections**.
Secret fields are write-only. The GUI receives only configured/missing status. Values are
encrypted by the backend in the platform-neutral `workspace-secrets` volume. ThothII temporarily
materializes a restrictive file only while an existing file-oriented connector needs it, then
removes that file when the runtime lease ends. **Forget** deletes the selected encrypted value.
removes that file when the runtime lease ends. **Forget stored value** deletes the selected encrypted value.
The workspace YAML stays environment-independent: it declares connector mechanisms, not host
paths or credentials. Installation trust material such as a Git CA or `known_hosts` remains an
+6 -4
View File
@@ -2,16 +2,18 @@
Authentication acceptance is documented in the [manual authentication matrix](../testing/authentication-manual-acceptance.md).
Use generic OIDC with Authentik as the certified group catalog, map only the exact TOT Users and
TOT Admin groups, then run Workspace Validate, `tht auth check`, `tht auth check --interactive`,
and Workspace Test in that order. Browser callback E2E, native Windows execution, approved PSD
TOT Admin groups, then run **Validate workspace source**, `tht auth check`, `tht auth check --interactive`,
and **Test workspace connections** in that order. Browser callback E2E, native Windows execution, approved PSD
manual identities, external L2, and the two parked restore-lock preconditions remain pending the
Task 15/release gates.
Guida operativa per collegare ThothII al DWH di PSD con il nuovo sistema (registry Git + descriptor
v3 + `tht`).
## Stato attuale (2026-08-13)
## Stato storico Mac/local (2026-08-13)
> Questo stato è storico per Mac/local; il server PSD Project A usa binding separato `postgres_direct` read-only.
>
> 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
@@ -22,7 +24,7 @@ v3 + `tht`).
- **Config operatore pronta** (file reali gitignored in `deploy/psd/`): `operator.env`,
`thothii-installation.yaml` e i secret d'installazione in `secrets/` (pi-auth, secret bundle,
chiave SSH, known_hosts). L'API key DWH va completata nella gestione Workspace ed è conservata
nel vault cifrato del backend. Nessuna CA: il DWH REST usa HTTPS pubblico.
nel vault cifrato del backend. Il certificato REST è self-issued/private: ogni Mac/local senza trust equivalente deve usare `TLS_CA_FILE` e verificare il fingerprint fuori banda, come in `docs/install/dwh-auth-tls.md`.
- **Stack avviato** (progetto `thothii-70417a3e30ea`, via `tht start`): `qdrant`, `embedding`
(con `qwen3-embedding:0.6b`), `core`, `frontend` sani. Il registry ha **clonato e attivato**
`psd-clinical` (stato `ready`).
+4 -4
View File
@@ -87,9 +87,9 @@ After repository activation, an authenticated user can:
1. Review the configured repository identity and update it without selecting a workspace.
2. Select a workspace to see the DWH/Evidence credential fields required by its connector modes.
3. Blind-save or rotate values; returned responses contain status only.
4. Run **Validate workspace source** and then test its configured connections.
5. Forget an obsolete value after dependent sessions and jobs have ended.
3. Blind-save or rotate values with **Save entered secrets**; returned responses contain status only.
4. Run **Validate workspace source** and then **Test workspace connections**.
5. Use **Forget stored value** for an obsolete value after dependent sessions and jobs have ended.
The backend encrypts values in `/data/workspace-secrets`, including the installation-specific
master key. The server profile persists that directory inside `THT_DATA_ROOT`; no workspace YAML
@@ -106,7 +106,7 @@ descriptors, Evidence paths, and cross-workspace invariants at one commit, then
the complete candidate. A rejected candidate never replaces the previous active snapshot. The
application-owned checkout and snapshots are read-only runtime state.
Validation proves descriptor and repository structure. **Test connections** additionally
Validation proves descriptor and repository structure. **Test workspace connections** additionally
materializes the current runtime secrets and contacts only the selected workspace's configured
DWH/Evidence endpoints. Failure does not modify or publish workspace source.