Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
keeper — 简单安全的机密保管工具 | Kitploit
工具/GitHubGitHub/agberohq/keeper
身份验证与授权加密/解密工具密码学秘密检测
GitHubagberohq/keeper

keeper

简单安全的机密保管工具

查看仓库
120445个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

keeper

Keeper 是一个用于 Go 的加密秘密存储器。它使用 Argon2id 密钥派生和 XChaCha20-Poly1305(默认)认证加密来加密静态的任意字节负载,并将它们存储在嵌入式 bbolt 数据库中。

它打包成三个独立可用的部分:

  • 一个 Go 库 — 将经过加固的秘密存储器直接嵌入到您的进程中,具有四个安全级别、每个桶的 DEK 隔离以及防篡改审计链。
  • 一个 HTTP 处理器 (x/keephandler) — 通过一次调用将 keeper 端点挂载到任何 net/http 多路复用器上,并带有可插拔的钩子、守卫和响应编码器,用于访问控制和审计日志记录。
  • 一个 CLI (cmd/keeper) — 一个终端界面,具有持久 REPL 会话、无回显秘密输入和零 shell 历史暴露。

Keeper 被设计为 Agbero 负载均衡器的基础秘密管理层,但独立于 Agbero,适用于任何 Go 项目。


目录

  • 安全模型
  • 密码学设计
  • 密钥层次结构
  • 存储模式
  • 审计链
  • Jack 集成
  • x/keepcmd — 可复用的 CLI 操作
  • x/keephandler — HTTP 处理器
  • API 参考
  • 错误目录
  • 安全决策
  • 依赖项

安全模型

Keeper 将秘密分区到桶中。每个桶都有一个不可变的 BucketSecurityPolicy,用于管理其数据加密密钥 (DEK) 的保护方式。提供四个级别。

方案 vs. 安全级别

方案是一个 URI 前缀,用于对相关桶进行分组(vault://、certs://、space:// 或您注册的任何名称)。安全级别是桶策略的一个属性,在创建时设置且此后不可更改。

您可以在同一方案内自由混合安全级别。例如,vault://system 可能是 LevelPasswordOnly(启动时自动解锁),而 vault://admin 是 LevelAdminWrapped(需要明确凭据)。

LevelPasswordOnly

桶的 DEK 是使用 HKDF-SHA256 从主密钥派生的,带有每个桶的域分离信息字符串(keeper-bucket-dek-v1:scheme:namespace)。当使用正确的主密码调用 UnlockDatabase 时,所有 LevelPasswordOnly 桶都会自动解锁。运行时不需要每个桶的凭据。此级别适用于进程在启动时需要且无需人工交互的秘密。

LevelAdminWrapped

桶有一个随机生成的 32 字节 DEK,对该桶唯一。DEK 永远不以明文形式存储。对于每个授权管理员,从 HKDF(masterKey‖adminCred, dekSalt) 派生一个密钥加密密钥 (KEK),并用于通过 XChaCha20-Poly1305 封装 DEK。在管理员使用其凭据调用 UnlockBucket 之前,该桶不可访问。仅凭主密码无法解密该桶。撤销一个管理员不会影响任何其他管理员的封装副本。

LevelHSM

桶的 DEK 在 CreateBucket 时生成,并立即由调用者提供的 HSMProvider 封装。该提供程序执行封装和解封装操作 —— keeper 在将 DEK 交给提供程序后永远不会处理原始 DEK。UnlockDatabase 自动调用提供程序为所有注册的 HSM 桶解封装并播种信封。主密钥轮换不会重新加密这些桶;DEK 由提供程序控制。

一个内置的 SoftHSM 实现(由 memguard 保护的封装密钥支持)位于 pkg/hsm 中,用于测试和 CI 环境。不要在生产中使用它。

LevelRemote

在密钥管理行为上与 LevelHSM 相同,但 HSMProvider 由 pkg/remote.Provider 实现 —— 一个可配置的 HTTPS 适配器,通过 TLS 将封装和解封装委托给任何远程 KMS 服务。预构建的配置适用于 HashiCorp Vault Transit、AWS KMS 和 GCP Cloud KMS,位于 pkg/remote 中。对于生产用途,配置 TLSClientCert 和 TLSClientKey 以启用双向 TLS 认证。


密码学设计

主密钥派生```

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:~
验证哈希在首次派生时存储:```
verifyHash ← Argon2id(masterKey, "verification", t=1, m=64 MiB, p=4) → 32 bytes

后续的 DeriveMaster 调用重新计算此哈希值,并与 crypto/subtle.ConstantTimeCompare 进行比较。如果匹配失败,则返回 ErrInvalidPassphrase。

KDF salt 按设计是不加密存储的。它必须在 UnlockDatabase 之前可读,以便派生主密钥——使用从主密钥派生的密钥对其进行加密将导致循环。KDF salt 不是一个秘密;它的目的是唯一性,而非机密性。

秘密加密

每个明文值使用 XChaCha20-Poly1305 通过 bucket DEK 进行加密:``` nonce ← random 24 bytes ciphertext ← XChaCha20-Poly1305.Seal(nonce, DEK, plaintext)

root@kitploit:~
存储的记录是一个 msgpack 编码的 `Secret` 结构体,包含
密文、加密元数据和模式版本。认证是隐式的:
使用错误密钥解密密文会在返回任何明文之前
产生 AEAD 认证失败。

### KEK 派生 — 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)

KEK 是使用 HKDF 而非第二次 Argon2 传递来派生的。主密钥已经由高成本 KDF 生成;第二次调用 Argon2 会给每次 UnlockBucket 调用增加数百毫秒的延迟,且无安全收益。HKDF-SHA256 大约在一微秒内完成操作。

纵深防御: 仅攻破数据库的攻击者能获取包装后的 DEK 和 HKDF 盐值,但若无主密钥则无法派生 KEK。仅攻破主密钥的攻击者无法解包任何 LevelAdminWrapped DEK,除非同时知晓管理员凭证。

元数据加密 — 秘密

秘密元数据(创建时间、更新时间、访问次数、版本)与密文分开加密:``` metaKey ← HKDF-SHA256(bucketDEK, nil, info="keeper-metadata-v1") → 32 bytes encryptedMeta ← XChaCha20-Poly1305.Seal(nonce, metaKey, msgpack(metadata))

root@kitploit:~
For `LevelAdminWrapped`, `LevelHSM`, and `LevelRemote` buckets this means
metadata is inaccessible without the bucket credential, preventing an attacker
with read access to the database file from learning access patterns or
timestamps.

对于 `LevelAdminWrapped`、`LevelHSM` 和 `LevelRemote` 存储桶,这意味着没有存储桶凭证就无法访问元数据,从而阻止了对数据库文件具有读访问权限的攻击者了解访问模式或时间戳。

**Timing side-channel note:** XChaCha20-Poly1305 processes the full ciphertext
before returning an authentication error. The fallback decrypt path (new
derived DEK → old master-key-as-DEK) takes the same wall-clock time regardless
of which key succeeds. No timing side-channel leaks the migration state of a
record.

**时序侧信道说明:** XChaCha20-Poly1305 在处理完整个密文后才返回认证错误。回退解密路径(新派生的 DEK → 旧主密钥作为 DEK)无论哪个密钥成功都消耗相同的挂钟时间。没有时序侧信道泄露记录的迁移状态。

### Metadata encryption — policies, WAL, and audit

### 元数据加密 — 策略、WAL 和审计

All structural metadata is also encrypted at rest. Two keys are derived from
the master key at `UnlockDatabase` time:

所有结构元数据也处于静态加密状态。在 `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 加密:BucketSecurityPolicy 值和轮换 WAL。

auditEncKey 加密:每个审计事件的 Scheme、Namespace 和 Details 字段。

两个密钥在 Lock() 时从内存中清除。用于元数据加密的密码算法与用于秘密的可配置 crypt.Cipher 接口相同——用户选择的密码(FIPS 使用 AES-256-GCM,默认使用 XChaCha20-Poly1305)会自动传递。

所有加密元数据块的传输格式:``` nonce (cipher.NonceSize() bytes) || AEAD-ciphertext

root@kitploit:~
### 策略桶键哈希

磁盘上的策略键是不透明的哈希值,而非纯文本的 `scheme:namespace` 字符串,从而防止离线枚举桶名称:```
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)

内存中的 schemeRegistry 继续使用 "scheme:namespace" 作为其 键——只有磁盘上的表示形式发生变化。

策略认证

每条策略记录携带两个完整性标签,在一个 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:~
### 审计 HMAC 签名

在 `UnlockDatabase` 之前,仅有 SHA-256 哈希可用。
解锁后,`loadPolicy` 验证 HMAC 标签。
`UnlockDatabase` 调用 `upgradePolicyHMACs` 来回填在此功能存在之前创建的策略上的 HMAC 标签。```
auditKey ← HKDF-SHA256(masterKey, nil, info="keeper-audit-hmac-v1") → 32 bytes
HMAC ← HMAC-SHA256(auditKey, event fields including Seq)

签名密钥在 UnlockDatabase 时激活,并在 Lock 时清除。当主密钥轮换时,Rotate 会向每个活动审计链附加一个密钥轮换检查点事件,使用旧审计密钥签名,作为旧周期(epoch)的最后一个事件。历史永远不会被重写;检查点是周期之间的信任桥梁。


密钥层次结构```

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:~
所有中间密钥在使用后立即归零。主密钥绝不会以任何形式写入磁盘。

---

## 存储模式

底层数据库为 bbolt。所有 bucket(桶)及其内容如下:

| bbolt bucket | Key | Value |
|---|---|---|
| `__meta__` | `salt` | msgpack — SaltStore(未加密;若加密则有循环依赖) |
| `__meta__` | `verify` | 原始字节 — Argon2id 验证哈希 |
| `__meta__` | `rotation_wal` | `nonce‖AEAD(msgpack(RotationWAL))` |
| `__meta__` | `bucket_dek_done` | `"1"` — DEK 迁移完成标记 |
| `__policies__` | `hex(SHA-256(scheme:ns))[:32]` | `nonce‖AEAD(msgpack(BucketSecurityPolicy))` |
| `__policies__` | `<base>__hash__` | 加密策略字节的十六进制 SHA-256 |
| `__policies__` | `<base>__hmac__` | 十六进制 HMAC-SHA256(policyKey, 加密策略字节) |
| `__audit__/scheme/namespace` | 事件 UUID | JSON — 审计事件 |
| `__audit__/scheme/namespace` | `__chain_index__` | JSON — chainIndex |
| `scheme/namespace` | 键字符串 | msgpack — Secret 结构体 |

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

审计事件字段

Event 结构体使用独立的明文路由字段(Scheme,Namespace)以及加密的负载字段(EncScheme,EncNamespace,EncDetails)。校验和基于明文路由字段和加密的 EncDetails 字节计算,因此链的完整性可以在三个层级上无需任何密钥进行验证:

层级持有可以验证
公开无SHA-256 校验和链(检测篡改和插入)
审计密钥持有者auditEncKey完整链 + 解密 Scheme/Namespace/Details
操作员主口令所有内容

示例: 合规审计员仅收到 auditEncKey。他们可以验证跨密钥轮换的完整 HMAC 链并读取所有事件详情,但无法解密任何机密值。一个仅拥有数据库文件的公开观察者仍然可以检测任何事件是否在事后被修改或插入。

版本化盐存储

KDF 盐以 msgpack 编码的 SaltStore 形式存储在 salt 元数据键下。每次盐轮换会追加一个新的 SaltEntry 并推进 CurrentVersion。旧条目作为审计线索保留。SaltStore 以未加密形式存储——参见安全决策。

崩溃安全旋转 WAL

Rotate 在操作任何记录之前写入一个 WAL。WAL 携带 WrappedOldKey:用新主密钥加密的轮换前主密钥。崩溃后旧口令丢失;WrappedOldKey 是将旧密钥安全传递跨越边界的唯一正确方式。在 UnlockDatabase 时,如果存在 WAL,新主密钥解密 WrappedOldKey,然后从 WAL 光标处恢复轮换。WAL 本身使用 policyEncKey 加密。


审计链

每个重要操作都会向存储桶的审计链追加一个防篡改事件。链的完整性依赖于两种机制。

校验和。 SHA-256 覆盖前一个校验和、ID、BucketID、Scheme、Namespace、EncDetails、EventType 和时间戳。使用明文形式的 Scheme/Namespace(始终与加密形式一同保留)确保校验和在加载路径上稳定。EncDetails 提供对加密负载的完整性保护。

HMAC。 HMAC-SHA256 覆盖包括 Seq 在内的所有字段。能够写入数据库但不知道审计密钥的攻击者无法生成有效的 HMAC。VerifyIntegrity 对每个事件检查两个层级。

密钥轮换时期边界。 在 Rotate 时,会向每个活动链追加一个检查点事件,携带进出审计密钥的指纹。该检查点使用传出密钥签名。持有任何时期密钥的审计员可以从 wrapped_new_key 字段恢复后续时期密钥,并验证整个链上的 HMAC 连续性。

自动修剪。 当在 Config 中设置了 AuditPruneInterval 时,jack.Scheduler 会定期运行并对每个注册的存储桶调用 PruneEvents。无论此设置如何,LevelHSM 和 LevelRemote 存储桶永远不会被修剪。


Jack 集成

Jack 是一个可选的进程监管库。当通过 WithJack 提供 JackConfig 时,keeper 会自动激活后台组件:

  • 自动锁定循环: 定期检查最后活动时间戳,并在 AutoLockInterval 后丢弃 LevelAdminWrapped 存储桶的 DEK。LevelPasswordOnly 存储桶保持解锁状态,以便后台作业不间断运行。循环任务内部的单写锁模式消除了早期设计中存在的 RUnlock→Lock 竞态条件。
  • 每存储桶 DEK 回收器: 基于 TTL 的 LevelAdminWrapped DEK 过期。
  • 健康监控患者: bbolt 读取延迟检查以及加密/解密往返验证,两者都注册到 jack.Doctor。
  • 审计修剪调度器: 对所有非 HSM 存储桶定期执行 PruneEvents。
  • 异步事件池: 审计事件提交时不会阻塞主操作。

如果未提供 JackConfig,keeper 将不运行这些后台任务。Keeper 从不调用 pool.Shutdown——池的生命周期属于调用方。


x/keepcmd

x/keepcmd 提供了与 CLI 框架解耦的可复用 keeper 操作。将其嵌入您自己的应用程序,无需引入 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` 从不调用 `prompter` 或从标准输入读取。密码短语解析完全由调用者负责——这使得该包在无头服务器环境中保持安全。

`NoClose: true` 防止 `Commands` 在每次操作后调用 `store.Close()`。在 REPL/会话环境中使用,其中一个存储被多次调用共享。

---

## x/keephandler

`x/keephandler` 将 keeper HTTP 端点挂载到任何 `net/http` 多路复用器上。无外部路由器依赖——它使用 Go 1.22+ 的方法+模式路由,配合标准库 `http.ServeMux`。```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)
    }),
)

端点

钩子契约

BeforeFunc 返回 (allow bool, err error)。

  • (true, nil) — 允许请求继续。
  • (false, nil) — 中止;钩子已写入完整响应。
  • (false, err) — 中止;框架使用 err.Error() 写入 500。 钩子必须没有向 w 写入任何内容。

Hook.CaptureBody bool 控制 AfterFunc 是否接收响应体。 false(默认)消耗一个轻量级 statusWriter 包装器; true 将完整响应体缓冲到 bytes.Buffer 中供 AfterFunc 使用——每个请求一次分配。

钩子按注册顺序执行。多次 WithHooks 调用是累加的。对于给定的路由名称,仅使用第一个注册的钩子——同一路由的后续注册将被忽略。


API 参考

构造与解锁```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` 按顺序执行以下操作:

1. 派生并激活审计 HMAC 签名密钥
2. 派生并激活策略 HMAC 密钥
3. 派生并激活 `policyEncKey` 和 `auditEncKey`
4. 清除并重新加载 `schemeRegistry`(解密所有策略 blob)
5. 恢复任何中断的轮换 WAL
6. 升级策略 HMAC 标签
7. 将所有 `LevelPasswordOnly` 存储桶的 DEK 注入 Envelope
8. 启动后台任务(迁移循环、自动锁定、健康患者)

### LevelPasswordOnly 存储桶 — 完整生命周期```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 — 全生命周期```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 存储桶```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")

审计密钥导出```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:~
### 密钥轮换```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"))

比较并交换```go

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

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

错误目录

所有哨兵错误均支持 errors.Is 和 errors.As。堆栈跟踪在错误创建时通过 github.com/olekukonko/errors 捕获。


安全决策

ErrAuthFailed 统一所有 UnlockBucket 失败(CWE-204 / CVSS 5.3)。 无论是未知管理员 ID 还是错误密码,均返回 ErrAuthFailed。这防止了通过时机或错误字符串比较来枚举管理员 ID。RevokeAdmin 保留 ErrAdminNotFound,因为它是在已解锁存储上执行的管理操作。有意省略了管理员 ID 存在性的常量时间比较。能够测量 bbolt 存储桶查找中微秒级差异的攻击者需要本地文件系统访问权限——此时他们可直接读取策略存储桶。威胁模型假设数据库文件可能已被攻破;针对远程枚举的时序防御是首要关注点。

Argon2id 主导时序。 Argon2id 在典型硬件上耗时 200–500 ms。派生后的比较差异小四个数量级以上,无法在远程测量。不应用任何人工均衡。

DEK 在 CAS 事务边界内检索。 CompareAndSwapNamespacedFull 在 bbolt 写事务内检索存储桶 DEK,消除了并发 Rotate 在检索与使用之间更改 DEK 的时间窗口。

口令在 HTTP 处理程序中永远不会存储为 Go 字符串。 所有三个口令字段(passphrase、new_passphrase)通过原始映射提取直接从 JSON 解码为 []byte,使字符串后备数组远离长期堆。[]byte 副本在使用后通过 wipeBytes 清零。

CLI 中无 --passphrase 标志。 标志会出现在 ps 输出和 shell 历史中。CLI 仅从 KEEPER_PASSPHRASE 环境变量或交互式无回显提示接受口令。

REPL 秘密值永不可见。 REPL 中 set <key> 不带内联值时使用 term.ReadPassword——不会出现在终端回滚、shell 历史或 ps 中。对于非敏感数据,可提供内联值(set key value)以方便操作。

SaltStore 有意不加密。 KDF 盐必须在 UnlockDatabase 之前可读,以派生主密钥。policyEncKey(用于所有其他元数据加密)本身派生自主密钥——用 policyEncKey 加密盐会导致循环。KDF 盐提供唯一性而非机密性;加密盐没有安全价值。

策略存储桶键经过哈希而非明文。 磁盘上的策略键为 hex(SHA-256("scheme:namespace"))[:32]——128 位密钥空间——而非可读字符串。离线攻击者读取 bbolt 文件时,在未解密策略 blob 的情况下无法枚举存储桶名称。

元数据加密使用与秘密相同的密码接口。 所有 policyEncKey 和 auditEncKey 操作均通过 s.config.NewCipher(key) 进行——与秘密值配置的同一 crypt.Cipher 接口。用户的密码选择(AES-256-GCM 用于 FIPS 140,默认 XChaCha20-Poly1305)自动应用于策略、WAL 和审计加密。没有代码路径硬编码特定算法。

主密钥轮换期间跳过 LevelHSM 和 LevelRemote 存储桶。 reencryptAllWithKey 和 RotateSalt 明确跳过这些存储桶。DEK 由提供程序控制;主盐轮换不影响它。

使用 WrappedOldKey 实现崩溃安全轮换。 Rotate 在接触任何记录之前写入 WAL。WAL 携带 WrappedOldKey:用新主密钥加密的轮换前主密钥。崩溃后,UnlockDatabase 使用已验证的新密钥解密 WrappedOldKey,并从光标位置继续轮换。


依赖项

下载工具
方法路径描述
POST{prefix}/unlock使用口令解锁存储
POST{prefix}/lock锁定存储
GET{prefix}/status锁定状态 —— 无需认证即可安全轮询
GET{prefix}/keys列出所有密钥
GET{prefix}/keys/{key}获取密钥值
POST{prefix}/keys存储密钥(JSON 或 multipart)
DELETE{prefix}/keys/{key}删除密钥
POST{prefix}/rotate轮换主口令
POST{prefix}/rotate/salt轮换 KDF 盐值
GET{prefix}/backup流式传输数据库快照
错误含义
ErrStoreLocked在存储锁定期间尝试操作
ErrInvalidPassphrase主口令错误
ErrAuthFailed任何 UnlockBucket 失败——不区分密码错误与未知管理员 ID(CWE-204)
ErrKeyNotFound密钥不存在
ErrBucketLocked存储桶尚未解锁
ErrPolicyImmutable为已有存储桶设置第二个策略
ErrPolicyNotFound给定 scheme/namespace 无对应策略
ErrAdminNotFound管理员 ID 不在策略中——仅限 RevokeAdmin
ErrHSMProviderNil未注册提供程序即创建 HSM/远程存储桶
ErrCheckLatency数据库读取延迟超过 DBLatencyThreshold
ErrCASConflict当前值与 CompareAndSwap 中的预期值不匹配
ErrSecurityDowngrade跨存储桶移动从较高安全级别降至较低级别
ErrAlreadyUnlocked对已解锁的存储调用 UnlockDatabase
ErrMasterRequired使用 nil 或已销毁的 Master 调用 UnlockDatabase
ErrChainBroken审计链完整性验证失败
ErrMetadataDecrypt无法解密加密的元数据
ErrPolicySignature策略 HMAC 验证失败——记录已被篡改
包用途
go.etcd.io/bbolt嵌入式键值存储
golang.org/x/cryptoArgon2id、XChaCha20-Poly1305、HKDF、scrypt
github.com/awnumar/memguard内存安全密钥保护区(主密钥、DEK)
github.com/vmihailenco/msgpack/v5秘密和策略的二进制序列化
github.com/olekukonko/jack进程监控(可选的 Jack 集成)
github.com/olekukonko/ll结构化日志
github.com/olekukonko/errors带堆栈跟踪的哨兵错误
github.com/olekukonko/zero安全的字节切片清零
github.com/olekukonko/prompter无回显终端提示(仅 CLI)
github.com/integrii/flaggyCLI 标志解析(仅 cmd/keeper)
golang.org/x/termTTY 检测和原始密码读取(仅 CLI)