ADR-058: 188白名單補完 + Appendix A (12 Bug修復記錄 + E2E驗證 + Playbook覆蓋矩陣) Skill-04 DevOps v2.5: SSH自動修復架構章節 (白名單/SOP/陷阱) Skill-05 SRE: 自動修復E2E驗收規範 + 診斷表 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.1 KiB
ADR-058: Host Auto-Repair SSH 白名單架構
狀態: 已接受
日期: 2026-04-05 (台北時區)
起草: Claude Code (Sprint 3 Host Auto-Repair)
審查: 首席架構師 (同日)
背景
Phase O 可觀測性補完後,系統能偵測主機層服務異常(SentryDown、HarborDown、GiteaDown 等),但缺少從 K8s API Pod 向主機層發送修復命令的能力。需要一個安全、可審計的機制讓 AWOOOI API 透過 SSH 執行受限的修復操作。
決策
D1 — 使用 SSH command= forced command 白名單
在目標主機的 authorized_keys 中使用 command= 選項,強制所有使用特定 key 的 SSH 連線只能執行指定的白名單腳本,無論呼叫方送出什麼命令。
command="/home/wooo/bin/repair-bot-110.sh",no-pty,no-agent-forwarding ssh-ed25519 ...
優點:
- SSH key 洩漏也只能執行白名單操作(最小權限)
- 不需要在 API Pod 中安裝任何 agent
- 完整日誌記錄(repair-bot 寫
~/.repair-bot.log)
D2 — 命令格式:repair:<component>
白名單腳本接受 repair:<component> 格式,component 對應預設的修復動作(docker compose up -d 或 systemctl restart)。
Regex 兩端統一:^repair:([a-z0-9][a-z0-9-]{0,30})$
D3 — SSH Key 儲存在 K8s Secret,掛載到 API Pod
volumes:
- name: repair-ssh-key
secret:
secretName: awoooi-repair-ssh-key
defaultMode: 0400 # 八進位 0400 = 十進位 256 = r--------
掛載路徑:/etc/repair-ssh/id_ed25519
D4 — HostRepairAgent 透過 ActionType.SSH_COMMAND 整合到 Playbook 系統
Prometheus/SigNoz 告警 → webhook → auto_repair_service
→ 比對 Playbook (symptom_pattern)
→ _execute_step(ActionType.SSH_COMMAND)
→ HostRepairAgent.repair(layer, component)
→ SSH forced command
→ repair-bot.sh → docker compose up -d
D5 — layer 路由隔離
| Layer | 主機 | 使用者 | 修復類型 |
|---|---|---|---|
docker-110 |
192.168.0.110 | wooo | Docker Compose |
docker-188 |
192.168.0.188 | ollama | Docker Compose |
systemd-188 |
192.168.0.188 | ollama | systemd |
k8s |
— | — | kubectl(不走 SSH,拒絕) |
已批准的修復白名單
110 主機 (repair-bot-110.sh)
| Component | 修復方式 | 目錄 |
|---|---|---|
| sentry | docker compose up -d | /opt/sentry |
| harbor | docker compose up -d | /home/wooo/harbor/harbor |
| gitea | docker compose up -d | /home/wooo/gitea |
| gitea-runner | docker compose up -d | /home/wooo/act-runner |
| langfuse | docker compose up -d | /home/wooo/langfuse |
| alertmanager | docker compose up -d | /home/wooo/monitoring |
| signoz | docker compose up -d | /home/wooo/signoz/deploy/docker |
| stock-platform | docker compose up -d | /home/wooo/stockPlatform (2026-04-09 新增) |
188 主機 (repair-bot-188.sh)
| Component | 修復方式 | 目錄 |
|---|---|---|
| openclaw | docker compose up -d | /home/ollama/clawbot-v5 |
| minio | docker compose up -d | /home/ollama/minio |
| signoz | docker compose up -d | /home/ollama/signoz/deploy/docker |
| momo-app | docker compose up -d | /home/ollama/momo-pro (2026-04-09 新增) |
| tsenyang-website | docker compose up -d | /home/ollama/services/tsenyang (2026-04-09 新增) |
| bitan-app | docker compose up -d | /home/ollama/services/bitan (2026-04-09 新增) |
Appendix A — Bug 修復記錄 (2026-04-09)
執行者: Claude Sonnet 4.6 (Asia/Taipei) 背景: 自動修復機制從未成功執行(success_count 全部為 0),完整審計後發現下列阻斷性問題
| Bug | 問題描述 | 修復 | Commit |
|---|---|---|---|
| #5 | target_resource 用 instance (IP:port) 代替 component label → Jaccard 服務相似度為 0 |
webhooks.py 優先取 component label |
1fb0c0c |
| #6 | python:3.11-slim 無 openssh-client,ssh binary 缺失 |
Dockerfile 生產 stage 加 openssh-client |
1fb0c0c |
| #11 | NetworkPolicy 未開放 22 (SSH),預設拒絕所有未列 port | 02-network-policy.yaml 新增 110:22 + 188:22 egress |
07a097c |
| #12 | Secret defaultMode=0400 (root-only),appuser (UID 1000) 無法讀 SSH key |
Pod securityContext.fsGroup: 1000 |
77f2da9 |
E2E 驗證結果
# docker-110
SentryDown → Jaccard 匹配 sentry-down-repair → SSH wooo@192.168.0.110 repair:sentry
→ REPAIR_OK:sentry (6208ms) ✅
# docker-188
MoWoooWorkDown → Jaccard 匹配 momo-app-down-repair → SSH ollama@192.168.0.188 repair:momo-app
→ REPAIR_OK:momo-app (3791ms) ✅
Playbook 覆蓋矩陣 (2026-04-09,20 個 Playbook)
| 告警 | Playbook | Layer | success_count |
|---|---|---|---|
| SentryDown | sentry-down-repair | docker-110 | 1 ✅ |
| HarborDown | harbor-down-repair | docker-110 | 0 |
| GiteaDown | gitea-down-repair | docker-110 | 0 |
| AlertmanagerDown | alertmanager-down-repair | docker-110 | 0 |
| OpenClawDown | openclaw-down-repair | docker-188 | 0 |
| MoWoooWorkDown | momo-app-down-repair | docker-188 | 2 ✅ |
| TsenyangWebsiteDown | tsenyang-website-down-repair | docker-188 | 0 |
| BitanWoooWorkDown | bitan-app-down-repair | docker-188 | 0 |
| StockWoooWorkDown | stock-platform-down-repair | docker-110 | 0 |
| SignOzDown | signoz-down-repair | docker-188 | 0 |
| DockerContainerExited | docker-container-exited-repair | 動態 | 0 |
| DockerContainerUnhealthy | docker-container-unhealthy-repair | 動態 | 0 |
| KubePodNotReady | k8s-pod-not-ready-restart | k8s | 0 |
注意:
openclaw-down-repair原錯誤指向docker-110,已於 2026-04-09 修正為docker-188
首席架構師 Review 記錄 (2026-04-05)
評分:72/100 → 修正後 88/100
已修正問題:
- C1:
_ssh_executekey_path 改為直接傳入,不反查 LAYER_SSH_CONFIG - C2:
PlaybookService.create()proxy 方法,Router 不直接呼叫_repository - C3: CD Step 1b
sed替換 IMAGE_TAG_PLACEHOLDER,消除中斷風險 - M3: repair-bot regex 統一為
[a-z0-9][a-z0-9-]{0,30},禁止底線 - m1: defaultMode 加八進位說明注釋
- m2:
_ssh_execute用 deadline 計算剩餘 timeout
待觀察問題(已知、可接受):
- M1: 測試使用 AsyncMock(豁免原因:
_ssh_execute是真實 subprocess,無法在 CI 無 SSH 環境直接測試;已透過 E2E 驗收補充覆蓋) - M2:
StrictHostKeyChecking=no(內網環境可接受,未來加 known_hosts ConfigMap)
安全注意事項
-
SSH key 輪換:若
awoooi-repair-ssh-key洩漏,立即執行:- 110:
sed -i '/repair-bot/d' ~/.ssh/authorized_keys - 188:
sed -i '/repair-bot/d' ~/.ssh/authorized_keys - 重建 keypair 並更新 K8s Secret
- 110:
-
修復命令只能
docker compose up -d或systemctl restart,無法執行任意命令 -
日誌位置:
~/.repair-bot.log(各主機各自記錄)
相關文件
scripts/repair-bot/repair-bot-110.shscripts/repair-bot/repair-bot-188.shapps/api/src/services/host_repair_agent.pyk8s/awoooi-prod/04-repair-ssh-key-template.yamlops/monitoring/alerts-unified.yml