diff --git a/.gitignore b/.gitignore index 579ef28..2eb4ca1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ __pycache__/ .DS_Store .vault_pass.txt secrets/plaintext/ +# Local plaintext vault password must never be committed +vault-pass.txt +secrets/vault-pass.txt + +# Placeholder marker generated when password archive is absent +secrets/vault-pass.txt.zip.PLACEHOLDER diff --git a/README.md b/README.md index 0167691..98d2cbf 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,43 @@ # Agent Secret Vault -這個 repo 專門存放本地 AI agent 開發會用到的機密管理機制。 +本 repo 專門管理本地 AI agent / worker 需要的機密資料。 -核心設計: -- 使用 `ansible-vault` 作為加密格式 -- 加密檔可進 git -- vault password file 只放在本機 -- 多個 agent 透過統一腳本存取 secrets +## 核心設計 -## 內容 -- `scripts/vault.sh`:初始化、檢視、編輯、加密、解密、rekey -- `docs/secret-vault.md`:使用說明與設計原則 -- `secrets/vault.yml`:加密後 secrets 檔 +- 使用 `ansible-vault` 加密 `secrets/vault.yml` +- 加密後的 `secrets/vault.yml` 可以進 git +- vault password file 放在本機:`~/.config/vault-pass.txt` +- 新機器可透過 repo 內的密碼保護壓縮檔 `secrets/vault-pass.txt.zip` 安裝 password file +- 多個 agent 透過統一腳本讀取 secrets,不各自發明 credential 管理方式 -## 目標 -- 讓 Hermes / OpenClaw / cron worker / 其他本地 agent 共用同一套 secret storage contract -- 不把明文 secret 留在 repo -- 不讓每個 agent 各自發明一套 credential 管理方式 +## 主要文件 + +- 人類使用指南:[`docs/human-guide.md`](docs/human-guide.md) +- Agent 安裝 Runbook:[`docs/agent-install-runbook.md`](docs/agent-install-runbook.md) +- Agent 整合補充:[`docs/agent-integration.md`](docs/agent-integration.md) +- Vault 基礎說明:[`docs/secret-vault.md`](docs/secret-vault.md) + +## 常用指令 + +```bash +# 安裝 vault password file(會要求輸入壓縮檔密碼) +./scripts/install-vault-pass.sh + +# 維護者建立密碼保護壓縮檔 +./scripts/create-vault-pass-archive.sh + +# 檢視 vault +./scripts/vault.sh view + +# 編輯 vault +./scripts/vault.sh edit + +# 讀單一 secret +./scripts/get-secret.sh openclaw_alice.http_nodes.gitea.password +``` + +## 安全原則 + +- 不要把 `~/.config/vault-pass.txt` commit 到 git +- 不要把解密後完整 YAML 貼到 chat/log +- Agent 回報時只回報 key path、commit hash、驗證方式,不回報 secret value diff --git a/docs/agent-install-runbook.md b/docs/agent-install-runbook.md new file mode 100644 index 0000000..f1031b8 --- /dev/null +++ b/docs/agent-install-runbook.md @@ -0,0 +1,163 @@ +# Agent 安裝 Runbook:agent-secret-vault + +這份文件給 AI agent / cron worker / automation 使用。目標:把 `agent-secret-vault` 安裝好、缺檔時建立 placeholder、並能安全讀取 secrets。 + +## 安全規則 + +- 不要把 secret value、vault password、token、private key 印到 chat / log / 工單 comment。 +- 不要 dump 整份解密後的 `secrets/vault.yml`。 +- 只讀取任務所需的單一欄位。 +- 所有暫存解密檔用完立即刪除。 + +## 目標路徑 + +- Repo:`~/projects/agent-secret-vault` +- Vault 檔:`~/projects/agent-secret-vault/secrets/vault.yml` +- Vault password file:`~/.config/vault-pass.txt` +- 可覆寫環境變數:`VAULT_PASS_FILE=/custom/path` + +## 1. 安裝系統依賴 + +```bash +sudo apt update +sudo apt install -y git ansible unzip +``` + +驗證: + +```bash +command -v git +command -v ansible-vault +command -v unzip +``` + +## 2. Clone repo + +```bash +mkdir -p ~/projects +cd ~/projects +if [ ! -d agent-secret-vault/.git ]; then + git clone ssh://git@gitea.cowbay.org:2203/hermes/agent-secret-vault.git +fi +cd agent-secret-vault +``` + +若 clone 失敗,建立 placeholder 讓後續步驟可明確回報缺什麼: + +```bash +mkdir -p ~/projects/agent-secret-vault/{scripts,secrets,docs} +cat > ~/projects/agent-secret-vault/README.PLACEHOLDER.md <<'PLACEHOLDER' +# Placeholder + +agent-secret-vault repo 尚未成功 clone。 +需要 Gitea SSH 權限:ssh://git@gitea.cowbay.org:2203/hermes/agent-secret-vault.git +PLACEHOLDER +``` + +然後回報人類:需要把此 agent / 機器的 SSH public key 加到 Gitea 或 deploy key。 + +## 3. 安裝 vault password file + +標準位置: + +```text +~/.config/vault-pass.txt +``` + +安裝來源是 repo 內的密碼保護壓縮檔: + +```text +secrets/vault-pass.txt.zip +``` + +執行: + +```bash +cd ~/projects/agent-secret-vault +./scripts/install-vault-pass.sh +``` + +安裝過程會要求使用者手動輸入壓縮檔密碼;解壓成功後會寫入: + +```text +~/.config/vault-pass.txt +``` + +權限應為 `600`。 + +### 若壓縮檔不存在 + +建立 placeholder,不要自行編造密碼: + +```bash +mkdir -p ~/projects/agent-secret-vault/secrets +cat > ~/projects/agent-secret-vault/secrets/vault-pass.txt.zip.PLACEHOLDER <<'PLACEHOLDER' +Missing file: secrets/vault-pass.txt.zip +Purpose: password-protected archive containing vault-pass.txt +Action: ask human maintainer to provide this archive or commit it to the private repo. +PLACEHOLDER +``` + +然後回報人類:缺 `secrets/vault-pass.txt.zip`,需要提供。 + +### 若要用非預設路徑 + +```bash +export VAULT_PASS_FILE=/path/to/vault-pass.txt +./scripts/install-vault-pass.sh /path/to/vault-pass.txt.zip +``` + +## 4. 驗證安裝 + +```bash +cd ~/projects/agent-secret-vault +ansible-vault view secrets/vault.yml --vault-password-file ~/.config/vault-pass.txt >/dev/null +``` + +成功:exit code 0。失敗時只回報錯誤摘要,不要輸出密碼或 vault 內容。 + +## 5. 讀取單一 secret + +```bash +cd ~/projects/agent-secret-vault +./scripts/get-secret.sh +``` + +範例: + +```bash +./scripts/get-secret.sh gitea.api_token +./scripts/get-secret.sh openclaw_alice.http_nodes.gitea.password +``` + +把輸出直接交給需要 secret 的動作使用;不要貼到 chat / log。 + +## 6. Worker 需要 env 時 + +```bash +cd ~/projects/agent-secret-vault +umask 077 +./scripts/render-env.sh gitea > /tmp/gitea.env +set -a +. /tmp/gitea.env +set +a +rm -f /tmp/gitea.env +``` + +使用完如果擔心環境被 dump,執行 `unset` 清掉相關變數。 + +## 7. 更新 secret 的 agent 流程 + +只有在人類明確要求更新 secrets 時才做: + +```bash +cd ~/projects/agent-secret-vault +git pull --ff-only +./scripts/vault.sh edit +ansible-vault view secrets/vault.yml --vault-password-file ~/.config/vault-pass.txt >/dev/null +git add secrets/vault.yml +git commit -m "Update secret " +git push +``` + +回報只包含 key 名稱、commit hash、驗證方式;不要包含 secret value。 diff --git a/docs/agent-integration.md b/docs/agent-integration.md index c41d6b1..c6c2048 100644 --- a/docs/agent-integration.md +++ b/docs/agent-integration.md @@ -5,7 +5,7 @@ ## 基本資訊 - vault 位置:`~/projects/agent-secret-vault/secrets/vault.yml` -- vault password 位置:`~/.config/continuous-ai-workflow-spec/vault-pass.txt` +- vault password 位置:`~/.config/vault-pass.txt` - 加密格式:ansible-vault(AES256) ## Agent 讀取 secrets 的方法 @@ -64,7 +64,7 @@ cd ~/projects/agent-secret-vault 確認: ```bash -chmod 600 ~/.config/continuous-ai-workflow-spec/vault-pass.txt +chmod 600 ~/.config/vault-pass.txt ``` ### 3. ansible-vault not found diff --git a/docs/human-guide.md b/docs/human-guide.md new file mode 100644 index 0000000..82fb91e --- /dev/null +++ b/docs/human-guide.md @@ -0,0 +1,170 @@ +# 人類使用指南:agent-secret-vault + +這份文件給人類維護者看:如何安裝、操作,以及如何用自然語言指示 agent 管理 secrets。 + +## 這是什麼 + +`agent-secret-vault` 是本地 AI agent 共用的機密資料 repo。 + +- 加密資料:`secrets/vault.yml` +- 加密格式:Ansible Vault +- 解密鑰匙:`~/.config/vault-pass.txt` +- Repo:`ssh://git@gitea.cowbay.org:2203/hermes/agent-secret-vault.git` + +`secrets/vault.yml` 可以進 git;`~/.config/vault-pass.txt` 不可以進 git。 + +## 安裝 + +```bash +sudo apt update +sudo apt install -y git ansible unzip + +mkdir -p ~/projects +cd ~/projects +git clone ssh://git@gitea.cowbay.org:2203/hermes/agent-secret-vault.git +cd agent-secret-vault +``` + +接著安裝 vault password file: + +```bash +./scripts/install-vault-pass.sh +``` + +這會從 repo 內的密碼保護壓縮檔解出: + +```text +~/.config/vault-pass.txt +``` + +過程中需要你手動輸入壓縮檔密碼。 + +驗證: + +```bash +./scripts/vault.sh view +``` + +## 建立安裝用密碼保護壓縮檔 + +維護者若要讓其他 agent / 機器安裝,先在已可解密的機器上執行: + +```bash +cd ~/projects/agent-secret-vault +./scripts/create-vault-pass-archive.sh +git add secrets/vault-pass.txt.zip +git commit -m "Add vault password archive" +git push +``` + +腳本會要求互動輸入 zip 密碼。這個 zip 密碼不要寫進 repo、chat 或 log;交給安裝者時用另外的安全渠道。 + +## 常用人工操作 + +### 查看 vault + +```bash +cd ~/projects/agent-secret-vault +./scripts/vault.sh view +``` + +### 編輯 vault + +```bash +cd ~/projects/agent-secret-vault +git pull --ff-only +./scripts/vault.sh edit +git add secrets/vault.yml +git commit -m "Update secrets" +git push +``` + +### 讀單一 secret + +```bash +./scripts/get-secret.sh gitea.api_token +./scripts/get-secret.sh openclaw_alice.http_nodes.gitea.password +``` + +## 用自然語言請 agent 操作 secrets + +你可以直接對 agent 下這類命令: + +### 查詢但不要顯示 secret + +```text +幫我確認 agent-secret-vault 裡有沒有 gitea.api_token,不要把 token 印出來。 +``` + +Agent 應該只回報「有 / 沒有」與使用的 key path,不應顯示 token。 + +### 使用 secret 去登入或呼叫 API + +```text +用 agent-secret-vault 裡的 openclaw_alice.http_nodes.gitea 帳密登入 Gitea,登入成功後回報狀態,不要把帳密貼出來。 +``` + +Agent 可以讀取 secret 並用於 browser/API,但不能把 secret value 回傳聊天。 + +### 新增 secret + +```text +幫我把新的 API token 加到 agent-secret-vault,key 放在 openclaw_alice.http_nodes.example_service.api_token;先 git pull,更新後驗證可讀,再 commit/push。不要在回報中顯示 token。 +``` + +如果 token 需要由你提供,請用安全渠道或互動輸入;不要把高敏感 token 直接貼到公開群組。 + +### 修改 secret + +```text +把 agent-secret-vault 裡 openclaw_alice.http_nodes.gitea.password 更新成我等一下提供的新密碼;完成後只回報 commit hash 和驗證方式。 +``` + +### 列出 key 結構 + +```text +列出 agent-secret-vault 目前有哪些 top-level sections 和 key 名稱,不要顯示任何 password/token/secret/private_key 的值。 +``` + +## Agent 應遵守的回報格式 + +完成更新後,agent 回報應包含: + +- 更新的 key path +- repo path +- commit hash +- 驗證方式,例如 `./scripts/get-secret.sh ` 成功 + +不應包含: + +- secret value +- vault password +- 解密後完整 YAML + +## 故障排除 + +### `Decryption failed` + +通常是 `~/.config/vault-pass.txt` 不存在、權限不對、或內容不是正確密碼。 + +檢查: + +```bash +ls -l ~/.config/vault-pass.txt +``` + +權限應為 `600`。 + +### `secrets/vault-pass.txt.zip` 不存在 + +代表 repo 裡沒有安裝用的密碼保護壓縮檔。請由維護者建立並提交到私有 repo,或用其他安全方式提供 vault password file。 + +### Gitea clone/push 失敗 + +檢查: + +```bash +ssh -T -p 2203 git@gitea.cowbay.org +``` + +通常需要把該機器的 SSH public key 加到 Gitea。 diff --git a/docs/secret-vault.md b/docs/secret-vault.md index a9e6574..85e3ec7 100644 --- a/docs/secret-vault.md +++ b/docs/secret-vault.md @@ -4,7 +4,7 @@ ## 設計 - 加密檔:`secrets/vault.yml` -- 本機 vault password file:`~/.config/continuous-ai-workflow-spec/vault-pass.txt` +- 本機 vault password file:`~/.config/vault-pass.txt` - 管理腳本:`scripts/vault.sh` ## 原則 diff --git a/scripts/create-vault-pass-archive.sh b/scripts/create-vault-pass-archive.sh new file mode 100755 index 0000000..e2de9f3 --- /dev/null +++ b/scripts/create-vault-pass-archive.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SRC="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}" +OUT="${1:-$REPO_DIR/secrets/vault-pass.txt.zip}" + +usage() { + cat <&2 + exit 2 +fi + +if ! command -v zip >/dev/null 2>&1; then + echo "Missing dependency: zip" >&2 + echo "Install it with: sudo apt install -y zip" >&2 + exit 3 +fi + +mkdir -p "$(dirname "$OUT")" +tmpdir="$(mktemp -d)" +cleanup() { rm -rf "$tmpdir"; } +trap cleanup EXIT +install -m 600 "$SRC" "$tmpdir/vault-pass.txt" + +( + cd "$tmpdir" + # zip prompts for archive password interactively. + zip -e -q "$OUT" vault-pass.txt +) +chmod 600 "$OUT" +echo "Created password-protected archive: $OUT" diff --git a/scripts/get-secret.sh b/scripts/get-secret.sh index 7e6a411..db01b39 100755 --- a/scripts/get-secret.sh +++ b/scripts/get-secret.sh @@ -3,7 +3,7 @@ set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VAULT_FILE="${VAULT_FILE:-$REPO_DIR/secrets/vault.yml}" -VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/continuous-ai-workflow-spec/vault-pass.txt}" +VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}" usage() { cat <&2 </dev/null 2>&1; then + echo "Missing dependency: unzip" >&2 + echo "Install it with: sudo apt install -y unzip" >&2 + exit 3 +fi + +tmpdir="$(mktemp -d)" +cleanup() { rm -rf "$tmpdir"; } +trap cleanup EXIT + +umask 077 +mkdir -p "$(dirname "$DEST")" +chmod 700 "$(dirname "$DEST")" || true + +# unzip will prompt for the archive password interactively. +unzip -q "$ARCHIVE" -d "$tmpdir" + +src="$tmpdir/vault-pass.txt" +if [ ! -f "$src" ]; then + echo "Archive extracted, but vault-pass.txt was not found inside." >&2 + exit 4 +fi + +install -m 600 "$src" "$DEST" + +echo "Installed vault password file: $DEST" diff --git a/scripts/render-env.sh b/scripts/render-env.sh index 06e7250..536fa0c 100755 --- a/scripts/render-env.sh +++ b/scripts/render-env.sh @@ -3,7 +3,7 @@ set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VAULT_FILE="${VAULT_FILE:-$REPO_DIR/secrets/vault.yml}" -VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/continuous-ai-workflow-spec/vault-pass.txt}" +VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}" SECTION="${1:-}" [ -n "$SECTION" ] || { echo "用法: $0
"; exit 1; } diff --git a/scripts/vault.sh b/scripts/vault.sh index ade523e..66c2cce 100755 --- a/scripts/vault.sh +++ b/scripts/vault.sh @@ -3,7 +3,7 @@ set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VAULT_FILE="${VAULT_FILE:-$REPO_DIR/secrets/vault.yml}" -VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/continuous-ai-workflow-spec/vault-pass.txt}" +VAULT_PASS_FILE="${VAULT_PASS_FILE:-$HOME/.config/vault-pass.txt}" usage() { cat <