Skip to main content

Architettura di Vault

Questa pagina è un riferimento tecnico per operatori e contributori attenti alla sicurezza che desiderano comprendere in modo approfondito come funziona l’integrazione del vault di Switchbord. Per le istruzioni di configurazione, vedi Configurazione di Vault.

Modello di Memorizzazione

Switchbord utilizza due livelli di oggetti del database per memorizzare i segreti del workspace: vault.secrets (integrato in Supabase) I valori dei segreti effettivamente crittografati risiedono qui. Ogni riga contiene una colonna secret crittografata con pgsodium (AES-256-GCM), una colonna name per l’identificazione leggibile e un key_id che fa riferimento alla chiave pgsodium utilizzata per la crittografia. Le righe sono identificate da un UUID. app_private.workspace_secret_bindings (personalizzata di Switchbord) Questa tabella mappa un workspace ai suoi segreti:
La tabella dei binding non contiene di per sé dati sensibili. È puramente un puntatore: per il workspace X e il tipo di segreto Y, individua il segreto vault Z. La decrittografia effettiva avviene in vault.secrets.

Perché lo Schema app_private

Lo schema app_private è utilizzato per tabelle e funzioni che non devono essere esposte tramite PostgREST (l’API REST auto-generata di Supabase). Per impostazione predefinita, PostgREST espone solo gli oggetti nello schema public (e gli schemi esplicitamente elencati in db_schema). Il posizionamento di workspace_secret_bindings in app_private garantisce che:
  • Nessun client anonimo o autenticato possa eseguire direttamente una SELECT sulla tabella dei binding tramite la libreria client di Supabase
  • Non venga generato alcun endpoint REST per /app_private/workspace_secret_bindings
  • L’accesso sia possibile solo tramite funzioni esplicitamente definite eseguite come SECURITY DEFINER
La row-level security è anch’essa attivata su workspace_secret_bindings come misura di defense-in-depth, ma il posizionamento nello schema app_private è il livello di isolamento primario.
Il pattern dello schema app_private è una convenzione standard di Supabase per gli oggetti di schema sensibili. È documentato nelle guide di Supabase su “Row Level Security” e “Functions” come il modo consigliato per proteggere dati che non dovrebbero mai essere accessibili direttamente.

Architettura del Percorso di Lettura

Il percorso di lettura è progettato per evitare di richiedere una connessione diretta al database dal servizio worker. Tutte le letture sensibili passano attraverso funzioni RPC di Supabase definite con SECURITY DEFINER.

Funzioni SECURITY DEFINER

Per garantire che gli elementi del vault non vengano mai esposti tramite lo schema public standard o gli endpoint REST auto-generati, Switchbord utilizza il pattern dello schema app_private. app_private.read_workspace_secret Unisce app_private.workspace_secret_bindings con vault.decrypted_secrets per restituire il valore in chiaro. Viene eseguita come SECURITY DEFINER, consentendole di accedere allo schema vault, normalmente limitato. app_private.resolve_workspace_by_verify_token Utilizzata durante la verifica del webhook Meta per instradare le richieste al workspace corretto senza esporre i token al client.

Wrapper RPC Pubblici

Poiché PostgREST espone solo lo schema public, forniamo wrapper leggeri che applicano l’autorizzazione:
  • public.sb_read_workspace_secret: valida auth.role() == 'service_role' prima di delegare alla funzione privata.
  • public.sb_resolve_workspace_by_verify_token: segue lo stesso pattern di sicurezza per la risoluzione del token.
Questi wrapper rifiutano esplicitamente le chiamate effettuate con la chiave anon o un JWT con ambito utente. Solo la chiave del ruolo di servizio (SUPABASE_SECRET_KEY) può invocarli. Ciò è applicato a livello SQL tramite controlli auth.role() all’interno delle funzioni wrapper.

Percorso di Scrittura

Il percorso di scrittura passa attraverso l’applicazione web di Switchbord e non coinvolge il worker:
Il percorso di scrittura utilizza una connessione Postgres diretta (SUPABASE_DB_URL) quando invoca vault.create_secret() dall’esterno del server applicativo, ma la route API Next.js gestisce questo utilizzando il client con ruolo di servizio di Supabase. Chi effettua il self-hosting non necessita di SUPABASE_DB_URL nel worker — solo nell’applicazione web se sono necessarie scritture al vault dal codice lato server.

Crittografia a Riposo

Supabase Vault utilizza pgsodium, un’estensione PostgreSQL che avvolge libsodium.
  • Algoritmo: XChaCha20-Poly1305 (un cifrario a flusso con autenticazione)
  • Gestione delle chiavi: pgsodium utilizza una chiave radice derivata da un segreto memorizzato fuori dal database (nell’infrastruttura di gestione delle chiavi di Supabase per Cloud, o configurata separatamente per il self-hosting)
  • Ambito della crittografia: ogni segreto in vault.secrets viene crittografato con una chiave derivata dalla chiave radice. Il testo cifrato e l’ID della chiave vengono memorizzati insieme; il testo in chiaro non viene mai persistito.
  • Decrittografia: la vista vault.decrypted_secrets esegue la decrittografia in modo trasparente utilizzando pgsodium.crypto_aead_det_decrypt(). Solo i ruoli del database con permesso SELECT su questa vista possono leggere i valori in chiaro.
Per i deployment su Supabase Cloud, la chiave radice pgsodium è gestita da Supabase e non viene mai esposta ai proprietari del progetto. Per Supabase self-hosted, l’utente è responsabile della protezione della chiave radice — vedi la guida al self-hosting di Supabase per la configurazione delle chiavi.

Riepilogo del Controllo degli Accessi

Il worker autentica tutte le chiamate RPC utilizzando il JWT del ruolo di servizio (SUPABASE_SECRET_KEY). Questa chiave deve essere mantenuta segreta e non deve mai essere esposta ai client browser o registrata nei log.

Riferimento per la Migrazione

L’integrazione del vault è composta da due livelli: Integrato in Supabase (nessuna azione richiesta)
  • Schema vault e tabella vault.secrets
  • Vista vault.decrypted_secrets
  • Funzione vault.create_secret()
  • Estensione pgsodium
Questi sono presenti su tutti i progetti Supabase Cloud e possono essere attivati su Supabase self-hosted tramite CREATE EXTENSION supabase_vault. Personalizzato di Switchbord (applicato tramite migrazioni)
  • Creazione dello schema app_private
  • Tabella app_private.workspace_secret_bindings
  • Funzione app_private.read_workspace_secret()
  • Funzione app_private.resolve_workspace_by_verify_token()
  • Wrapper public.sb_read_workspace_secret()
  • Wrapper public.sb_resolve_workspace_by_verify_token()
Questi vengono creati dalle migrazioni del database di Switchbord in supabase/migrations/. L’esecuzione di supabase db push applica tutte le migrazioni in sospeso e garantisce l’esistenza di questi oggetti. Se è necessario ispezionare direttamente le definizioni delle funzioni:

Considerazioni sulla Sicurezza per chi Effettua il Self-Hosting

Esposizione della chiave del ruolo di servizio La SUPABASE_SECRET_KEY può invocare qualsiasi funzione RPC e ignora la RLS. Trattarla con la stessa cura di una password root del database. Ruotarla immediatamente in caso di sospetta esposizione. Chiave pgsodium di Supabase self-hosted Se si esegue Supabase localmente o sulla propria infrastruttura, la chiave radice pgsodium deve essere impostata prima che venga memorizzato qualsiasi segreto. Se la chiave cambia o viene persa, tutti i segreti vault memorizzati diventano irrecuperabili — non esiste alcun key escrow. Integrità delle migrazioni Le funzioni SECURITY DEFINER in app_private sono sicure solo quanto lo è il ruolo proprietario del database. Assicurarsi che solo le parti fidate abbiano la possibilità di ridefinire o sostituire queste funzioni tramite migrazioni.

Guide Correlate