Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
keeper — Gestor Simple y Seguro de Secretos | Kitploit
Herramientas/GitHubGitHub/agberohq/keeper
Autenticación y AutorizaciónHerramientas de Cifrado/DescifradoCriptografíaDetección de Secretos
GitHubagberohq/keeper

keeper

Gestor Simple y Seguro de Secretos

Ver Repositorio
12044hace 5 mesesRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

keeper

Keeper es un almacén criptográfico de secretos para Go. Cifra cargas útiles de bytes arbitrarios en reposo usando derivación de clave Argon2id y cifrado autenticado XChaCha20-Poly1305 (predeterminado), y las almacena en una base de datos bbolt integrada.

Se distribuye como tres cosas que puedes usar de forma independiente:

  • Una biblioteca Go — incrusta un almacén de secretos endurecido directamente en tu proceso, con cuatro niveles de seguridad, aislamiento DEK por bucket y una cadena de auditoría a prueba de manipulaciones.
  • Un manejador HTTP (x/keephandler) — monta los endpoints de keeper en cualquier mux net/http en una sola llamada, con ganchos, protectores y codificadores de respuesta enchufables para control de acceso y registro de auditoría.
  • Una CLI (cmd/keeper) — una interfaz de terminal con una sesión REPL persistente, entrada de secretos sin eco y exposición cero del historial del shell.

Keeper fue diseñado como la capa fundamental de gestión de secretos para el balanceador de carga Agbero pero no tiene dependencia de Agbero y funciona en cualquier proyecto Go.


Contenido

  • Modelo de seguridad
  • Diseño criptográfico
  • Jerarquía de claves
  • Esquema de almacenamiento
  • Cadena de auditoría
  • Integración de Jack
  • x/keepcmd — operaciones CLI reutilizables
  • x/keephandler — manejador HTTP
  • Referencia de la API
  • Catálogo de errores
  • Decisiones de seguridad
  • Dependencias

Modelo de seguridad

Keeper particiona los secretos en buckets. Cada bucket tiene un BucketSecurityPolicy inmutable que gobierna cómo se protege su Clave de Cifrado de Datos (DEK). Hay cuatro niveles disponibles.

Esquemas vs. Niveles de Seguridad

Un esquema es un prefijo URI que agrupa buckets relacionados (vault://, certs://, space:// o cualquier nombre que registres). Un nivel de seguridad es una propiedad de la política del bucket establecida en la creación e inmutable a partir de entonces.

Puedes mezclar niveles de seguridad libremente dentro del mismo esquema. Por ejemplo, vault://system podría ser LevelPasswordOnly (desbloqueado automáticamente al inicio), mientras que vault://admin es LevelAdminWrapped (requiere credencial explícita).

LevelPasswordOnly

El DEK del bucket se deriva de la clave maestra usando HKDF-SHA256 con una cadena de información separada por dominio por bucket (keeper-bucket-dek-v1:scheme:namespace). Todos los buckets LevelPasswordOnly se desbloquean automáticamente cuando se llama a UnlockDatabase con la frase de contraseña maestra correcta. No se requiere ninguna credencial por bucket en tiempo de ejecución. Este nivel es apropiado para secretos que el proceso necesita al inicio sin interacción humana.

LevelAdminWrapped

El bucket tiene un DEK de 32 bytes generado aleatoriamente único para ese bucket. El DEK nunca se almacena en texto plano. Para cada administrador autorizado, se deriva una Clave de Cifrado de Claves (KEK) de HKDF(masterKey‖adminCred, dekSalt) y se usa para envolver el DEK mediante XChaCha20-Poly1305. El bucket es inaccesible hasta que un administrador llama a UnlockBucket con su credencial. La frase de contraseña maestra por sí sola no puede descifrar el bucket. Revocar a un administrador no afecta la copia envuelta de ningún otro administrador.

LevelHSM

El DEK del bucket se genera en el momento de CreateBucket y es envuelto inmediatamente por un HSMProvider proporcionado por el llamante. El proveedor realiza las operaciones de envoltura y desenvolvimiento — keeper nunca maneja el DEK en bruto después de entregarlo al proveedor. UnlockDatabase llama automáticamente al proveedor para desenvolver y sembrar el Envolvente para todos los buckets HSM registrados. La rotación de la clave maestra no recifra estos buckets; el DEK está controlado por el proveedor.

Una implementación SoftHSM integrada respaldada por una clave de envoltura protegida por memguard está disponible en pkg/hsm para entornos de prueba y CI. No la uses en producción.

LevelRemote

Idéntico a LevelHSM en el comportamiento de gestión de claves, pero el HSMProvider es implementado por pkg/remote.Provider — un adaptador HTTPS configurable que delega la envoltura y desenvolvimiento a cualquier servicio KMS remoto sobre TLS. Se proporcionan configuraciones preconstruidas para HashiCorp Vault Transit, AWS KMS y GCP Cloud KMS en pkg/remote. Para uso en producción, configura TLSClientCert y TLSClientKey para habilitar la autenticación TLS mutua.


Diseño criptográfico

Derivación de la clave maestra```

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:~
Se almacena un hash de verificación en la primera derivación:```
verifyHash ← Argon2id(masterKey, "verification", t=1, m=64 MiB, p=4) → 32 bytes

Las llamadas posteriores a DeriveMaster recalculan este hash y lo comparan con crypto/subtle.ConstantTimeCompare. Una discrepancia devuelve ErrInvalidPassphrase.

La sal KDF se almacena sin cifrar por diseño. Debe ser legible antes de UnlockDatabase para derivar la clave maestra — cifrarla con una clave derivada de la maestra sería circular. Una sal KDF no es un secreto; su propósito es la unicidad, no la confidencialidad.

Cifrado de secretos

Cada valor de texto plano se cifra con XChaCha20-Poly1305 usando el DEK del bucket:``` nonce ← random 24 bytes ciphertext ← XChaCha20-Poly1305.Seal(nonce, DEK, plaintext)

root@kitploit:~
The stored record is a msgpack-encoded `Secret` struct containing the ciphertext, encrypted metadata, and schema version. Authentication is implicit: a ciphertext decrypted with the wrong key produces an AEAD authentication failure before any plaintext is returned.

### 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)

La KEK se deriva usando HKDF en lugar de una segunda pasada de Argon2. La clave maestra ya fue producida por un KDF de alto costo; una segunda invocación de Argon2 agregaría cientos de milisegundos de latencia a cada llamada UnlockBucket sin beneficio de seguridad. HKDF-SHA256 opera en aproximadamente un microsegundo.

Defensa en profundidad: Un atacante que comprometa solo la base de datos obtiene el DEK envuelto y la sal HKDF pero no puede derivar la KEK sin la clave maestra. Un atacante que comprometa solo la clave maestra no puede desenvolver ningún DEK LevelAdminWrapped sin conocer también la credencial de administrador.

Cifrado de metadatos — secretos

Los metadatos secretos (tiempo de creación, tiempo de actualización, recuento de accesos, versión) se cifran por separado del texto cifrado:``` metaKey ← HKDF-SHA256(bucketDEK, nil, info="keeper-metadata-v1") → 32 bytes encryptedMeta ← XChaCha20-Poly1305.Seal(nonce, metaKey, msgpack(metadata))

root@kitploit:~
Para los buckets `LevelAdminWrapped`, `LevelHSM` y `LevelRemote`, esto significa que los metadatos son inaccesibles sin la credencial del bucket, evitando que un atacante con acceso de lectura al archivo de la base de datos aprenda patrones de acceso o marcas de tiempo.

**Nota sobre canal lateral de temporización:** XChaCha20-Poly1305 procesa el texto cifrado completo antes de devolver un error de autenticación. La ruta de descifrado de respaldo (nueva DEK derivada → antigua clave maestra como DEK) toma el mismo tiempo de reloj independientemente de qué clave tenga éxito. Ningún canal lateral de temporización filtra el estado de migración de un registro.

### Cifrado de metadatos — políticas, WAL y auditoría

Todos los metadatos estructurales también están cifrados en reposo. Se derivan dos claves de la clave maestra al momento de `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 cifra: los valores de BucketSecurityPolicy y el WAL de rotación.

auditEncKey cifra: los campos Scheme, Namespace y Details de cada evento de auditoría.

Ambas claves se eliminan de la memoria en Lock(). El cifrado utilizado para el cifrado de metadatos es la misma interfaz crypt.Cipher configurable utilizada para los secretos — la elección de cifrado del usuario (AES-256-GCM para FIPS, XChaCha20-Poly1305 por defecto) se aplica automáticamente.

Wire format para todos los blobs de metadatos cifrados:``` nonce (cipher.NonceSize() bytes) || AEAD-ciphertext

root@kitploit:~
### Hashing de claves de bucket de políticas

Las claves de políticas en disco son hashes opacos en lugar de cadenas de texto plano `scheme:namespace`, lo que impide la enumeración fuera de línea de nombres de 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)

El schemeRegistry en memoria sigue usando "scheme:namespace" como su clave — solo cambia la representación en disco.

Autenticación de políticas

Cada registro de política lleva dos etiquetas de integridad escritas atómicamente en una transacción 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:~
Antes de `UnlockDatabase`, solo está disponible el hash SHA-256. Después del desbloqueo,
`loadPolicy` verifica la etiqueta HMAC. `UnlockDatabase` llama a `upgradePolicyHMACs`
para rellenar retroactivamente las etiquetas HMAC en políticas creadas antes de que existiera esta funcionalidad.

### Firma de HMAC de auditoría```
auditKey ← HKDF-SHA256(masterKey, nil, info="keeper-audit-hmac-v1") → 32 bytes
HMAC ← HMAC-SHA256(auditKey, event fields including Seq)

La clave de firma se activa en UnlockDatabase y se borra en Lock. Cuando se rota la clave maestra, Rotate añade un evento de punto de control de rotación de clave a cada cadena de auditoría activa, firmado con la clave de auditoría antigua como el evento final de la época antigua. La historia nunca se reescribe; el punto de control es el puente de confianza entre épocas.


Jerarquía de claves```

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:~
Todas las claves intermedias se ponen a cero inmediatamente después de su uso. La clave maestra nunca se escribe en disco en ninguna forma.

---

## Esquema de almacenamiento

La base de datos subyacente es bbolt. Todos los buckets y su contenido:

| bucket bbolt | Clave | Valor |
|---|---|---|
| `__meta__` | `salt` | msgpack — SaltStore (sin cifrar; dependencia circular si está cifrado) |
| `__meta__` | `verify` | bytes crudos — hash de verificación Argon2id |
| `__meta__` | `rotation_wal` | `nonce‖AEAD(msgpack(RotationWAL))` |
| `__meta__` | `bucket_dek_done` | `"1"` — marcador de finalización de migración DEK |
| `__policies__` | `hex(SHA-256(scheme:ns))[:32]` | `nonce‖AEAD(msgpack(BucketSecurityPolicy))` |
| `__policies__` | `<base>__hash__` | hex SHA-256 de los bytes de política cifrados |
| `__policies__` | `<base>__hmac__` | hex HMAC-SHA256(policyKey, bytes de política cifrados) |
| `__audit__/scheme/namespace` | event UUID | JSON — Evento de auditoría |
| `__audit__/scheme/namespace` | `__chain_index__` | JSON — chainIndex |
| `scheme/namespace` | key string | msgpack — Estructura Secret |

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

Campos del evento de auditoría

La estructura Event utiliza campos de enrutamiento en texto plano separados (Scheme, Namespace) junto con campos de carga útil cifrados (EncScheme, EncNamespace, EncDetails). Las sumas de verificación se calculan sobre los campos de enrutamiento en texto plano y los bytes cifrados de EncDetails, de modo que la integridad de la cadena se puede verificar en tres niveles sin ninguna clave:

NivelTienePuede verificar
PúblicoNadaCadena de suma de verificación SHA-256 (detecta manipulación e inserción)
Titular de clave de auditoría

Ejemplo: Un auditor de cumplimiento recibe solo auditEncKey. Puede verificar toda la cadena HMAC a través de rotaciones de clave y leer todos los detalles del evento, pero no puede descifrar ningún valor secreto. Un observador público con solo el archivo de base de datos aún puede detectar si algún evento fue modificado o insertado después del hecho.

Almacén de sal versionado

La sal KDF se almacena como un SaltStore codificado en msgpack bajo la clave de metadatos salt. Cada rotación de sal añade una nueva SaltEntry y avanza CurrentVersion. Las entradas antiguas se conservan como registro de auditoría. El SaltStore se almacena sin cifrar; consulte Decisiones de seguridad.

WAL de rotación a prueba de fallos

Rotate escribe un WAL antes de tocar cualquier registro. El WAL lleva WrappedOldKey: la clave maestra previa a la rotación cifrada con la nueva clave maestra. Después de un fallo, la frase de contraseña anterior desaparece; WrappedOldKey es la única forma correcta de transportar la clave anterior a través del límite. En UnlockDatabase, cuando hay un WAL presente, la nueva clave maestra descifra WrappedOldKey y la rotación se reanuda desde el cursor del WAL. El propio WAL está cifrado con policyEncKey.


Cadena de auditoría

Cada operación significativa añade un evento a prueba de manipulaciones a la cadena de auditoría del bucket. La integridad de la cadena depende de dos mecanismos.

Suma de verificación. SHA-256 sobre prevChecksum, ID, BucketID, Scheme, Namespace, EncDetails, EventType y Timestamp. Usar Scheme/Namespace como texto plano (siempre conservados junto con las formas cifradas) asegura que la suma de verificación sea estable a través de las rutas de carga. EncDetails proporciona integridad sobre la carga útil cifrada.

HMAC. HMAC-SHA256 sobre todos los campos incluyendo Seq. Un atacante que pueda escribir en la base de datos pero no conozca la clave de auditoría no puede producir un HMAC válido. VerifyIntegrity verifica ambas capas para cada evento.

Límite de época de rotación de clave. En Rotate, se añade un evento de punto de control a cada cadena activa que lleva huellas digitales de las claves de auditoría saliente y entrante. El punto de control está firmado con la clave saliente. Los auditores que posean cualquier clave de época pueden recuperar las claves de época posteriores del campo wrapped_new_key y verificar la continuidad HMAC a través de toda la cadena.

Poda automática. Cuando AuditPruneInterval está establecido en Config, un jack.Scheduler se ejecuta periódicamente y llama a PruneEvents en cada bucket registrado. Los buckets LevelHSM y LevelRemote nunca se podan independientemente de esta configuración.


Integración con Jack

Jack es una biblioteca opcional de supervisión de procesos. Cuando se proporciona un JackConfig a través de WithJack, keeper activa componentes en segundo plano automáticamente:

  • Loop de bloqueo automático: Verifica periódicamente la marca de tiempo de la última actividad y elimina los DEK de los buckets LevelAdminWrapped después de AutoLockInterval. Los buckets LevelPasswordOnly permanecen desbloqueados para que los trabajos en segundo plano continúen sin interrupción. El patrón de bloqueo de escritura única dentro de la tarea del loop elimina la condición de carrera RUnlock→Lock presente en diseños anteriores.
  • Reaper DEK por bucket: Caducidad basada en TTL para DEKs LevelAdminWrapped.
  • Pacientes de monitoreo de salud: Verificación de latencia de lectura de bbolt y verificación de ida y vuelta de cifrado/descifrado, ambos registrados con jack.Doctor.
  • Programador de poda de auditoría: PruneEvents periódico en todos los buckets que no son HSM.
  • Pool de eventos asíncrono: Eventos de auditoría enviados sin bloquear la operación principal.

Si no se proporciona JackConfig, keeper se ejecuta sin estas tareas en segundo plano. Keeper nunca llama a pool.Shutdown — el ciclo de vida del pool pertenece al llamante.


x/keepcmd

x/keepcmd proporciona operaciones reutilizables de keeper desacopladas de cualquier framework CLI. Incorpórelo en su propia aplicación para obtener una gestión de secretos tipada y comprobable sin necesidad de incluir el 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` nunca llama a `prompter` ni lee desde stdin. La resolución de la frase de paso es responsabilidad exclusiva de la persona que llama — esto mantiene el paquete seguro en contextos de servidor sin cabeza.

`NoClose: true` evita que `Commands` llame a `store.Close()` después de cada operación. Úsalo en contextos de REPL/sesión donde un mismo almacén se comparte entre muchas llamadas.

---

## x/keephandler

`x/keephandler` monta endpoints HTTP de keeper en cualquier mux `net/http`. Sin dependencia de enrutador externo — usa enrutamiento por método+patrón de Go 1.22+ con `http.ServeMux` de la 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)
    }),
)

Puntos de conexión

Contrato de Hook

BeforeFunc retorna (allow bool, err error).

  • (true, nil) — permitir que la solicitud proceda.
  • (false, nil) — abortar; el hook ya ha escrito una respuesta completa.
  • (false, err) — abortar; el framework escribe un 500 usando err.Error(). El hook no debe haber escrito nada en w.

Hook.CaptureBody bool controla si AfterFunc recibe el cuerpo de la respuesta. false (predeterminado) cuesta un wrapper ligero statusWriter; true almacena en búfer el cuerpo completo en un bytes.Buffer para el AfterFunc — una asignación por solicitud.

Los hooks se ejecutan en el orden de registro. Múltiples llamadas WithHooks son aditivas. Solo se utiliza el primer hook registrado para un nombre de ruta dado; los registros posteriores para la misma ruta se ignoran.


Referencia de la API

Construcción y desbloqueo```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` realiza lo siguiente en orden:

1. Deriva y activa la clave de firma HMAC de auditoría
2. Deriva y activa la clave HMAC de política
3. Deriva y activa `policyEncKey` y `auditEncKey`
4. Limpia y recarga `schemeRegistry` (descifra todos los blobs de política)
5. Reanuda cualquier WAL de rotación interrumpida
6. Actualiza las etiquetas HMAC de política
7. Inserta todos los DEK de los buckets `LevelPasswordOnly` en el Envelope
8. Inicia tareas en segundo plano (bucle de migración, bloqueo automático, pacientes de salud)

### Bucket LevelPasswordOnly — ciclo de vida 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 de vida 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")

Exportación de clave de auditoría```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:~
### Rotación de claves```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"))

Comparar e intercambiar```go

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

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

Catálogo de errores

Todos los errores centinela funcionan con errors.Is y errors.As. Las trazas de pila se capturan en el momento de la creación mediante github.com/olekukonko/errors.


Decisiones de seguridad

ErrAuthFailed unifica todos los fallos de UnlockBucket (CWE-204 / CVSS 5.3). Tanto un ID de administrador desconocido como una contraseña incorrecta devuelven ErrAuthFailed. Esto previene la enumeración de ID de administrador mediante tiempos o comparación de cadenas de error. RevokeAdmin conserva ErrAdminNotFound porque es una operación administrativa en un almacén ya desbloqueado. La comparación en tiempo constante para la presencia del ID de administrador se omite intencionadamente. Un atacante que pueda medir diferencias de sub-microsegundos en búsquedas de buckets de bbolt necesitaría acceso al sistema de archivos local—en cuyo punto pueden leer el bucket de políticas directamente. El modelo de amenaza asume que el archivo de base de datos puede estar comprometido; la defensa por tiempo contra la enumeración remota es la principal preocupación.

Argon2id domina el tiempo. Argon2id toma 200–500 ms en hardware típico. Las diferencias de comparación posteriores a la derivación son cuatro o más órdenes de magnitud más pequeñas y no son medibles de forma remota. No se aplica ninguna ecualización artificial.

DEK recuperado dentro del límite de la transacción CAS. CompareAndSwapNamespacedFull recupera el DEK del bucket dentro de la transacción de escritura de bbolt, eliminando la ventana en la que un Rotate concurrente podría cambiar el DEK entre la recuperación y el uso.

La frase de contraseña nunca se almacena como una cadena Go en el manejador HTTP. Los tres campos de frase de contraseña (passphrase, new_passphrase) se decodifican desde JSON directamente en []byte mediante extracción de mapa sin procesar, manteniendo el arreglo de cadena subyacente fuera del montón de larga duración. La copia de []byte se pone a cero con wipeBytes después de su uso.

No hay flag --passphrase en la CLI. Los flags aparecen en la salida de ps y en el historial del shell. La CLI acepta la frase de contraseña solo desde la variable de entorno KEEPER_PASSPHRASE o un prompt interactivo sin eco.

Los valores secretos del REPL nunca son visibles. set <key> en el REPL sin un valor en línea usa term.ReadPassword — no aparece en el desplazamiento de la terminal, historial del shell ni ps. Se puede proporcionar un valor en línea (set key value) para datos no sensibles cuando sea conveniente.

SaltStore no está cifrado intencionadamente. La sal KDF debe ser legible antes de UnlockDatabase para derivar la clave maestra. policyEncKey (utilizada para todo el cifrado de otros metadatos) se deriva a su vez de la clave maestra — cifrar la sal con policyEncKey sería circular. Una sal KDF proporciona unicidad, no confidencialidad; no hay valor de seguridad en cifrarla.

Las claves del bucket de políticas están hasheadas, no en texto plano. Las claves de políticas en disco son hex(SHA-256("esquema:espacio de nombres"))[:32] — 128 bits de espacio de claves — en lugar de cadenas legibles. Un atacante fuera de línea que lea el archivo bbolt no puede enumerar nombres de buckets sin descifrar los blobs de políticas.

El cifrado de metadatos utiliza la misma interfaz de cifrado que los secretos. Todas las operaciones de policyEncKey y auditEncKey pasan por s.config.NewCipher(key) — la misma interfaz crypt.Cipher configurada para valores secretos. La elección de cifrado del usuario (AES-256-GCM para FIPS 140, XChaCha20-Poly1305 por defecto) se propaga automáticamente al cifrado de políticas, WAL y auditoría. Ninguna ruta de código hardcodea un algoritmo específico.

Los buckets LevelHSM y LevelRemote se omiten durante la rotación de la clave maestra. reencryptAllWithKey y RotateSalt omiten explícitamente estos buckets. El DEK está controlado por el proveedor; la rotación de la sal maestra no lo afecta.

Rotación segura ante fallos con WrappedOldKey. Rotate escribe un WAL antes de tocar cualquier registro. El WAL lleva WrappedOldKey: la clave maestra previa a la rotación cifrada con la nueva clave maestra. Tras un fallo, UnlockDatabase descifra WrappedOldKey usando la nueva clave verificada y reanuda la rotación desde el cursor.


Dependencias

Descargar herramienta
auditEncKey
Cadena completa + descifrar Scheme/Namespace/Details
OperadorFrase de contraseña maestraTodo
MétodoRutaDescripción
POST{prefix}/unlockDesbloquear el almacén con una frase de contraseña
POST{prefix}/lockBloquear el almacén
GET{prefix}/statusEstado del bloqueo — seguro de consultar sin autenticación
GET{prefix}/keysListar todas las claves secretas
GET{prefix}/keys/{key}Recuperar un valor secreto
POST{prefix}/keysAlmacenar un secreto (JSON o multipart)
DELETE{prefix}/keys/{key}Eliminar un secreto
POST{prefix}/rotateRotar la frase de contraseña maestra
POST{prefix}/rotate/saltRotar la sal KDF
GET{prefix}/backupTransmitir una instantánea de la base de datos
ErrorSignificado
ErrStoreLockedOperación intentada mientras el almacén está bloqueado
ErrInvalidPassphraseFrase de contraseña maestra incorrecta
ErrAuthFailedCualquier fallo de UnlockBucket — no distingue entre contraseña incorrecta e ID de administrador desconocido (CWE-204)
ErrKeyNotFoundLa clave secreta no existe
ErrBucketLockedEl bucket no ha sido desbloqueado
ErrPolicyImmutableSegunda política para un bucket existente
ErrPolicyNotFoundNo hay política para el esquema/espacio de nombres dado
ErrAdminNotFoundID de administrador no está en la política — solo RevokeAdmin
ErrHSMProviderNilBucket HSM/Remoto creado sin un proveedor registrado
ErrCheckLatencyLa latencia de lectura de la base de datos superó DBLatencyThreshold
ErrCASConflictEl valor actual no coincide con el esperado en CompareAndSwap
ErrSecurityDowngradeMovimiento entre buckets de un nivel de seguridad superior a uno inferior
ErrAlreadyUnlockedSe llamó a UnlockDatabase en un almacén ya desbloqueado
ErrMasterRequiredSe llamó a UnlockDatabase con un Master nulo o destruido
ErrChainBrokenFalló la verificación de integridad de la cadena de auditoría
ErrMetadataDecryptNo se pudo descifrar los metadatos cifrados
ErrPolicySignatureFalló la verificación HMAC de la política — el registro fue manipulado
PaquetePropósito
go.etcd.io/bboltAlmacén de clave-valor embebido
golang.org/x/cryptoArgon2id, XChaCha20-Poly1305, HKDF, scrypt
github.com/awnumar/memguardEnclave de claves seguro en memoria (clave maestra, DEKs)
github.com/vmihailenco/msgpack/v5Serialización binaria para secretos y políticas
github.com/olekukonko/jackSupervisión de procesos (integración opcional con Jack)
github.com/olekukonko/llRegistro estructurado
github.com/olekukonko/errorsErrores centinela con trazas de pila
github.com/olekukonko/zeroPuesta a cero segura de slices de bytes
github.com/olekukonko/prompterPrompts de terminal sin eco (solo CLI)
github.com/integrii/flaggyAnálisis de flags de CLI (solo cmd/keeper)
golang.org/x/termDetección de TTY y lectura de contraseñas sin procesar (solo CLI)