Files
awoooi/docs/adr/ADR-087-auto-approve-security-hardening.md
OG T ba8cf6105d docs(adr): ADR-086 Telegram UI 清洗規範 + ADR-087 AutoApprove kubectl 閘門
ADR-086: Telegram 通知卡片 UI 清洗規範
- _parse_debate_summary() 設計決定與各 TYPE 欄位清洗規則
- TYPE-3 鍵盤重構:批准/拒絕永遠第一行
- 技術債:_parse_debate_summary 提升模組層級(P1-1)

ADR-087: AutoApprove 安全強化 — kubectl 強制執行閘門
- 條件 1d 設計:_raw_action 語意 + NO_EXECUTABLE_ACTION reason
- Solver Nemo 格式 kubectl 驗證
- 降級指令改為真實 kubectl 唯讀調查
- min_trust_score=0 保留理由記錄(TrustEngine 記憶體持久化技術債)
- P0-2 風險記錄:kubectl exec 未加入 _DESTRUCTIVE_PATTERNS

2026-04-17 ogt + Claude Sonnet 4.6(亞太): Session 技術債清理

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:25:34 +08:00

4.6 KiB
Raw Blame History

ADR-087: AutoApprove 安全強化 — kubectl 強制執行閘門

狀態: 已接受
日期: 2026-04-17
作者: ogt + Claude Sonnet 4.6
關聯: ADR-070全自動 AIOps 閉環、ADR-082多 Agent 協作)


背景

Phase 2 多 Agent 協作ADR-082Solver Agent 透過 OpenClaw 的 openclaw_nemo provider 產出修復方案時,有兩條路徑:

  1. 標準格式{"candidates": [{"action": "kubectl rollout restart ...", ...}]}
  2. Nemo 格式{"action_title": "重啟 Crash Looping Pod", "confidence": 0.85}

Nemo 格式的 action_title 是自然語言描述,不是可執行的 kubectl 指令。

問題一auto_approve 誤放行自然語言 action

原有條件:

  • 條件 1caction 為空 → 拒絕
  • 條件 1d未驗證 action 是否可執行

結果Solver Nemo 路徑輸出「重啟 Crash Looping Pod」→ 條件 1c 通過(非空)→ 自動批准 → kubectl_command 欄位為空 → 實際無法執行 → incident 卡在 INVESTIGATING 狀態。

問題二Solver 降級動作為自然語言

_default_action_for_category() 回傳:

  • "restart_pod""check_disk_usage" 等非 kubectl 字串

這些字串通過條件 1c非空但無法被執行層使用。


決策

1. 條件 1dkubectl 格式強制閘

位置:auto_approve.py:evaluate(),在條件 1c 之後、條件 2 之前。

# 條件 1d: 自然語言描述不可自動執行
_raw_action = proposal_data.get("action", "") or ""      # 原始值,不 fallback
_kubectl_cmd = proposal_data.get("kubectl_command", "") or ""
_has_kubectl = "kubectl" in _raw_action.lower() or "kubectl" in _kubectl_cmd.lower()
if not _has_kubectl:
    return self._reject(
        reason=AutoApproveReason.NO_EXECUTABLE_ACTION,
        detail=f"Action '{_raw_action[:60]}' is natural language — no kubectl command",
        ...
    )

設計要點

  • 使用 proposal_data.get("action", "") 原始值(非 line 232 已 fallback 的 action 變數),避免 fallback 語意混淆Code Review P0-1
  • 新增 AutoApproveReason.NO_EXECUTABLE_ACTION enum 值,與 NO_PLAYBOOK(無匹配 Playbook語意分離防止污染 KM 飛輪學習資料分類Code Review P1-2

2. Solver Nemo 格式kubectl 驗證

位置:solver_agent.py:_extract_candidates()

if "action_title" in parsed and "candidates" not in parsed:
    action_title = str(parsed.get("action_title", ""))
    if "kubectl" not in action_title.lower():
        return []   # → 觸發 _degraded_plan輸出真實 kubectl 調查指令

3. 降級指令改為真實 kubectl

_default_action_for_category() 改為按類別回傳唯讀調查指令:

Category 降級指令
pod/kube/crash kubectl get pods -n awoooi-prod -o wide
disk/storage/pvc kubectl exec -n awoooi-prod deployment/postgresql -- df -h
cpu/load kubectl top pods -n awoooi-prod --sort-by=cpu
memory/oom kubectl top pods -n awoooi-prod --sort-by=memory
network/connect kubectl get services -n awoooi-prod
default kubectl get pods -n awoooi-prod

設計原則降級指令均為唯讀調查操作無副作用blast_radius=20


min_trust_score 設計決定

min_trust_score: int = 0 保持不變。

理由TrustEngine 資料儲存於記憶體(_trust_records dictPod 重啟後歸零。若將門檻提高至 0.8,每次 Pod 重啟後所有動作都無法自動執行,直到累積足夠歷史記錄。這在生產環境中不可接受。

正確方向:將 TrustEngine 持久化至 PostgreSQL已列為 Phase 3.5 技術債)。


後果

  • 自然語言 action 不再通過 auto_approve強制走 TYPE-3 人工審核
  • Solver 降級路徑輸出真實 kubectl 調查指令,可被 auto_approve 正確評估
  • NO_EXECUTABLE_ACTION reason 與 NO_PLAYBOOK 區分KM 飛輪學習資料分類正確

已知風險 (P0-2 — 下次 Security Review)

kubectl exec 指令(降級路徑中的 disk 類別)被允許通過條件 1dkubectl exec 可以在容器內執行任意命令。目前降級指令固定為 df -h(唯讀),但若未來 LLM 輸出包含 kubectl exec 的高風險變體,現有 _DESTRUCTIVE_PATTERNS 不包含 exec 關鍵字,可能漏過。

建議:下次 Security Review 時評估是否將 kubectl exec 加入 _DESTRUCTIVE_PATTERNS 或獨立 allow-list。


相關 Commits

Commit 說明
93205ce P1 kubectl gate + P2 Nemo path 強制
fb225c5 Code Review P0-1 action fallback + P1-2 NO_EXECUTABLE_ACTION enum