Sägetstrasse 18, 3123 Belp, Switzerland +41 79 173 36 84 info@ict.technology

    HashiCorp Vault Deep Dive – Parte 2b: Lavoro pratico con la Key/Value Secrets Engine

    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.