La Key/Value Secrets Engine è parte integrante di quasi ogni implementazione di Vault. Costituisce la base per la memorizzazione sicura di secrets statici ed è, nella pratica, utilizzata molto più frequentemente rispetto a molte engine dinamiche.
Dopo l’introduzione teorica nella parte 2a, in questo articolo ci dedichiamo al lavoro concreto con la KV Engine. Mostriamo come scrivere, leggere, aggiornare e cancellare secrets, analizzando in modo pratico le differenze tra la versione 1 e la versione 2 di KV. Il focus è sui comandi rilevanti per la produzione, sugli ostacoli realistici e su raccomandazioni concrete per l’operatività quotidiana, motivo per cui vi trasmetto queste conoscenze come una combinazione di tutorial e cheat-sheet.
Il comando vault kv: la vostra cassetta degli attrezzi per l’uso quotidiano
L’interazione con la KV Engine avviene tramite il comando vault kv. Questo comando astrae internamente le differenze tra le versioni, ma indica chiaramente nell’output se si tratta di una engine v1 o v2. I sottocomandi fondamentali possono essere suddivisi in due gruppi:
Operazioni di base (disponibili in KV v1 e v2):
- put: scrivere o aggiornare secrets
- get: recuperare secrets
- delete: cancellare secrets (non necessariamente in modo definitivo)
- list: mostrare tutti i percorsi esistenti
Operazioni avanzate (solo KV v2):
- patch: aggiornare singole chiavi senza sovrascrivere le altre
- rollback: ripristinare una versione specifica
- undelete: recuperare versioni eliminate
- destroy: eliminare versioni in modo irrevocabile
Questi comandi avanzati sono disponibili solo in KV v2, poiché si basano sui metadati memorizzati internamente. La vecchia v1 non supporta ancora i metadati.
Scrivere secrets con put: semplice, ma insidioso
Il comando di scrittura vault kv put è il metodo centrale per scrivere secrets nella KV Engine:
vault kv put <percorso> <chiave>=<valore> [<chiave>=<valore>...]
Un esempio semplice: per prima cosa abilitiamo le Secrets Engine:
$ vault secrets enable kv
Success! Enabled the kv secrets engine at: kv/
$
$ vault secrets enable -path=kvv2 kv-v2
Success! Enabled the kv-v2 secrets engine at: kvv2/
Il comando seguente salva il secret nel percorso kv/app/db con la chiave pass e il valore 123. L'output varia a seconda della versione utilizzata:
KV Versione 1:
$ vault kv put kv/app/db pass=123 Success! Data written to: kv/app/db $
KV Versione 2:
$ vault kv put kvv2/app/db pass=123 == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T10:52:07.551872783Z custom_metadata <nil> deletion_time n/a destroyed false version 1 $
La versione 2 fornisce metadati come timestamp di creazione e versione attuale.
Un'importante osservazione: Con KV v2, il salvataggio avviene su un percorso interno con il prefisso aggiuntivo /data/. Questo è particolarmente rilevante quando si usa l’API direttamente, perché significa che non si può semplicemente migrare da v1 a v2 senza eventualmente adattare script e applicazioni!
Salvare più coppie Key/Value contemporaneamente
Un singolo comando put può salvare un numero arbitrario di chiavi:
$ vault kv put kv/app/db pass=123 user=admin api=myapisecret Success! Data written to: kv/app/db $ vault kv put kvv2/app/db pass=123 user=admin api=myapisecret == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T10:56:04.624145825Z custom_metadata <nil> deletion_time n/a destroyed false version 2 [rramge@ol9 terraform-vault-kv]$
Nella pratica questo permette di risparmiare comandi, ma non dimenticate: l'intero contenuto al percorso viene sovrascritto. In caso di dubbio, è meglio procedere per singole chiavi, per aumentare la leggibilità degli script e ridurre il rischio di errori - oppure utilizzare file, come mostrato nella sezione seguente.
Importare secrets da file JSON
Soprattutto per ambienti automatizzati o strutture complesse di secrets, è consigliabile l’utilizzo di file per l’inserimento di più coppie Key/Value:
$ vault kv put kv/app/db @secrets.json
Esempio di contenuto del file secrets.json:
{ "pass": "123", "user": "admin", "api": "myapisecret" }
Differenza importante tra put e patch
Molti utenti di Vault commettono presto un errore fatale: usano put per aggiornare singoli valori.
Questo comporta, sia in KV v1 che v2, che tutti i dati già presenti per il secret vengano cancellati se non sono inclusi nel nuovo comando put.
Esempio:
# Stato iniziale
$ vault kv put kv/app/db pass=123 user=admin api=myoldapisecret
Success! Data written to: kv/app/db
$
# Aggiornamento della chiave API, erroneamente con "put" $ vault kv put kv/app/db pass=123 user=admin api=myoldapisecret
Success! Data written to: kv/app/db
$
# Risultato: rimane solo "api" $ vault kv get kv/app/db
=== Data ===
Key Value
--- -----
api mynewapisecret
$
Le chiavi user e pass sono quindi andate perse.
vault kv put non era il comando corretto per conservare le chiavi esistenti user e pass. Solo patch ne impedisce la cancellazione. Torneremo su questo più avanti. Per ora ricordate che put è distruttivo:
Importante: Un comando vault kv put sostituisce sempre tutti i dati presenti al percorso indicato. Non è un’operazione di merge!
Leggere secrets con get: uno sguardo alla memoria
Con vault kv get si recuperano i secrets. Per impostazione predefinita viene sempre mostrata la versione più recente.
Ad esempio, questo comando mostra:
vault kv get kv/app/db
Con KV v1:
$ vault kv get kv/app/db
==== Data ====
Key Value
--- -----
api myapisecret
pass 123
user admin
$
Con KV v2:
$ vault kv get kvv2/app/db
== Secret Path ==
kvv2/data/app/db
======= Metadata =======
Key Value
--- -----
created_time 2025-07-01T10:56:04.624145825Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 2
==== Data ====
Key Value
--- -----
api myapisecret
pass 123
user admin
$
Nota: Anche qui il percorso /data/ in KV v2 è visibile. Se intendete effettuare il parsing di questo output in script o applicazioni, tenetelo in considerazione.
Output JSON per script e automazione
Con -
format=json si ottengono dati leggibili da macchina:
$ vault kv get -format=json kv/app/db { "request_id": "7de43863-d7c4-837f-b59c-4b5e546a1d65", "lease_id": "", "lease_duration": 2764800, "renewable": false, "data": { "api": "myapisecret", "pass": "123", "user": "admin" }, "warnings": null, "mount_type": "kv" } $
È possibile combinarlo con jq:
$ vault kv get -format=json kv/app/db | jq -r '.data.api' myapisecret $
Pro-Tip: Potete impostare in modo permanente il formato di output predefinito su JSON tramite la variabile d'ambiente VAULT_FORMAT:
export VAULT_FORMAT=json
Recuperare versioni di un secret con -version
Il comportamento di lettura di get è piuttosto semplice da comprendere, ma bisogna prima conoscerne le differenze. Si applicano le seguenti regole:
- Un normale comando get restituisce sempre l'ultima versione.
- Per i secrets cancellati (KV v2) si ricevono ancora i metadati, ma nessun dato.
- Versioni specifiche possono essere recuperate con --version=X (solo con KV v2).
Esempio di recupero di una specifica versione di un secret:
$ vault kv get --version=1 kvv2/app/db == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T10:52:07.551872783Z custom_metadata <nil> deletion_time n/a destroyed false version 1 ==== Data ==== Key Value --- ----- pass 123 $ vault kv get --version=2 kvv2/app/db == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T10:56:04.624145825Z custom_metadata <nil> deletion_time n/a destroyed false version 2 ==== Data ==== Key Value --- ----- api myapisecret pass 123 user admin $
L'ho già detto più volte, ma non si può sottolineare abbastanza: La versioning dei secrets è disponibile solo in KV v2. Ciò significa anche che solo la KV v2 Secrets Engine consente una visione mirata della cronologia dei singoli secrets. Questo può essere molto importante per attività di auditing future, ad esempio per dimostrare se e quante volte i secrets sono stati effettivamente ruotati.
Aggiornamenti mirati e ripristino dei secrets
patch: aggiornare in modo preciso la chiave corretta
Più sopra in questo articolo abbiamo già sottolineato esplicitamente che il comando put sovrascrive completamente i secrets esistenti e potrebbe eliminare dati presenti. Per questo, si consiglia di utilizzare patch per modificare selettivamente singoli valori all’interno di un secret.
Con KV v1 questo non è possibile per mancanza di metadati:
$ vault kv patch kv/app/db user=dbadmin
KV engine mount must be version 2 for patch support
$
Con KV v2 invece sì:
$ vault kv patch kvv2/app/db user=dbadmin == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T11:07:50.365256138Z custom_metadata <nil> deletion_time n/a destroyed false version 3 $
Risultato: solo la chiave user viene aggiornata, il resto del secret rimane invariato.
rollback: ripristinare in sicurezza versioni precedenti
Un secret salvato erroneamente? Avete usato vault kv put per sbaglio? Nessun problema con KV v2. Se avete sovrascritto accidentalmente dei dati, potete tornare a una versione precedente tramite rollback in KV v2:
$ vault kv get kvv2/app/db == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T11:07:50.365256138Z custom_metadata <nil> deletion_time n/a destroyed false version 3 ==== Data ==== Key Value --- ----- api myapisecret pass 123 user dbadmin $ $ vault kv rollback -version=2 kvv2/app/db Key Value --- ----- created_time 2025-07-01T11:10:44.341592593Z custom_metadata <nil> deletion_time n/a destroyed false version 4 $dbadmin \$
\$ vault kv rollback -version=2 kvv2/app/db
Key Value --- ----- created_time 2025-07-01T11:10:44.341592593Z custom_metadata <nil> deletion_time n/a destroyed false version 4 $
Questo comando crea una nuova versione con il contenuto della versione precedente indicata. Nel nostro esempio:
- La versione 2 contiene il contenuto originale
- La versione 3 era il tentativo di sovrascrittura errato
- La versione 4 è il risultato del rollback
Pro-Tip: Non viene ripristinato tutto allo stato della versione 2, ma viene creata una nuova versione con il contenuto della versione 2. La cronologia resta quindi intatta.
Eliminazione dei dati: delete vs. destroy
È fondamentale ricordare che il comando delete ha effetti diversi in KV v1 e v2.
KV v1: delete definitivo
Per i secrets con la KV v1 Secrets Engine, il comando delete elimina definitivamente i dati:
$ vault kv delete kv/app/db
Success! Data deleted (if it existed) at: kv/app/db
$
$ vault kv get kv/app/db
No value found at kv/app/db
$
I dati sono quindi eliminati in modo irrevocabile. Un ripristino è possibile solo tramite il ripristino di uno snapshot di Vault.
KV v2: delete soft
In KV v2, un delete è solo un Soft Delete:
$ vault kv delete kvv2/app/db
Success! Data deleted (if it existed) at: kvv2/data/app/db
$
$ vault kv get kvv2/app/db
== Secret Path ==
kvv2/data/app/db
======= Metadata =======
Key Value
--- -----
created_time 2025-07-01T11:10:44.341592593Z
custom_metadata <nil>
deletion_time 2025-07-01T11:13:33.705332433Z
destroyed false
version 4
$
I dati non sono più visibili, ma sono ancora presenti nel sistema. I metadati invece sono ancora disponibili; notate in particolare il nuovo valore per la chiave deletion_time, che in precedenza era n/a.
I secrets eliminati in modo soft possono essere riattivati con undelete o eliminati definitivamente con destroy.
undelete: riattivare versioni eliminate
Se l’eliminazione è avvenuta per errore:
$ vault kv undelete -versions=4 kvv2/app/db Success! Data written to: kvv2/undelete/app/db $ $ vault kv get kvv2/app/db == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T11:10:44.341592593Z custom_metadata <nil> deletion_time n/a destroyed false version 4 ==== Data ==== Key Value --- ----- api myapisecret pass 123 user admin $
I dati diventano nuovamente visibili. Condizione necessaria: non deve essere stato eseguito un destroy.
destroy: eliminazione definitiva di singole versioni
Importante: destroy è irreversibile - anche undelete e rollback non funzionano più. Usatelo solo se siete certi e non confondetelo mai con delete!
Dopo l'esecuzione, non è più possibile alcun ripristino:
$ vault kv destroy -versions=2 kvv2/app/db Success! Data written to: kvv2/destroy/app/db $
È anche possibile eliminare più versioni contemporaneamente:
$ vault kv destroy -versions=1,3,4 kvv2/app/db Success! Data written to: kvv2/destroy/app/db $
Dopo un destroy, il campo destroyed assume il valore true:
$ vault kv get kvv2/app/db == Secret Path == kvv2/data/app/db ======= Metadata ======= Key Value --- ----- created_time 2025-07-01T11:10:44.341592593Z custom_metadata <nil> deletion_time n/a destroyed true version 4 $
Questo campo esiste principalmente per scopi di audit. Un rollback o undelete non è più possibile e funge da prova che è stato eseguito un destroy.
Consigli pratici per il vostro lavoro quotidiano e la vostra strategia KV
1. Usare il controllo di versione
Verificate regolarmente quale versione di KV state utilizzando:
$ vault secrets list --detailed | grep kv kv/ kv kv_11a18e23 system system false replicated false false map[] n/a 4a479e06-6b44-e721-bbff-5ab3df0b7134 n/a v0.21.0+builtin n/a supported kvv2/ kv kv_be4ef9a0 system system false replicated false false map[version:2] n/a 390c2b5f-45cd-6774-ae17-3d643d951401 n/a v0.21.0+builtin n/a supported $
Cercate version:2 nella colonna delle opzioni (la penultima a destra).
2. Usare percorsi strutturati
Sviluppate una convenzione di naming coerente all’interno del percorso della Secret Engine, ad esempio:
apps/ └── payment-service/ ├── prod/ │ ├── db-credentials │ └── api-keys └── dev/ ├── db-credentials └── api-keys
3. Usare patch invece di put
Per aggiornare singoli valori, usate sempre patch invece di put per evitare la perdita di dati.
4. Output JSON per workflow automatizzati
Negli script, utilizzate sempre:
vault kv get -format=json kv/app/db | jq -r '.data.password'
5. Definite una strategia di rollback
Documentate le versioni importanti e preparate procedure di rollback. Ad esempio:
# Salvare la versione attuale $ CURRENT_VERSION=$(vault kv get -format=json kvv2/app/db | jq -r '.data.metadata.version') $ echo $CURRENT_VERSION 5 # In caso di problemi: rollback $ vault kv rollback --version=$((CURRENT_VERSION-1)) kvv2/app/db
Evitare fonti di errore comuni
1. L’errore "write is not merge"
Il problema più comune: sovrascrittura involontaria tramite put invece di patch.
2. Confusione sui percorsi in KV v2
Tenete presente:
- Percorsi diversi in KV v2 a causa di maiuscole/minuscole
- KV v2 aggiunge automaticamente /data/ al percorso interno. La CLI lo astrae, ma nelle chiamate dirette all’API dovete tenerne conto.
3. Confusione tra delete e destroy
- delete = Soft Delete (recuperabile) in KV v2, Hard Delete in KV v1
- destroy = Hard Delete (irrecuperabile) in KV v2
4. Nessun reset del numero di versione dopo delete
Le versioni in KV v2 sono progressive e non vengono mai azzerate, nemmeno dopo un delete.
Conclusione
La Key/Value Secrets Engine è facile da spiegare, ma ricca di sfumature nell’utilizzo pratico. Una comprensione solida della versioning, dei comandi e dei loro effetti collaterali è essenziale per un funzionamento stabile e sicuro.
Chi usa put con attenzione, impiega patch per una gestione mirata e padroneggia rollback in caso di emergenza, ha pieno controllo dello strumento KV Engine. E chi evita destroy finché non è strettamente necessario, si risparmia molti problemi e conserva un’uscita dall’eventuale crisi.
Promemoria pratico: In KV v2 quasi tutto è recuperabile - tranne dopo un destroy.
Sfruttate questa sicurezza, ma strutturate comunque i vostri processi con lungimiranza.