feat: add agent integration guide and get-secret/render-env tools
This commit is contained in:
113
docs/agent-integration.md
Normal file
113
docs/agent-integration.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# Agent 整合指南
|
||||||
|
|
||||||
|
本 vault 供本地 AI agent(Hermes、OpenClaw、cron worker 等)安全存取機密資訊使用。
|
||||||
|
|
||||||
|
## 基本資訊
|
||||||
|
|
||||||
|
- vault 位置:`~/projects/agent-secret-vault/secrets/vault.yml`
|
||||||
|
- vault password 位置:`~/.config/continuous-ai-workflow-spec/vault-pass.txt`
|
||||||
|
- 加密格式:ansible-vault(AES256)
|
||||||
|
|
||||||
|
## Agent 讀取 secrets 的方法
|
||||||
|
|
||||||
|
### 方法 1:用 vault.sh 腳本(推薦)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/projects/agent-secret-vault
|
||||||
|
|
||||||
|
# 檢視 vault 內容
|
||||||
|
./scripts/vault.sh view
|
||||||
|
|
||||||
|
# 解密到暫存檔(用完記得刪)
|
||||||
|
./scripts/vault.sh decrypt /tmp/vault.yml
|
||||||
|
|
||||||
|
# 讀特定 section
|
||||||
|
python3 - <<'PY'
|
||||||
|
import yaml
|
||||||
|
with open('/tmp/vault.yml') as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
print(data['gitea']['api_token'])
|
||||||
|
PY
|
||||||
|
|
||||||
|
rm -f /tmp/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法 2:用 get-secret 工具(最快)
|
||||||
|
|
||||||
|
如果你有 `scripts/get-secret.sh`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/get-secret.sh gitea.api_token
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法 3:用 render-env.sh(適合 worker)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 渲染成 env 檔
|
||||||
|
./scripts/render-env.sh gitea > /tmp/gitea.env
|
||||||
|
source /tmp/gitea.env
|
||||||
|
# 現在 $GITEA_API_TOKEN 可用
|
||||||
|
rm /tmp/gitea.env
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常見錯誤
|
||||||
|
|
||||||
|
### 1. vault password file 不存在
|
||||||
|
|
||||||
|
解決:
|
||||||
|
```bash
|
||||||
|
cd ~/projects/agent-secret-vault
|
||||||
|
./scripts/vault.sh init
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. permission denied
|
||||||
|
|
||||||
|
確認:
|
||||||
|
```bash
|
||||||
|
chmod 600 ~/.config/continuous-ai-workflow-spec/vault-pass.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. ansible-vault not found
|
||||||
|
|
||||||
|
確認已安裝 ansible:
|
||||||
|
```bash
|
||||||
|
which ansible-vault
|
||||||
|
```
|
||||||
|
|
||||||
|
## 重要原則
|
||||||
|
|
||||||
|
1. **不要把解密後的 vault 明文寫進 log**
|
||||||
|
2. **不要把 secrets 直接寫進 prompt**
|
||||||
|
3. **用完暫存檔立刻刪除**
|
||||||
|
4. **不要把 vault-pass.txt 提交到 git**
|
||||||
|
|
||||||
|
## 現有 secrets 清單
|
||||||
|
|
||||||
|
vault 內目前有這些 section:
|
||||||
|
|
||||||
|
- `gitea`:開發用 Gitea 帳號( Hermestest 用)
|
||||||
|
- `openclaw_alice`:Alice 的完整 secrets(15+ 服務)
|
||||||
|
|
||||||
|
取用時用 dot notation:
|
||||||
|
- `gitea.api_token`
|
||||||
|
- `openclaw_alice.http_nodes.gitea.password`
|
||||||
|
|
||||||
|
## 如果要新增 secrets
|
||||||
|
|
||||||
|
1. 先解密:
|
||||||
|
```bash
|
||||||
|
./scripts/vault.sh decrypt /tmp/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 編輯:
|
||||||
|
```bash
|
||||||
|
vim /tmp/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 重新加密:
|
||||||
|
```bash
|
||||||
|
./scripts/vault.sh encrypt /tmp/vault.yml
|
||||||
|
cp /tmp/vault.yml secrets/vault.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
4. commit 並 push
|
||||||
39
scripts/get-secret.sh
Executable file
39
scripts/get-secret.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
用法: $(basename "$0") <key>
|
||||||
|
|
||||||
|
範例:
|
||||||
|
$(basename "$0") gitea.api_token
|
||||||
|
$(basename "$0") openclaw_alice.http_nodes.gitea.password
|
||||||
|
|
||||||
|
讀取 vault 中的單一 key。
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
KEY="${1:-}"
|
||||||
|
[ -n "$KEY" ] || { usage; exit 1; }
|
||||||
|
|
||||||
|
TMP_DEC=$(mktemp)
|
||||||
|
chmod 600 "$TMP_DEC"
|
||||||
|
ansible-vault decrypt "$VAULT_FILE" --vault-password-file "$VAULT_PASS_FILE" --output "$TMP_DEC" 2>/dev/null
|
||||||
|
|
||||||
|
python3 - <<PY
|
||||||
|
import yaml
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
data = yaml.safe_load(Path('$TMP_DEC').read_text())
|
||||||
|
parts = '$KEY'.split('.')
|
||||||
|
val = data
|
||||||
|
for p in parts:
|
||||||
|
val = val.get(p, '')
|
||||||
|
print(val if val else '')
|
||||||
|
PY
|
||||||
|
|
||||||
|
rm -f "$TMP_DEC"
|
||||||
31
scripts/render-env.sh
Executable file
31
scripts/render-env.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
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}"
|
||||||
|
|
||||||
|
SECTION="${1:-}"
|
||||||
|
[ -n "$SECTION" ] || { echo "用法: $0 <section>"; exit 1; }
|
||||||
|
|
||||||
|
TMP_DEC=$(mktemp)
|
||||||
|
chmod 600 "$TMP_DEC"
|
||||||
|
ansible-vault decrypt "$VAULT_FILE" --vault-password-file "$VAULT_PASS_FILE" --output "$TMP_DEC" 2>/dev/null
|
||||||
|
|
||||||
|
python3 - <<PY
|
||||||
|
import yaml
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
data = yaml.safe_load(Path('$TMP_DEC').read_text())
|
||||||
|
section_data = data.get('$SECTION', {})
|
||||||
|
prefix = '${SECTION^^}'.replace('-', '_')
|
||||||
|
|
||||||
|
for key, val in section_data.items():
|
||||||
|
if isinstance(val, dict):
|
||||||
|
for subkey, subval in val.items():
|
||||||
|
print(f"{prefix}_{key.upper()}_{subkey.upper()}={subval}")
|
||||||
|
else:
|
||||||
|
print(f"{prefix}_{key.upper()}={val}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
rm -f "$TMP_DEC"
|
||||||
Reference in New Issue
Block a user