Files
awoooi/docs/adr/ADR-058-host-auto-repair-ssh-whitelist.md
OG T c01026be9b docs(skills+adr): 自動修復全鏈路知識更新 — ADR-058 Appendix A + Skills v2.5
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>
2026-04-09 18:21:24 +08:00

7.1 KiB
Raw Blame History

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_resourceinstance (IP:port) 代替 component label → Jaccard 服務相似度為 0 webhooks.py 優先取 component label 1fb0c0c
#6 python:3.11-slimopenssh-clientssh 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-0920 個 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_execute key_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

安全注意事項

  1. 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
  2. 修復命令只能 docker compose up -dsystemctl restart,無法執行任意命令

  3. 日誌位置~/.repair-bot.log(各主機各自記錄)


相關文件

  • scripts/repair-bot/repair-bot-110.sh
  • scripts/repair-bot/repair-bot-188.sh
  • apps/api/src/services/host_repair_agent.py
  • k8s/awoooi-prod/04-repair-ssh-key-template.yaml
  • ops/monitoring/alerts-unified.yml