Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
keeper — Semplice gestore sicuro di segreti | Kitploit
Strumenti/GitHubGitHub/agberohq/keeper
Autenticazione e AutorizzazioneStrumenti di Crittografia/DecrittografiaCrittografiaRilevamento Segreti
GitHubagberohq/keeper

keeper

Semplice gestore sicuro di segreti

Vedi Repository
12044 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

keeper

Keeper è uno store crittografico di segreti per Go. Cripta payload arbitrari di byte a riposo utilizzando la derivazione delle chiavi Argon2id e la crittografia autenticata XChaCha20-Poly1305 (predefinita) e li memorizza in un database bbolt embedded.

Viene fornito come tre componenti che puoi usare indipendentemente:

  • Una libreria Go — integra uno store di segreti robusto direttamente nel tuo processo, con quattro livelli di sicurezza, isolamento DEK per bucket e una catena di audit a prova di manomissione.
  • Un handler HTTP (x/keephandler) — monta gli endpoint di keeper su qualsiasi mux net/http con una singola chiamata, con hook, guardie e codificatori di risposta inseribili per il controllo degli accessi e la registrazione di audit.
  • Un'interfaccia a riga di comando (cmd/keeper) — un terminale con sessione REPL persistente, immissione di segreti senza eco e nessuna esposizione della cronologia della shell.

Keeper è stato progettato come livello fondamentale di gestione dei segreti per il load balancer Agbero ma non ha dipendenze da Agbero e funziona in qualsiasi progetto Go.


Sommario

  • Modello di sicurezza
  • Progettazione crittografica
  • Gerarchia delle chiavi
  • Schema di archiviazione
  • Catena di audit
  • Integrazione con Jack
  • x/keepcmd — operazioni CLI riutilizzabili
  • x/keephandler — handler HTTP
  • Riferimento API
  • Catalogo degli errori
  • Decisioni di sicurezza
  • Dipendenze

Modello di sicurezza

Keeper partiziona i segreti in bucket. Ogni bucket ha un BucketSecurityPolicy immutabile che regola come viene protetta la sua Chiave di Crittografia dei Dati (DEK). Sono disponibili quattro livelli.

Schemi vs. Livelli di sicurezza

Uno schema è un prefisso URI che raggruppa bucket correlati (vault://, certs://, space:// o qualsiasi nome che registri). Un livello di sicurezza è una proprietà della policy del bucket impostata al momento della creazione e immutabile successivamente.

Puoi mescolare liberamente i livelli di sicurezza all'interno dello stesso schema. Ad esempio, vault://system potrebbe essere LevelPasswordOnly (sbloccato automaticamente all'avvio), mentre vault://admin è LevelAdminWrapped (richiede credenziali esplicite).

LevelPasswordOnly

La DEK del bucket è derivata dalla chiave master usando HKDF-SHA256 con una stringa info separata per dominio per bucket (keeper-bucket-dek-v1:scheme:namespace). Tutti i bucket LevelPasswordOnly vengono sbloccati automaticamente quando viene chiamato UnlockDatabase con la corretta passphrase master. Nessuna credenziale per bucket è richiesta in fase di esecuzione. Questo livello è appropriato per segreti di cui il processo ha bisogno all'avvio senza interazione umana.

LevelAdminWrapped

Il bucket ha una DEK casuale di 32 byte univoca per quel bucket. La DEK non viene mai memorizzata in chiaro. Per ogni amministratore autorizzato, una Chiave di Crittografia delle Chiavi (KEK) viene derivata da HKDF(masterKey‖adminCred, dekSalt) e usata per avvolgere la DEK tramite XChaCha20-Poly1305. Il bucket è inaccessibile finché un amministratore non chiama UnlockBucket con la propria credenziale. La sola passphrase master non può decifrare il bucket. La revoca di un amministratore non influisce sulla copia avvolta di nessun altro amministratore.

LevelHSM

La DEK del bucket viene generata al momento di CreateBucket e immediatamente avvolta da un HSMProvider fornito dal chiamante. Il provider esegue le operazioni di avvolgimento e disavvolgimento — keeper non gestisce mai la DEK grezza dopo averla consegnata al provider. UnlockDatabase chiama automaticamente il provider per disavvolgere e popolare l'Envelope per tutti i bucket HSM registrati. La rotazione della chiave master non re-cripta questi bucket; la DEK è controllata dal provider.

Un'implementazione SoftHSM integrata, basata su una chiave di avvolgimento protetta da memguard, è disponibile in pkg/hsm per test e ambienti CI. Non utilizzarla in produzione.

LevelRemote

Identico a LevelHSM per quanto riguarda la gestione delle chiavi, ma HSMProvider è implementato da pkg/remote.Provider — un adattatore HTTPS configurabile che delega le operazioni di avvolgimento e disavvolgimento a qualsiasi servizio KMS remoto su TLS. Le configurazioni predefinite per HashiCorp Vault Transit, AWS KMS e GCP Cloud KMS sono fornite in pkg/remote. Per uso in produzione, configura TLSClientCert e TLSClientKey per abilitare l'autenticazione TLS reciproca.


Progettazione crittografica

Derivazione della chiave master```

salt ← random 32 bytes, generated once, stored as a versioned SaltStore (unencrypted) masterKey ← Argon2id(passphrase, salt, t=3, m=64 MiB, p=4) → 32 bytes

root@kitploit:~
Un hash di verifica viene memorizzato alla prima derivazione:```
verifyHash ← Argon2id(masterKey, "verification", t=1, m=64 MiB, p=4) → 32 bytes

Subsequent DeriveMaster calls recompute this hash and compare it with crypto/subtle.ConstantTimeCompare. A mismatch returns ErrInvalidPassphrase.

The KDF salt is stored unencrypted by design. It must be readable before UnlockDatabase to derive the master key — encrypting it with a key derived from the master would be circular. A KDF salt is not a secret; its purpose is uniqueness, not confidentiality.

Crittografia dei segreti

Ogni valore in chiaro viene crittografato con XChaCha20-Poly1305 utilizzando la DEK del bucket:``` nonce ← random 24 bytes ciphertext ← XChaCha20-Poly1305.Seal(nonce, DEK, plaintext)

root@kitploit:~
Il record memorizzato è un struct `Secret` codificato in msgpack contenente il testo cifrato, i metadati cifrati e la versione dello schema. L'autenticazione è implicita: un testo cifrato decifrato con la chiave sbagliata produce un fallimento di autenticazione AEAD prima che venga restituito qualsiasi testo in chiaro.

### KEK derivation — LevelAdminWrapped```
salt ← random 32 bytes, generated at bucket creation, stored in policy
ikm ← masterKey ‖ adminCredential
KEK ← HKDF-SHA256(ikm, salt, info="keeper-kek-v1") → 32 bytes
wrappedDEK ← XChaCha20-Poly1305.Seal(nonce, KEK, DEK)

Il KEK è derivato usando HKDF invece di un secondo passaggio di Argon2. La chiave master era già stata prodotta da un KDF ad alto costo; una seconda invocazione di Argon2 aggiungerebbe centinaia di millisecondi di latenza a ogni chiamata di UnlockBucket senza alcun vantaggio in termini di sicurezza. HKDF-SHA256 opera in circa un microsecondo.

Difesa in profondità: Un attaccante che compromette solo il database ottiene il DEK avvolto e il salt di HKDF, ma non può derivare il KEK senza la chiave master. Un attaccante che compromette solo la chiave master non può disavvolgere alcun DEK LevelAdminWrapped senza conoscere anche le credenziali dell'amministratore.

Crittografia dei metadati — segreti

I metadati segreti (tempo di creazione, tempo di aggiornamento, conteggio degli accessi, versione) sono crittografati separatamente dal testo cifrato:``` metaKey ← HKDF-SHA256(bucketDEK, nil, info="keeper-metadata-v1") → 32 bytes encryptedMeta ← XChaCha20-Poly1305.Seal(nonce, metaKey, msgpack(metadata))

root@kitploit:~
Per i bucket `LevelAdminWrapped`, `LevelHSM` e `LevelRemote`, ciò significa
che i metadati sono inaccessibili senza le credenziali del bucket, impedendo a un attaccante
con accesso in lettura al file del database di apprendere pattern di accesso o
timestamp.

**Nota sul canale laterale temporale:** XChaCha20-Poly1305 elabora l'intero testo cifrato
prima di restituire un errore di autenticazione. Il percorso di decifratura di fallback (nuovo
DEK derivato → vecchia chiave master come DEK) richiede lo stesso tempo a orologio
indipendentemente da quale chiave abbia successo. Nessun canale laterale temporale rivela lo stato di migrazione di un record.

### Crittografia dei metadati — policy, WAL e audit

Tutti i metadati strutturali sono anch'essi crittografati a riposo. Due chiavi vengono derivate
dalla chiave master al momento di `UnlockDatabase`:```
policyEncKey ← HKDF-SHA256(masterKey, nil, info="keeper-policy-enc-v1") → 32 bytes
auditEncKey  ← HKDF-SHA256(masterKey, nil, info="keeper-audit-enc-v1")  → 32 bytes

policyEncKey crittografa: i valori BucketSecurityPolicy e il WAL di rotazione.

auditEncKey crittografa: i campi Scheme, Namespace e Details di ogni evento di audit.

Entrambe le chiavi vengono cancellate dalla memoria al Lock(). Il cifrario utilizzato per la crittografia dei metadati è la stessa interfaccia configurabile crypt.Cipher usata per i segreti — la scelta del cifrario da parte dell'utente (AES-256-GCM per FIPS, XChaCha20-Poly1305 di default) viene applicata automaticamente.

Formato wire per tutti i blob di metadati crittografati:``` nonce (cipher.NonceSize() bytes) || AEAD-ciphertext

root@kitploit:~
### Hashing delle chiavi dei bucket di policy

Le chiavi delle policy su disco sono hash opachi invece di stringhe in chiaro `scheme:namespace`, impedendo l'enumerazione offline dei nomi dei bucket:```
base ← hex(SHA-256("scheme:namespace"))[:32]   // 32 hex chars = 128-bit key space
_policies/<base>          → encrypted BucketSecurityPolicy
_policies/<base>__hash__  → SHA-256(encrypted policy bytes)
_policies/<base>__hmac__  → HMAC-SHA256(policyKey, encrypted policy bytes)

Il schemeRegistry in memoria continua a utilizzare "scheme:namespace" come chiave — solo la rappresentazione su disco cambia.

Autenticazione delle policy

Ogni record di policy porta due tag di integrità scritti atomicamente in una singola transazione bbolt:``` hash ← SHA-256(encryptedPolicyBytes) — unauthenticated, pre-unlock integrity policyKey ← HKDF-SHA256(masterKey, nil, info="keeper-policy-hmac-v1") → 32 bytes hmac ← HMAC-SHA256(policyKey, encryptedPolicyBytes) — authenticated, post-unlock integrity

root@kitploit:~
Before `UnlockDatabase`, solo l'hash SHA-256 è disponibile. Dopo lo sblocco,
`loadPolicy` verifica il tag HMAC. `UnlockDatabase` chiama `upgradePolicyHMACs`
per riempire retroattivamente i tag HMAC sulle policy create prima dell'esistenza di questa funzionalità.

### Audit della firma HMAC```
auditKey ← HKDF-SHA256(masterKey, nil, info="keeper-audit-hmac-v1") → 32 bytes
HMAC ← HMAC-SHA256(auditKey, event fields including Seq)

La chiave di firma viene attivata in UnlockDatabase e cancellata in Lock. Quando la chiave master viene ruotata, Rotate aggiunge un evento di checkpoint di rotazione della chiave a ogni catena di audit attiva, firmato con la vecchia chiave di audit come ultimo evento della vecchia epoca. La cronologia non viene mai riscritta; il checkpoint è il ponte di fiducia tra le epoche.


Gerarchia delle chiavi```

passphrase │ └─ Argon2id(salt) ──→ masterKey (32 bytes, memguard Enclave) │ ├─ HKDF("keeper-audit-hmac-v1") ──→ auditKey (HMAC signing) ├─ HKDF("keeper-audit-enc-v1") ──→ auditEncKey (audit field encryption) ├─ HKDF("keeper-policy-hmac-v1") ──→ policyKey (policy HMAC) ├─ HKDF("keeper-policy-enc-v1") ──→ policyEncKey (policy/WAL encryption) │ ├─ [LevelPasswordOnly] │ └─ HKDF("keeper-bucket-dek-v1:scheme:ns") ──→ DEK │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ ├─ [LevelAdminWrapped] │ ├─ random 32 bytes ──→ DEK │ │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ │ │ └─ HKDF("keeper-kek-v1", masterKey‖adminCred, dekSalt) │ └─ KEK │ └─ XChaCha20-Poly1305(KEK, DEK) ──→ wrappedDEK │ └─ [LevelHSM / LevelRemote] ├─ random 32 bytes ──→ DEK │ └─ HKDF("keeper-metadata-v1") ──→ metaKey │ └─ HSMProvider.WrapDEK(DEK) ──→ wrappedDEK (stored; provider controls the wrapping key)

root@kitploit:~
Tutte le chiavi intermedie vengono azzerate immediatamente dopo l'uso. La chiave principale non viene mai scritta su disco in alcuna forma.

---

## Schema di archiviazione

Il database sottostante è bbolt. Tutti i bucket e i loro contenuti:

| bbolt bucket | Key | Value |
|---|---|---|
| `__meta__` | `salt` | msgpack — SaltStore (non crittografato; dipendenza circolare se crittografato) |
| `__meta__` | `verify` | byte grezzi — hash di verifica Argon2id |
| `__meta__` | `rotation_wal` | `nonce‖AEAD(msgpack(RotationWAL))` |
| `__meta__` | `bucket_dek_done` | `"1"` — indicatore di completamento migrazione DEK |
| `__policies__` | `hex(SHA-256(scheme:ns))[:32]` | `nonce‖AEAD(msgpack(BucketSecurityPolicy))` |
| `__policies__` | `<base>__hash__` | hex SHA-256 dei byte della policy crittografata |
| `__policies__` | `<base>__hmac__` | hex HMAC-SHA256(policyKey, encrypted policy bytes) |
| `__audit__/scheme/namespace` | event UUID | JSON — audit Event |
| `__audit__/scheme/namespace` | `__chain_index__` | JSON — chainIndex |
| `scheme/namespace` | key string | msgpack — Secret struct |

### Secret struct (msgpack)```go
type Secret struct {
    Ciphertext    []byte `msgpack:"ct"`
    EncryptedMeta []byte `msgpack:"em,omitempty"`
    SchemaVersion int    `msgpack:"sv"`  // always 1
}

Campi dell'evento di audit

La struct Event utilizza campi di routing in chiaro separati (Scheme, Namespace) insieme a campi del payload crittografati (EncScheme, EncNamespace, EncDetails). I checksum vengono calcolati sui campi di routing in chiaro e sui byte crittografati di EncDetails, in modo che l'integrità della catena possa essere verificata su tre livelli senza alcuna chiave:

LivelloHaPuò verificare
PubblicoNienteCatena di checksum SHA-256 (rileva manomissioni e inserimenti)
Detentore della chiave di audit

Esempio: Un revisore della conformità riceve solo auditEncKey. Può verificare l'intera catena HMAC attraverso le rotazioni delle chiavi e leggere tutti i dettagli dell'evento, ma non può decifrare alcun valore segreto. Un osservatore pubblico con solo il file del database può comunque rilevare se un evento è stato modificato o inserito successivamente.

Archivio dei salt versionato

Il salt KDF è memorizzato come SaltStore codificato in msgpack sotto la chiave dei metadati salt. Ogni rotazione del salt aggiunge una nuova SaltEntry e incrementa CurrentVersion. Le voci vecchie vengono conservate come traccia di audit. La SaltStore viene memorizzata non crittografata — vedi Decisioni di sicurezza.

WAL di rotazione resistente agli arresti

Rotate scrive un WAL prima di toccare qualsiasi record. Il WAL trasporta WrappedOldKey: la chiave principale pre-rotazione crittografata con la nuova chiave principale. Dopo un arresto, la vecchia frase di accesso non è più disponibile; WrappedOldKey è l'unico modo corretto per trasportare la vecchia chiave attraverso il confine. In UnlockDatabase, quando è presente un WAL, la nuova chiave principale decifra WrappedOldKey e la rotazione riprende dal cursore del WAL. Il WAL stesso è crittografato con policyEncKey.


Catena di audit

Ogni operazione significativa aggiunge un evento a prova di manomissione alla catena di audit del bucket. L'integrità della catena dipende da due meccanismi.

Checksum. SHA-256 su prevChecksum, ID, BucketID, Scheme, Namespace, EncDetails, EventType e Timestamp. L'uso di Scheme/Namespace come testo in chiaro (sempre conservati insieme alle forme crittografate) garantisce che il checksum sia stabile attraverso i percorsi di caricamento. EncDetails fornisce integrità sul payload crittografato.

HMAC. HMAC-SHA256 su tutti i campi inclusi Seq. Un attaccante che può scrivere nel database ma non conosce la chiave di audit non può produrre un HMAC valido. VerifyIntegrity controlla entrambi i livelli per ogni evento.

Confine di epoca della rotazione delle chiavi. In Rotate, un evento checkpoint viene aggiunto a ogni catena attiva che trasporta le impronte digitali sia della chiave di audit uscente che di quella entrante. Il checkpoint viene firmato con la chiave uscente. I revisori che possiedono una qualsiasi chiave di epoca possono recuperare le chiavi di epoca successive dal campo wrapped_new_key e verificare la continuità HMAC attraverso l'intera catena.

Pulizia automatica. Quando AuditPruneInterval è impostato in Config, un jack.Scheduler viene eseguito periodicamente e chiama PruneEvents su ogni bucket registrato. I bucket LevelHSM e LevelRemote non vengono mai puliti indipendentemente da questa impostazione.


Integrazione Jack

Jack è una libreria opzionale di supervisione dei processi. Quando viene fornito un JackConfig tramite WithJack, keeper attiva automaticamente i componenti in background:

  • Auto-lock Looper: Controlla periodicamente il timestamp dell'ultima attività e rilascia i DEK dei bucket LevelAdminWrapped dopo AutoLockInterval. I bucket LevelPasswordOnly rimangono sbloccati in modo che i job in background continuino ininterrotti. Il pattern di singolo blocco di scrittura all'interno dell'attività del looper elimina la condizione di competizione RUnlock→Lock presente nei progetti precedenti.
  • Per-bucket DEK Reaper: Scadenza basata su TTL per i DEK LevelAdminWrapped.
  • Health monitoring patients: Controllo della latenza di lettura di bbolt e verifica del round-trip di crittografia/decifratura, entrambi registrati con jack.Doctor.
  • Audit prune scheduler: PruneEvents periodico su tutti i bucket non HSM.
  • Async event Pool: Eventi di audit inviati senza bloccare l'operazione principale.

Se JackConfig non viene fornito, keeper viene eseguito senza queste attività in background. Keeper non chiama mai pool.Shutdown — il ciclo di vita del pool appartiene al chiamante.


x/keepcmd

x/keepcmd fornisce operazioni di keeper riutilizzabili disaccoppiate da qualsiasi framework CLI. Incorporalo nella tua applicazione per ottenere una gestione dei segreti tipizzata e testabile senza dover includere il binario CLI.```go import "github.com/agberohq/keeper/x/keepcmd"

cmds := &keepcmd.Commands{ Store: func() (*keeper.Keeper, error) { return security.KeeperOpen(cfg) // your own config }, Out: keepcmd.PlainOutput{}, NoClose: false, // true in REPL / session contexts }

cmds.List() // all keys: scheme://namespace/key cmds.List("vault") // all keys in scheme vault cmds.List("vault", "system") // all keys in vault://system cmds.Get("vault://system/jwt_secret") cmds.Set("vault://system/jwt_secret", "newsecret", keepcmd.SetOptions{}) cmds.Rotate(newPassphraseBytes) // caller resolved the passphrase — no prompter dependency cmds.RotateSalt(currentPassBytes) // same

root@kitploit:~
`keepcmd` non chiama mai `prompter` né legge da stdin. La risoluzione della passphrase
è interamente responsabilità del chiamante — ciò mantiene il pacchetto sicuro in
contesti di server headless.

`NoClose: true` impedisce a `Commands` di chiamare `store.Close()` dopo ogni
operazione. Utilizzalo in contesti REPL / sessione in cui un singolo store è condiviso
tra molte chiamate.

---

## x/keephandler

`x/keephandler` monta gli endpoint HTTP keeper su qualsiasi mux `net/http`. Nessuna
dipendenza esterna da router — utilizza il routing metodo+pattern di Go 1.22+ con
`http.ServeMux` della stdlib.```go
import "github.com/agberohq/keeper/x/keephandler"

keephandler.Mount(mux, store,
    keephandler.WithPrefix("/api/keeper"),
    keephandler.WithGuard(func(w http.ResponseWriter, r *http.Request, route string) bool {
        if !acl.Allow(r.Header.Get("X-Principal"), route) {
            http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
            return false
        }
        return true
    }),
    keephandler.WithHooks(
        keephandler.Hook{
            Route:       keephandler.RouteGet,
            CaptureBody: false,
            After: func(r *http.Request, status int, _ []byte) {
                audit.Log(r.Context(), route, status)
            },
        },
    ),
    keephandler.WithEncoder(func(w http.ResponseWriter, route string, status int, data any) {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(status)
        json.NewEncoder(w).Encode(map[string]any{
            "ok":    status < 400,
            "route": route,
            "data":  data,
        })
    }),
    keephandler.WithRoutes(func(m *http.ServeMux) {
        m.HandleFunc("POST /api/keeper/totp/{user}", myTOTPHandler)
    }),
)

Endpoints

Contratto dell'hook

BeforeFunc restituisce (allow bool, err error).

  • (true, nil) — lascia proseguire la richiesta.
  • (false, nil) — interrompi; l'hook ha già scritto una risposta completa.
  • (false, err) — interrompi; il framework scrive un 500 usando err.Error(). L'hook non deve aver scritto nulla su w.

Hook.CaptureBody bool controlla se AfterFunc riceve il corpo della risposta. false (default) costa un wrapper leggero statusWriter; true bufferizza l'intero corpo in un bytes.Buffer per AfterFunc — una allocazione per richiesta.

Gli hook vengono eseguiti nell'ordine di registrazione. Chiamate multiple a WithHooks sono additive. Viene utilizzato solo il primo hook registrato per un dato nome di route—le registrazioni successive per la stessa route vengono ignorate.


Riferimento API

Costruzione e sblocco```go

store, err := keeper.New(keeper.Config{ DBPath: "/var/lib/agbero/keeper.db", AutoLockInterval: 30 * time.Minute, EnableAudit: true, AuditPruneInterval: 24 * time.Hour, AuditPruneKeepLastN: 10_000, AuditPruneOlderThan: 90 * 24 * time.Hour, DBLatencyThreshold: 200 * time.Millisecond, Logger: logger, }, keeper.WithJack(keeper.JackConfig{ Pool: jackPool, Shutdown: jackShutdown, })) defer store.Close()

// Shorthand (wraps DeriveMaster + UnlockDatabase): if err := store.Unlock([]byte(os.Getenv("KEEPER_PASSPHRASE"))); err != nil { log.Fatal(err) // ErrInvalidPassphrase on wrong passphrase }

root@kitploit:~
`UnlockDatabase` esegue nell'ordine:

1. Deriva e attiva la chiave di firma HMAC di audit
2. Deriva e attiva la chiave HMAC delle policy
3. Deriva e attiva `policyEncKey` e `auditEncKey`
4. Cancella e ricarica `schemeRegistry` (decifra tutti i blob delle policy)
5. Riprende qualsiasi WAL di rotazione interrotto
6. Aggiorna i tag HMAC delle policy
7. Semina tutti i DEK dei bucket `LevelPasswordOnly` nell'Envelope
8. Avvia le attività in background (loop di migrazione, blocco automatico, pazienti di health)

### Bucket LevelPasswordOnly — ciclo di vita completo```go
err := store.CreateBucket("vault", "system", keeper.LevelPasswordOnly, "init")

store.Set("vault://system/jwt_secret", []byte("supersecret"))
val, err := store.Get("vault://system/jwt_secret")

// Namespaced convenience wrappers
store.SetNamespaced("admin", "jwt_secret", secretBytes)
val, err = store.GetNamespaced("admin", "jwt_secret")

LevelAdminWrapped bucket — ciclo di vita completo```go

err := store.CreateBucket("finance", "payroll", keeper.LevelAdminWrapped, "ops-team") err = store.AddAdminToPolicy("finance", "payroll", "alice", []byte("alicepass"))

store.SetNamespacedFull("finance", "payroll", "salary_key", []byte("AES256..."))

store.LockBucket("finance", "payroll") err = store.UnlockBucket("finance", "payroll", "bob", []byte("bobpass")) // ErrAuthFailed — does not distinguish wrong password from unknown admin (CWE-204)

err = store.RevokeAdmin("finance", "payroll", "alice") err = store.RotateAdminWrappedDEK("finance", "payroll", "bob", []byte("bobpass"))

needs, err := store.NeedsAdminRekey("finance", "payroll")

root@kitploit:~
### LevelHSM / LevelRemote buckets```go
import (
    "github.com/agberohq/keeper/pkg/hsm"
    "github.com/agberohq/keeper/pkg/remote"
)

// SoftHSM — testing only
provider, _ := hsm.NewSoftHSM()
store.RegisterHSMProvider("secure", "keys", provider)
store.CreateBucket("secure", "keys", keeper.LevelHSM, "ops")

// Vault Transit
cfg := remote.VaultTransit("https://vault.corp:8200", vaultToken, "my-key")
cfg.TLSClientCert = "/etc/keeper/client.crt"
cfg.TLSClientKey  = "/etc/keeper/client.key"
provider, _ = remote.New(cfg)
store.RegisterHSMProvider("tenant", "secrets", provider)
store.CreateBucket("tenant", "secrets", keeper.LevelRemote, "ops")

Esportazione chiave di audit```go

// Export the audit encryption key to allow a third-party auditor to decrypt // event details without access to the master passphrase. auditKey, err := store.ExportAuditKey() defer zero.Bytes(auditKey)

events, err := auditStore.LoadChain("vault", "system", auditKey)

root@kitploit:~
### Rotazione delle chiavi```go
// Rotate passphrase — crash-safe WAL, resumes on next Unlock if interrupted
store.Rotate([]byte("new-passphrase"))

// Rotate KDF salt — re-derives master key, re-encrypts LevelPasswordOnly
store.RotateSalt([]byte("current-passphrase"))

Confronta e scambia```go

err := store.CompareAndSwapNamespacedFull("vault", "system", "counter", []byte("old"), []byte("new")) // ErrCASConflict if current value does not match old

root@kitploit:~
### Copia di sicurezza```go
f, _ := os.Create("keeper.db.bak")
info, err := store.Backup(f)
// info.Bytes, info.Timestamp, info.DBPath

Catalogo degli errori

Tutti gli errori sentinella funzionano con errors.Is e errors.As. Le tracce dello stack vengono acquisite al momento della creazione tramite github.com/olekukonko/errors.


Decisioni di sicurezza

ErrAuthFailed unifica tutti i fallimenti di UnlockBucket (CWE-204 / CVSS 5.3). Sia un ID amministratore sconosciuto sia una password errata restituiscono ErrAuthFailed. Questo impedisce l'enumerazione degli ID amministratore tramite timing o confronto di stringhe di errore. RevokeAdmin mantiene ErrAdminNotFound perché è un'operazione amministrativa su uno store già sbloccato. Il confronto in tempo costante per la presenza dell'ID amministratore è stato intenzionalmente omesso. Un attaccante in grado di misurare differenze sub-microsecondo nelle ricerche nei bucket bbolt avrebbe bisogno dell'accesso al filesystem locale—a quel punto può leggere direttamente il bucket delle politiche. Il modello di minaccia presuppone che il file del database possa essere compromesso; la difesa contro l'enumerazione remota tramite timing è la preoccupazione principale.

Argon2id domina i tempi. Argon2id impiega 200-500 ms su hardware tipico. Le differenze di confronto post-derivazione sono di quattro o più ordini di grandezza più piccole e non sono misurabili da remoto. Non viene applicata alcuna equalizzazione artificiale.

DEK recuperato all'interno del confine della transazione CAS. CompareAndSwapNamespacedFull recupera il DEK del bucket all'interno della transazione di scrittura bbolt, eliminando la finestra in cui una Rotate concorrente potrebbe cambiare il DEK tra il recupero e l'uso.

La frase di accesso non viene mai memorizzata come stringa Go nell'handler HTTP. Tutti e tre i campi della frase di accesso (passphrase, new_passphrase) vengono decodificati da JSON direttamente in []byte tramite estrazione della mappa grezza, mantenendo l'array di backing della stringa fuori dall'heap di lunga durata. La copia []byte viene azzerata con wipeBytes dopo l'uso.

Nessun flag --passphrase nella CLI. I flag compaiono nell'output di ps e nella cronologia della shell. La CLI accetta la frase di accesso solo dalla variabile d'ambiente KEEPER_PASSPHRASE o da un prompt interattivo senza echo.

I valori segreti nella REPL non sono mai visibili. set <key> nella REPL senza un valore in linea utilizza term.ReadPassword — non compare nel rollback del terminale, nella cronologia della shell o in ps. Un valore in linea (set key value) può essere fornito per dati non sensibili quando è comodo.

SaltStore è intenzionalmente non crittografato. Il sale KDF deve essere leggibile prima di UnlockDatabase per derivare la chiave master. policyEncKey (usata per tutta la crittografia degli altri metadati) è essa stessa derivata dalla chiave master — crittografare il sale con policyEncKey sarebbe circolare. Un sale KDF fornisce unicità, non riservatezza; non c'è valore di sicurezza nel crittografarlo.

Le chiavi del bucket delle politiche sono hash, non testo in chiaro. Le chiavi delle politiche su disco sono hex(SHA-256("scheme:namespace"))[:32] — 128 bit di spazio delle chiavi — piuttosto che stringhe leggibili. Un attaccante offline che legge il file bbolt non può enumerare i nomi dei bucket senza decifrare i blob delle politiche.

La crittografia dei metadati utilizza la stessa interfaccia cipher delle chiavi segrete. Tutte le operazioni su policyEncKey e auditEncKey passano attraverso s.config.NewCipher(key) — la stessa interfaccia crypt.Cipher configurata per i valori segreti. La scelta del cipher dell'utente (AES-256-GCM per FIPS 140, XChaCha20-Poly1305 per impostazione predefinita) viene applicata automaticamente alla crittografia delle politiche, del WAL e dell'audit. Nessun percorso di codice codifica in modo rigido un algoritmo specifico.

Bucket LevelHSM e LevelRemote saltati durante la rotazione della chiave master. reencryptAllWithKey e RotateSalt saltano esplicitamente questi bucket. Il DEK è controllato dal provider; la rotazione del sale master non lo influenza.

Rotazione crash-safe con WrappedOldKey. Rotate scrive un WAL prima di toccare qualsiasi record. Il WAL porta WrappedOldKey: la chiave master pre-rotazione crittografata con la nuova chiave master. Dopo un crash, UnlockDatabase decifra WrappedOldKey usando la nuova chiave verificata e riprende la rotazione dal cursore.


Dipendenze

Scarica lo strumento
auditEncKey
Catena completa + decifratura di Scheme/Namespace/Details
OperatoreFrase di accesso principaleTutto
MetodoPercorsoDescrizione
POST{prefix}/unlockSblocca il negozio con una passphrase
POST{prefix}/lockBlocca il negozio
GET{prefix}/statusStato di blocco — sicuro da interrogare senza autenticazione
GET{prefix}/keysElenca tutte le chiavi segrete
GET{prefix}/keys/{key}Recupera un valore segreto
POST{prefix}/keysMemorizza un segreto (JSON o multipart)
DELETE{prefix}/keys/{key}Elimina un segreto
POST{prefix}/rotateRuota la passphrase principale
POST{prefix}/rotate/saltRuota il sale KDF
GET{prefix}/backupStream di un'istantanea del database
ErroreSignificato
ErrStoreLockedOperazione tentata mentre lo store è bloccato
ErrInvalidPassphraseFrase di accesso master errata
ErrAuthFailedQualsiasi fallimento di UnlockBucket — non distingue tra password errata e ID amministratore sconosciuto (CWE-204)
ErrKeyNotFoundLa chiave segreta non esiste
ErrBucketLockedIl bucket non è stato sbloccato
ErrPolicyImmutableSeconda politica per un bucket esistente
ErrPolicyNotFoundNessuna politica per lo scheme/namespace indicato
ErrAdminNotFoundID amministratore non presente nella politica — solo RevokeAdmin
ErrHSMProviderNilBucket HSM/Remoto creato senza un provider registrato
ErrCheckLatencyLatenza di lettura DB superata la soglia DBLatencyThreshold
ErrCASConflictIl valore corrente non corrisponde a quello atteso in CompareAndSwap
ErrSecurityDowngradeSpostamento tra bucket da un livello di sicurezza superiore a uno inferiore
ErrAlreadyUnlockedUnlockDatabase chiamato su uno store già sbloccato
ErrMasterRequiredUnlockDatabase chiamato con un Master nil o distrutto
ErrChainBrokenVerifica di integrità della catena di audit fallita
ErrMetadataDecryptImpossibile decifrare i metadati crittografati
ErrPolicySignatureVerifica HMAC della politica fallita — il record è stato manomesso
PacchettoScopo
go.etcd.io/bboltKey-value store embedded
golang.org/x/cryptoArgon2id, XChaCha20-Poly1305, HKDF, scrypt
github.com/awnumar/memguardEnclave di chiavi memory-safe (chiave master, DEK)
github.com/vmihailenco/msgpack/v5Serializzazione binaria per segreti e politiche
github.com/olekukonko/jackSupervisione dei processi (integrazione Jack opzionale)
github.com/olekukonko/llLog strutturati
github.com/olekukonko/errorsErrori sentinella con tracce dello stack
github.com/olekukonko/zeroAzzeramento sicuro di byte-slice
github.com/olekukonko/prompterPrompt terminale senza echo (solo CLI)
github.com/integrii/flaggyParsing dei flag CLI (solo cmd/keeper)
golang.org/x/termRilevamento TTY e lettura raw della password (solo CLI)