From 3b5e26213c6ee053c9dc9d3a540abe64b1921413 Mon Sep 17 00:00:00 2001 From: voson Date: Fri, 6 Mar 2026 15:00:50 +0800 Subject: [PATCH] Enhance AI-Native Secret Manager design document with updated features and architecture. Clarified target audience, improved security model, and added detailed components and workflow examples for secret management. Introduced a mixed storage encryption model and refined market positioning table. --- ai-secrets-manager-design.md | 573 ++++++++++++++++++++++------------- 1 file changed, 368 insertions(+), 205 deletions(-) diff --git a/ai-secrets-manager-design.md b/ai-secrets-manager-design.md index 7fcebf2..38959fd 100644 --- a/ai-secrets-manager-design.md +++ b/ai-secrets-manager-design.md @@ -1,6 +1,6 @@ # AI-Native Secret Manager 设计文档 -面向 AI Agent(Cursor / OpenCode)的轻量级 Secret 管理工具,通过 MCP 协议提供安全的环境变量注入,secret 永远不进入 LLM 上下文。 +面向 AI Agent(Cursor / OpenCode)和开发者的轻量级 Secret 管理工具。通过 CLI 提供安全的环境变量注入,secret 在使用阶段永远不进入 LLM 上下文。 ## 背景与动机 @@ -16,145 +16,208 @@ 聊天中直接粘贴 AccessKey ← 明文进入 LLM context → 发送到云端 API ``` -**核心安全问题**:Secret 明文进入 LLM 的上下文窗口,被发送到 Anthropic/OpenAI 等远程服务器,并留存在聊天历史中。 +**核心安全问题**:Secret 明文反复进入 LLM 的上下文窗口,被发送到 Anthropic/OpenAI 等远程服务器,并留存在聊天历史中。 ### 设计目标 -1. **Secret 不进入 LLM 上下文**:AI Agent 只知道环境变量名(`$VAR_NAME`),不知道值 -2. **跨设备**:多台电脑共享同一套 secrets(云端 PostgreSQL) -3. **面向 AI Agent**:通过 MCP 协议无缝集成 Cursor / OpenCode -4. **轻量**:核心 500-800 行代码,不依赖重型框架 -5. **安全**:客户端加密,PostgreSQL 只存密文,Master Key 存本地 OS Keychain +1. **使用阶段 Secret 不进入 LLM 上下文**:AI Agent 只知道环境变量名(`$VAR_NAME`),不知道值 +2. **跨设备、跨平台**:macOS / Windows / Linux 多台电脑共享同一套 secrets +3. **面向 AI Agent**:AI 通过 CLI 进行 CRUD 和环境变量注入 +4. **人性化管理**:Web UI 提供可视化的 secret 管理界面 +5. **轻量安全**:客户端加密(CLI + WASM),Server 端永远不接触敏感明文 ### 市场定位 -| 工具 | 定位 | AI Agent 支持 | 自托管 | -|------|------|--------------|--------| -| HashiCorp Vault | 企业级 Secret 管理 | 无 | 是 | -| Infisical | 开发团队 Secret 管理 | 无 | 是 | -| 1Password MCP | 消费级密码管理 + AI | 有(SaaS) | 否 | -| SOPS / age | 文件加密 | 无 | - | -| **本项目** | **AI Agent 的 Secret 注入** | **核心功能** | **是** | - -"AI-native Secret Manager" 这个定位目前市场几乎空白。 +| 工具 | 定位 | AI Agent 支持 | 客户端加密 | 自托管 | +|------|------|--------------|-----------|--------| +| HashiCorp Vault | 企业级 Secret 管理 | 无 | 否 | 是 | +| Infisical | 开发团队 Secret 管理 | 无 | 有 | 是 | +| 1Password MCP | 消费级密码管理 + AI | 有(SaaS) | 有 | 否 | +| SOPS / age | 文件加密 | 无 | 是 | - | +| **本项目** | **AI Agent 的 Secret 注入** | **核心功能** | **是** | **是** | ## 架构 +### 整体架构 + ``` -┌─────────────────────────────────────────────────────────────────┐ -│ 设备 A (MacBook) 设备 B (其他电脑) │ -│ ┌──────────────┐ ┌──────────────┐ │ -│ │ Cursor/OC │ │ Cursor/OC │ │ -│ │ ↓ MCP │ │ ↓ MCP │ │ -│ │ secrets-mcp │ │ secrets-mcp │ │ -│ └──────┬───────┘ └──────┬───────┘ │ -│ │ │ │ -│ ┌──────┴───────┐ ┌──────┴───────┐ │ -│ │ OS Keychain │ │ OS Keychain │ │ -│ │ (master key) │ │ (master key) │ │ -│ └──────────────┘ └──────────────┘ │ -│ │ │ │ -└─────────┼───────────────────────────────┼───────────────────────┘ - │ TLS + Auth │ - └───────────────┬───────────────┘ - │ - ┌─────────┴─────────┐ - │ Cloud PostgreSQL │ - │ (加密存储 secrets) │ - └───────────────────┘ + Rust crypto crate(加密核心) + ┌──────────────────────────┐ + │ AES-256-GCM / Argon2id │ + │ DEK 管理 / 信封加密 │ + └──────┬──────────┬─────────┘ + │ │ + 编译为 native │ │ 编译为 WASM + │ │ +┌────────────────────┐ │ ┌────┴───────────────┐ +│ CLI (Rust 跨平台) │◄──────┘ │ Web UI (浏览器) │ +│ + OS Keychain │ │ + WASM 加密模块 │ +│ macOS / Win / Linux│ │ + wasm-bindgen │ +└─────────┬──────────┘ └─────────┬──────────┘ + │ HTTPS │ HTTPS + └──────────────┬───────────────────┘ + │ + ┌─────────┴──────────┐ + │ Remote Server │ + │ (Rust / Axum) │ + │ ├─ HTTPS API │ + │ └─ 托管 Web UI + │ + │ WASM 静态文件 │ + └─────────┬──────────┘ + │ + ┌─────────┴──────────┐ + │ PostgreSQL 18 │ + └────────────────────┘ ``` ### 组件 | 组件 | 职责 | 技术选型 | |------|------|---------| -| **CLI** | 管理 secrets(CRUD)、初始化、导入 | Rust | -| **MCP Server** | AI Agent 接口、环境变量注入 | Rust (stdio) | -| **PostgreSQL** | 持久化存储(密文) | 云端 PG | -| **OS Keychain** | 存储 Master Key | macOS Keychain / Linux secret-service | +| **crypto** | 加密核心(AES-256-GCM + Argon2id + 信封加密) | Rust crate,编译为 native + wasm32 | +| **server** | HTTPS API、数据库 CRUD、认证、托管静态文件 | Rust / Axum | +| **cli** | 客户端加解密、OS Keychain、环境变量注入、CRUD | Rust / clap | +| **web (WASM)** | 浏览器端加解密绑定 | Rust / wasm-bindgen | +| **web-ui** | 可视化 Secret 管理界面 | HTML / JS / CSS | +| **PostgreSQL 18** | 持久化存储(密文 + 明文元数据) | 云端 PG | -CLI 和 MCP Server 共用加密库,可编译为同一个二进制(子命令区分): +### Cargo Workspace 结构 -```bash -secrets set ricnsmart/aliyun.ak "LTAI5t..." # CLI 模式 -secrets mcp # MCP Server 模式(stdio) +``` +secrets/ +├── crates/ +│ ├── crypto/ ← 加密核心(编译目标: native + wasm32) +│ ├── server/ ← Axum HTTPS Server +│ ├── cli/ ← CLI 客户端(链接 crypto native) +│ └── web/ ← WASM 绑定(wasm-bindgen,链接 crypto wasm) +├── web-ui/ ← 前端代码(调用 WASM 模块) +└── Cargo.toml ← workspace 根配置 ``` ## 核心设计 -### 1. 安全模型:Secret 不进入 LLM 上下文 +### 1. 安全模型:分层暴露策略 -**两种模式对比**: +Secret 在不同生命周期阶段有不同的暴露等级: -| | 模式 A: AI 拿明文 | 模式 B: 环境变量注入 | -|---|---|---| -| AI 看到什么 | `"LTAI5t79fLxc..."` | `"$ACS_ACCESS_KEY_ID"` | -| LLM context 暴露 | secret 明文 | 只有变量名 | -| 云端 API 风险 | 明文发到 Anthropic | 只发变量名 | -| 聊天历史 | 明文留存 | 安全 | +| 阶段 | Secret 暴露情况 | 说明 | +|------|----------------|------| +| **创建** | 可能经过 LLM | 用户通过聊天告知 AI,或 AI 执行 `secrets set` 命令。一次性暴露,可接受 | +| **使用** | 不经过 LLM | AI 只引用 `$VAR_NAME`,本地 shell 展开后执行。反复使用,始终安全 | +| **generate** | 完全不经过 LLM | CLI 本地生成随机密码 → 本地加密 → 密文发送 Server。值不经过 AI | -**本项目采用模式 B**。 +**核心价值**:一个 secret 可能被使用几十上百次,只有创建时可能暴露一次,之后的所有使用都通过环境变量引用,安全。 -工作流: +工作流示例: ``` -AI Agent: 调用 MCP tool inject_secrets("ricnsmart") -MCP Server: (本地解密,注入环境变量) - → 返回给 AI: "已注入: $SSH_KEY_RICNSMART, $HOST_TIANCHU_PRIMARY, ..." -AI Agent: 执行 shell - ssh -i "$SSH_KEY_RICNSMART" "$SSH_USER@$HOST_TIANCHU_PRIMARY" "..." - ← shell 进程拿到真实值,LLM 只写了变量名 +# 创建阶段(一次性,secret 可能经过 LLM) +AI Agent: secrets set ricnsmart/aliyun.ak "LTAI5t..." + +# 使用阶段(反复使用,secret 不经过 LLM) +AI Agent: eval $(secrets inject ricnsmart --export) +AI Agent: ssh -i "$RICNSMART_SSH_KEY" "$RICNSMART_SSH_USER@$RICNSMART_HOST_PRIMARY" "..." + ↑ AI 只写变量名,本地 shell 展开后执行 + +# 生成阶段(secret 完全不经过 LLM) +AI Agent: secrets generate ricnsmart/db.password --length 32 + → CLI 本地生成 → 本地加密 → 密文存 Server + → 返回: "已生成并存储,环境变量名: RICNSMART_DB_PASSWORD" ``` -### 2. 加密模型:客户端加密 + 信封加密 +### SSH 远程命令场景 -``` -存储路径: - secret_value - → AES-256-GCM(DEK) → 密文 → 存入 PostgreSQL - DEK (Data Encryption Key) - → AES-256-GCM(Master Key) → 加密 DEK → 存入 PostgreSQL - Master Key - → Argon2id(password) → 派生 → 存入 OS Keychain +环境变量注入天然支持 SSH 远程命令场景: + +```bash +eval $(secrets inject ricnsmart --export) +ssh user@server "mysql -u root -p'$DB_PASSWORD' -e 'CREATE DATABASE app'" ``` -PostgreSQL 只存密文和加密后的 DEK,被攻破也无法获取明文。 +执行过程: +1. `eval` 将 secrets 注入当前 shell 环境变量 +2. AI 写命令只引用 `$DB_PASSWORD`(变量名) +3. **本地 shell 展开** `$DB_PASSWORD` 为真实值 +4. 展开后的命令通过 **SSH 加密通道**传输到远程执行 +5. AI 始终只看到变量名,从不看到实际值 -**每条 secret 使用独立的 DEK**,限制单个密钥泄露的影响范围。 +### 2. 加密模型:客户端加密 + 混合存储 + +采用**混合加密模型**:用户决定每条 secret 是否加密。敏感数据客户端加密后存密文,非敏感数据明文存储以支持搜索。 + +#### 敏感数据:信封加密 + +``` +secret_value + → AES-256-GCM(DEK) → 密文 → 存入 PostgreSQL (encrypted_value) +DEK (Data Encryption Key) + → AES-256-GCM(Master Key) → 加密 DEK → 存入 PostgreSQL (encrypted_dek) +Master Key + → Argon2id(password, salt) → 派生 → 存入 OS Keychain +``` + +**每条加密的 secret 使用独立的 DEK**,限制单个密钥泄露的影响范围。 + +PostgreSQL 只存密文和加密后的 DEK,Server 被攻破也无法获取明文。 + +#### 非敏感数据:明文存储 + +用户选择不加密的 secret(如 URL、用户名等)以明文存储,支持服务端搜索和索引。 + +#### 元数据:JSONB 灵活字段 + +每条 secret 附带 JSONB 元数据字段,始终为明文,用于描述、标签、备注等。支持 GIN 索引高效搜索。 ### 3. 跨设备 Master Key 同步 -采用 **密码派生** 方案: +采用**密码派生**方案,跨平台支持: ```bash # 新设备初始化(只需一次) -secrets init -# 输入 master password → Argon2id 派生 Master Key → 存入 OS Keychain +secrets init --server "https://secrets.example.com" +# 输入 master password +# → 从 Server 获取 Argon2id salt +# → 本地派生 Master Key +# → 存入 OS Keychain # 之后自动从 Keychain 获取,无需再次输入 ``` -所有设备使用相同密码,派生出相同的 Master Key。Argon2id 的 salt 存储在 PostgreSQL 中。 +所有设备使用相同密码,派生出相同的 Master Key。Argon2id salt 存储在 Server 端 PostgreSQL 中。 -### 4. MCP Tool 接口 +| 平台 | Keychain 实现 | +|------|--------------| +| macOS | Keychain Services | +| Windows | Credential Manager | +| Linux | libsecret (Secret Service API) | -```typescript -// Tool 1: 注入 secrets 到当前 shell 环境(核心功能) -// AI Agent 调用后,后续 shell 命令可通过 $VAR 引用 -inject_secrets(project: string) -→ { injected: string[], hint: string } -// 示例返回: { injected: ["TIANCHU_HOST_PRIMARY", "TIANCHU_SSH_KEY", ...], -// hint: "使用 $TIANCHU_HOST_PRIMARY 引用天储主服务器" } +Rust `keyring` crate 统一封装三个平台。 -// Tool 2: 列出可用 projects 和 keys(不含值) -list_secrets(project?: string) -→ string[] -// 示例返回: ["ricnsmart/ssh.tianchu-primary", "ricnsmart/aliyun.ak", ...] +### 4. Web UI 浏览器端加密(Rust WASM) + +Web UI 通过 Rust 编译为 WebAssembly 实现浏览器端加密,与 CLI **共享完全相同的加密逻辑**: -// Tool 3: 用 secret 执行命令(secret 不经过 LLM,可选) -exec_with_secrets(project: string, command: string) -→ { stdout: string, stderr: string, exit_code: number } -// MCP Server 本地替换 $VAR 后执行,输出可脱敏 ``` +用户打开 Web UI + → 输入 master password + → WASM 模块中 Argon2id 派生 master key(salt 从 Server API 获取) + → master key 仅存于浏览器内存 + +创建敏感 secret: + → WASM 加密 → 密文通过 HTTPS API 发送到 Server + +查看敏感 secret: + → Server 返回密文 → WASM 解密 → 浏览器中显示 + +页面关闭: + → master key 从内存消失 +``` + +**Server 端全程不接触明文**,无论是 CLI 还是 Web UI 访问,安全模型一致。 + +使用 WASM 而非 Web Crypto API 的原因: +- Web Crypto API 不支持 Argon2id(只有 PBKDF2) +- WASM 直接复用 CLI 的 Rust 加密代码,保证一致性 +- Rust 内存安全 + 成熟加密 crate,优于 JS 实现 ### 5. 环境变量命名规则 @@ -168,9 +231,102 @@ project: refining, key: grafana.password 规则: upper(project) + "_" + upper(key.replace(".", "_")) ``` +## Server API + +### 认证 + +API Token 认证。首次 `secrets init` 时生成 Token,存入客户端 OS Keychain。所有 API 请求通过 `Authorization: Bearer ` 头认证。 + +### 接口列表 + +``` +# 配置 +GET /api/config/:key 获取配置(如 argon2_salt) +POST /api/config 设置配置 + +# 项目 +GET /api/projects 列出所有项目 +POST /api/projects 创建项目 +DELETE /api/projects/:id 删除项目 + +# Secrets +GET /api/secrets 列出 secrets(支持过滤,不含加密值) + ?project=ricnsmart + &pattern=ssh.* + &kind=file +POST /api/secrets 创建 secret(接收密文或明文) +GET /api/secrets/:id 获取 secret 详情(含加密值密文,客户端解密) +PUT /api/secrets/:id 更新 secret +DELETE /api/secrets/:id 删除 secret + +# 静态文件 +GET / Web UI 入口 +GET /assets/* Web UI 静态资源 + WASM 文件 +``` + +### 请求/响应示例 + +**创建加密 secret**: + +```json +POST /api/secrets +{ + "project": "ricnsmart", + "key": "aliyun.access_key_id", + "is_encrypted": true, + "kind": "text", + "encrypted_value": "", + "encrypted_dek": "", + "nonce": "", + "dek_nonce": "", + "metadata": { + "description": "阿里云主账号 AccessKey", + "tags": ["cloud", "aliyun", "production"], + "env_var": "RICNSMART_ALIYUN_ACCESS_KEY_ID" + } +} +``` + +**创建非加密 secret**: + +```json +POST /api/secrets +{ + "project": "ricnsmart", + "key": "gitea.url", + "is_encrypted": false, + "kind": "text", + "plaintext_value": "https://gitea.refining.dev", + "metadata": { + "description": "Gitea 服务地址", + "tags": ["service", "gitea"] + } +} +``` + +**列出 secrets 响应**(不含加密值): + +```json +[ + { + "id": "uuid-...", + "project": "ricnsmart", + "key": "aliyun.access_key_id", + "is_encrypted": true, + "kind": "text", + "metadata": { + "description": "阿里云主账号 AccessKey", + "tags": ["cloud", "aliyun", "production"] + }, + "created_at": "2025-03-06T10:00:00Z", + "updated_at": "2025-03-06T10:00:00Z" + } +] +``` + ## 数据模型 -### PostgreSQL Schema +### PostgreSQL 18 Schema ```sql CREATE TABLE projects ( @@ -184,29 +340,43 @@ CREATE TABLE secrets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), project_id UUID REFERENCES projects(id) ON DELETE CASCADE, key TEXT NOT NULL, -- "aliyun.access_key_id" - encrypted_value BYTEA NOT NULL, -- AES-256-GCM 密文 - encrypted_dek BYTEA NOT NULL, -- 加密后的 DEK - nonce BYTEA NOT NULL, -- GCM nonce - dek_nonce BYTEA NOT NULL, -- DEK 加密的 nonce - kind TEXT NOT NULL DEFAULT 'text', -- "text", "ssh-key", "json", "file" - description TEXT, + + -- 敏感数据(客户端加密后存储) + encrypted_value BYTEA, -- AES-256-GCM 密文 + encrypted_dek BYTEA, -- 加密后的 DEK + nonce BYTEA, -- GCM nonce + dek_nonce BYTEA, -- DEK 加密的 nonce + + -- 非敏感数据(明文存储,可搜索) + plaintext_value TEXT, -- 用户选择不加密时存储明文 + + -- 灵活元数据(明文 JSONB,可搜索) + metadata JSONB DEFAULT '{}', -- 描述、标签、备注等 + + is_encrypted BOOLEAN NOT NULL DEFAULT true, + kind TEXT NOT NULL DEFAULT 'text', -- "text", "ssh-key", "json", "file" + created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now(), UNIQUE(project_id, key) ); --- Argon2id salt,跨设备共享 +-- JSONB GIN 索引,支持高效搜索 +CREATE INDEX idx_secrets_metadata ON secrets USING GIN (metadata); +-- 按 kind 过滤 +CREATE INDEX idx_secrets_kind ON secrets (kind); + +-- 全局配置(Argon2id salt 等) CREATE TABLE config ( key TEXT PRIMARY KEY, value BYTEA NOT NULL ); --- INSERT INTO config (key, value) VALUES ('argon2_salt', ...); --- 审计日志(可选) +-- 审计日志 CREATE TABLE audit_log ( id BIGSERIAL PRIMARY KEY, secret_id UUID REFERENCES secrets(id) ON DELETE SET NULL, - action TEXT NOT NULL, -- "read", "write", "delete", "inject" + action TEXT NOT NULL, -- "read", "write", "delete", "inject", "generate" device TEXT, -- 设备标识 detail TEXT, -- 额外信息 created_at TIMESTAMPTZ DEFAULT now() @@ -218,164 +388,157 @@ CREATE TABLE audit_log ( ``` # ricnsmart/config.toml -[[servers]] → ricnsmart/server.tianchu-primary (kind=json) -hostname = "..." { "hostname": "...", "ip": "8.153.204.96", -public_ip = "8.153.204.96" "key": "ricnsmart-sh.pem", "user": "ecs-user" } -key = "ricnsmart-sh.pem" +[[servers]] → ricnsmart/server.tianchu-primary (kind=json, encrypted) +hostname = "..." encrypted_value: {...} +public_ip = "8.153.204.96" metadata: {"description": "天储主服务器"} -[services.gitea] → ricnsmart/gitea.url = "https://gitea.refining.dev" -url = "https://gitea.refining.dev" ricnsmart/gitea.token = "92a627..." -token = "92a627..." +[services.gitea] → ricnsmart/gitea.url (kind=text, 不加密) +url = "https://gitea.refining.dev" plaintext_value: "https://gitea.refining.dev" + ricnsmart/gitea.token (kind=text, 加密) +token = "92a627..." encrypted_value: <密文> -# refining/config.toml - -[services.grafana] → refining/grafana.url = "https://grafana.refining.dev" -url = "..." refining/grafana.username = "voson" -username = "voson" refining/grafana.password = "ZXG-..." -password = "ZXG-..." - -[services.cloudflare] → refining/cloudflare.api_key = "57a99d..." -global_api_key = "57a99d..." refining/cloudflare.email = "voson..." - -[services.aliyun] → digitevents/aliyun.access_key_id = "LTAI5t..." -AccessKeyId = "..." digitevents/aliyun.access_key_secret = "ZKgl..." -AccessKeySecret = "..." - -# SSH 密钥 → ricnsmart/ssh-key.ricnsmart-sh (kind=file) -~/.../keys/ricnsmart-sh.pem refining/ssh-key.ricn-hk (kind=file) +# SSH 密钥 → ricnsmart/ssh-key.ricnsmart-sh (kind=file, 加密) +~/.../keys/ricnsmart-sh.pem encrypted_value: <密文> + metadata: {"original_path": "ricnsmart-sh.pem"} ``` ## CLI 命令设计 +CLI 是 AI Agent 和人共用的主要接口。 + ```bash # ===== 初始化 ===== -secrets init # 输入 master password,派生密钥,存入 Keychain -secrets init --db "postgres://..." # 指定 PG 连接串(也存入 Keychain) +secrets init --server "https://secrets.example.com" +# 输入 master password → 从 Server 获取 salt → Argon2id 派生 Master Key +# → Master Key + API Token 存入 OS Keychain # ===== 项目管理 ===== secrets project list # 列出所有项目 secrets project create ricnsmart # 创建项目 secrets project delete ricnsmart # 删除项目(需确认) -# ===== Secret 管理 ===== -secrets set ricnsmart/aliyun.ak "LTAI5t..." # 设置 text 类型 -secrets set ricnsmart/ssh.key --file ~/.ssh/ricnsmart.pem # 设置 file 类型 -secrets set ricnsmart/server.primary --json '{"host":...}' # 设置 json 类型 +# ===== Secret CRUD ===== +secrets set ricnsmart/aliyun.ak "LTAI5t..." # 创建(默认加密) +secrets set ricnsmart/gitea.url "https://..." --no-encrypt # 创建(不加密) +secrets set ricnsmart/ssh.key --file ~/.ssh/ricnsmart.pem # 从文件创建(加密) +secrets set ricnsmart/server.primary --json '{"host":...}' # JSON 类型(加密) -secrets get ricnsmart/aliyun.ak # 获取值(输出到 stdout) +secrets get ricnsmart/aliyun.ak # 获取值(本地解密,输出到 stdout) secrets list # 列出所有(不显示值) -secrets list ricnsmart/ # 列出项目下的(不显示值) +secrets list ricnsmart/ # 按项目过滤 +secrets list --pattern "ssh.*" # 模糊匹配 key +secrets list --kind file # 按类型过滤 secrets delete ricnsmart/aliyun.ak # 删除 -# ===== 导入 ===== -secrets import config.toml # 从现有 TOML 导入 -secrets import config.toml --project ricnsmart --dry-run # 预览 +# ===== 生成随机密码 ===== +secrets generate ricnsmart/db.password --length 32 # 默认字符集 +secrets generate ricnsmart/api.key --length 64 --charset hex # 指定字符集 +# → 本地生成 → 本地加密 → 密文存 Server +# → 输出: "已生成并存储,环境变量名: RICNSMART_DB_PASSWORD" # ===== 环境变量注入 ===== -secrets inject ricnsmart -- ./app # 注入后启动子进程 -secrets inject ricnsmart --export # 输出 export 语句 +# macOS / Linux (bash/zsh) +eval $(secrets inject ricnsmart --export) -# ===== MCP Server ===== -secrets mcp # 启动 MCP Server(stdio 模式) +# Windows PowerShell +secrets inject ricnsmart --export-ps | Invoke-Expression + +# 通用方式(所有平台,启动子进程) +secrets inject ricnsmart -- ./app + +# 选择性注入 +secrets inject ricnsmart --keys "aliyun.*,ssh.*" --export + +# ===== 导入 ===== +secrets import config.toml --project ricnsmart # 从 TOML 导入 +secrets import config.toml --project ricnsmart --dry-run # 预览 ``` -## MCP 集成配置 - -### Cursor (.cursor/mcp.json) - -```json -{ - "mcpServers": { - "secrets": { - "command": "secrets", - "args": ["mcp"] - } - } -} -``` - -### OpenCode - -```yaml -mcpServers: - secrets: - command: secrets - args: [mcp] -``` - -配置后,AI Agent 在需要凭证时会自动调用 `inject_secrets` 或 `list_secrets`,而非要求用户粘贴。 - ## 安全注意事项 ### Secret 生命周期 ``` -创建: CLI set → 本地加密 → 密文存 PG -读取: MCP inject → PG 拉取密文 → 本地解密 → 注入环境变量 → 返回变量名给 AI -使用: AI 写 shell 命令引用 $VAR → shell 进程替换为真实值 → 执行 -清理: 进程退出后环境变量消失 +创建: CLI set / generate → 本地加密 → HTTPS → Server 存密文到 PG + Web UI 输入 → WASM 加密 → HTTPS → Server 存密文到 PG +读取: CLI inject → HTTPS 拉取密文 → 本地解密 → 注入环境变量 + Web UI 查看 → HTTPS 拉取密文 → WASM 解密 → 浏览器显示 +使用: AI 写 shell 命令引用 $VAR → 本地 shell 展开为真实值 → 执行 +清理: 进程退出后环境变量消失,Web UI 关闭后 master key 消失 ``` ### 威胁模型 | 威胁 | 防御措施 | |------|---------| -| PostgreSQL 被攻破 | 客户端加密,PG 只有密文 | -| 设备丢失 | Master Key 在 OS Keychain(需要设备密码) | -| LLM 提供商窥探 | Secret 不进入 LLM context,只有变量名 | -| 命令输出泄露 secret | MCP Server 可选输出脱敏(替换已知 secret 值为 `***`) | -| Master Password 弱 | Argon2id 高计算成本(推荐配置:m=64MB, t=3, p=4) | +| PostgreSQL 被攻破 | 客户端加密,PG 只有密文,Server 不持有 Master Key | +| Server 被攻破 | 敏感数据为客户端密文,非敏感数据暴露但不含凭证 | +| 设备丢失 | Master Key 在 OS Keychain(需要设备密码/生物识别) | +| LLM 提供商窥探 | 使用阶段 secret 不进入 LLM context,只有变量名 | +| 命令输出泄露 secret | 可选输出脱敏(Phase 2,替换已知 secret 值为 `***`) | +| Master Password 弱 | Argon2id 高计算成本(推荐:m=64MB, t=3, p=4) | +| 传输层攻击 | HTTPS/TLS 加密传输 | +| WASM 模块被篡改 | Server 托管 WASM,可通过 SRI (Subresource Integrity) 校验 | ### 不防御的场景 -- 设备上的 root 权限攻击者(可直接读取进程环境变量) +- 设备上的 root 权限攻击者(可直接读取进程环境变量或内存) - 用户主动将 secret 粘贴到聊天中(行为问题,非技术问题) +- AI 通过 `echo $VAR` 读取环境变量值并返回到 LLM 上下文(已知弱点,Phase 2 输出脱敏可缓解) ## 技术选型 | 组件 | 选择 | 理由 | |------|------|------| -| 语言 | Rust | 单二进制分发、加密库成熟、性能好 | +| 语言 | Rust | 单二进制分发、加密库成熟、性能好、编译为 native + WASM | | 加密 | `aes-gcm` crate | AEAD,业界标准 | -| 密钥派生 | `argon2` crate | 抗 GPU/ASIC,OWASP 推荐 | -| OS Keychain | `keyring` crate | 跨平台(macOS/Linux/Windows) | -| 数据库 | `sqlx` crate + PostgreSQL | async、编译期 SQL 检查 | -| MCP 协议 | `rmcp` 或手动实现 JSON-RPC over stdio | MCP 协议本身很简单 | -| CLI 框架 | `clap` | Rust 生态标准 | +| 密钥派生 | `argon2` crate | 抗 GPU/ASIC,OWASP 推荐,支持 WASM 编译 | +| OS Keychain | `keyring` crate | 跨平台(macOS Keychain / Windows Credential Manager / Linux libsecret) | +| 数据库 | PostgreSQL 18 + `sqlx` crate | async、编译期 SQL 检查、JSONB 原生支持 | +| HTTP Server | `axum` crate | Rust 生态主流、tokio 驱动、tower 中间件 | +| WASM 绑定 | `wasm-bindgen` + `wasm-pack` | Rust → WASM 标准工具链 | +| CLI 框架 | `clap` crate | Rust 生态标准 | +| 序列化 | `serde` + `serde_json` | JSON 处理标准库 | ## 开发计划 -### Phase 1: MVP(核心功能) +### Phase 1: MVP(Server + CLI) -- [ ] 项目脚手架(Cargo workspace:`cli` + `core` + `mcp`) -- [ ] 加密模块(AES-256-GCM + Argon2id) -- [ ] PostgreSQL 存储层(schema + CRUD) -- [ ] OS Keychain 集成 -- [ ] CLI 基本命令(init / set / get / list / delete) -- [ ] MCP Server(inject_secrets / list_secrets) -- [ ] Cursor 集成测试 +- [ ] Cargo workspace 脚手架(`crypto` + `server` + `cli`) +- [ ] `crypto` crate:AES-256-GCM + Argon2id + 信封加密 +- [ ] PostgreSQL 18 schema + 迁移脚本 +- [ ] `server` crate:Axum HTTPS API(CRUD + config + 认证) +- [ ] `cli` crate:init / set / get / list / delete / generate / inject +- [ ] 跨平台 OS Keychain 集成(macOS / Windows / Linux) +- [ ] 跨平台 inject 命令(bash/zsh + PowerShell) +- [ ] `secrets import config.toml` 从现有配置导入 -### Phase 2: 完善 +### Phase 2: Web UI + 增强 -- [ ] `secrets import config.toml` 导入现有配置 -- [ ] `secrets inject -- ./app` 环境变量注入启动子进程 +- [ ] `web` crate:crypto 编译为 WASM + wasm-bindgen 绑定 +- [ ] Web UI 前端:CRUD 管理界面 + WASM 浏览器端加解密 +- [ ] Server 托管 Web UI + WASM 静态文件 +- [ ] TOTP 支持(存储 TOTP seed,生成一次性验证码) +- [ ] 输出脱敏(可选,替换已知 secret 值为 `***`) - [ ] 审计日志 -- [ ] 输出脱敏(可选) -- [ ] CI/CD 集成(Gitea Actions 中使用) ### Phase 3: 扩展 -- [ ] Web UI(查看/管理 secrets) -- [ ] Secret 轮换提醒 +- [ ] MCP Server(可选 AI 增强,stdio 模式) +- [ ] 模板渲染(`secrets template config.tpl > config.yaml`) - [ ] 团队共享(多用户 + RBAC) -- [ ] `exec_with_secrets` MCP tool +- [ ] Secret 轮换提醒 +- [ ] CI/CD 集成(Gitea Actions 中使用) ## 参考 - [MCP 协议规范](https://modelcontextprotocol.io/) -- [Infisical](https://github.com/Infisical/infisical) - 开源 Secret Manager -- [age](https://github.com/FiloSottile/age) - 文件加密工具 -- [SOPS](https://github.com/getsops/sops) - Mozilla 的加密文件编辑器 -- [Rust `aes-gcm`](https://docs.rs/aes-gcm/) - AEAD 加密 -- [Rust `argon2`](https://docs.rs/argon2/) - 密码哈希 -- [Rust `keyring`](https://docs.rs/keyring/) - 跨平台 Keychain +- [Infisical](https://github.com/Infisical/infisical) — 开源 Secret Manager +- [age](https://github.com/FiloSottile/age) — 文件加密工具 +- [SOPS](https://github.com/getsops/sops) — Mozilla 的加密文件编辑器 +- [Rust `aes-gcm`](https://docs.rs/aes-gcm/) — AEAD 加密 +- [Rust `argon2`](https://docs.rs/argon2/) — 密码哈希 +- [Rust `keyring`](https://docs.rs/keyring/) — 跨平台 Keychain +- [Rust `axum`](https://docs.rs/axum/) — HTTP Server 框架 +- [wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/) — Rust WASM 绑定 +- [wasm-pack](https://rustwasm.github.io/wasm-pack/) — Rust WASM 打包工具