Files
awoooi/docs/adr/ADR-012-dangerous-operations-governance.md
OG T 2b1264df05 docs: 完整治理架構 ADR-010/011/012 + CLAUDE.md 鐵律更新
2026-03-23 重大事故修復與治理:

1. ADR-010: Secrets 集中管理 (Bitwarden + Sealed Secrets)
2. ADR-011: NetworkPolicy 變更治理 (偵測 + 告警 + 人工決策)
3. ADR-012: 危險操作治理 (Tier 分級 + CI/CD 攔截 + 審計)
4. UX-001: 告警疲勞解決方案 (時間衰減 + 智慧分組)

CLAUDE.md 更新:
- 新增最高優先級鐵律 (禁止 ClawBot、OpenClaw 核心、禁止危險 API)
- 新增任務開始前必讀 Memory 對照表

事故教訓:
- Telegram Token 連續三次被 logOut 失效
- AWOOOI API 程式碼呼叫 logOut 導致災難
- 已停用 AWOOOI API Telegram,OpenClaw 為唯一 Gateway

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-23 19:44:56 +08:00

17 KiB
Raw Blame History

ADR-012: 危險操作治理架構

狀態: 提案 日期: 2026-03-23 決策者: 統帥 觸發事件: Telegram Token 連續三次被 logOut 災難


背景

事故摘要

2026-03-23AWOOOI API 程式碼中的 logOut API 呼叫導致 Telegram Bot Token 連續三次被永久失效:

時間線:
11:09 - 第一個 Token 被 logOut (舊版 AWOOOI API)
19:31 - 第二個 Token 被 logOut (舊 Pod 未完全終止)
19:39 - 第三個 Token 成功 (AWOOOI API 已停用 Telegram)

問題根因

問題 說明
危險程式碼進入生產 logOut 永久失效 Token無人攔截
無 Code Review 涉及外部服務的危險操作未被審查
無執行時告警 危險操作執行時沒有任何通知
無審計日誌 無法追溯誰/何時執行了什麼
變更順序錯誤 給新 Token 時舊服務還在運行

決策

建立三層危險操作治理架構

  1. 預防層 - CI/CD 攔截危險模式
  2. 執行層 - 危險操作需人工確認
  3. 審計層 - 所有操作記錄可追溯

架構設計

┌─────────────────────────────────────────────────────────────────────┐
│              危險操作治理架構 (Dangerous Operations Governance)       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  Layer 1: 預防層 (Prevention)                               │   │
│  │  ════════════════════════════                               │   │
│  │                                                             │   │
│  │  CI/CD Pipeline                                             │   │
│  │  ├── 危險模式掃描 (grep logOut, deleteWebhook, etc.)        │   │
│  │  ├── 外部服務 API 白名單檢查                                │   │
│  │  └── PR 標記 (涉及危險操作需額外審核)                       │   │
│  │                                                             │   │
│  │  Code Review                                                │   │
│  │  ├── CODEOWNERS: 危險檔案需 CTO/CISO 審核                   │   │
│  │  └── PR Template: 勾選「是否涉及外部服務」                  │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                              │                                      │
│                              ▼                                      │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  Layer 2: 執行層 (Execution Control)                        │   │
│  │  ═══════════════════════════════════                        │   │
│  │                                                             │   │
│  │  危險操作分級                                               │   │
│  │  ├── Tier 0 (禁止): logOut, revokeToken                    │   │
│  │  ├── Tier 1 (需審批): deleteWebhook, setWebhook            │   │
│  │  ├── Tier 2 (需告警): sendMessage (批量)                   │   │
│  │  └── Tier 3 (自動): getMe, getUpdates                      │   │
│  │                                                             │   │
│  │  執行前檢查                                                 │   │
│  │  ├── 產生簽核卡片 (Tier 1 操作)                             │   │
│  │  ├── 發送 Telegram 告警 (Tier 1-2 操作)                     │   │
│  │  └── 記錄到活躍事件                                         │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                              │                                      │
│                              ▼                                      │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  Layer 3: 審計層 (Audit Trail)                              │   │
│  │  ═════════════════════════════                              │   │
│  │                                                             │   │
│  │  記錄內容                                                   │   │
│  │  ├── 操作類型、時間、執行者                                 │   │
│  │  ├── 影響範圍 (哪些服務/Token)                              │   │
│  │  ├── 執行結果 (成功/失敗)                                   │   │
│  │  └── 關聯的 Incident ID                                     │   │
│  │                                                             │   │
│  │  儲存位置                                                   │   │
│  │  ├── PostgreSQL: audit_logs 表                              │   │
│  │  ├── SignOz: Trace 追蹤                                     │   │
│  │  └── 活躍事件: 顯示在 Dashboard                             │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

危險 API 清單

Telegram API

API 分級 說明 處理方式
logOut Tier 0 (禁止) 永久失效 Token CI/CD 攔截,禁止進入生產
close Tier 0 (禁止) 關閉所有 session CI/CD 攔截
deleteWebhook Tier 1 (審批) 可能影響其他服務 需簽核卡片
setWebhook Tier 1 (審批) 可能覆蓋其他設定 需簽核卡片
sendMessage (批量) Tier 2 (告警) 可能 spam 用戶 發送告警
getUpdates Tier 3 (自動) 只能單一實例 限定 OpenClaw
getMe Tier 3 (自動) 無副作用 允許

K8s API

操作 分級 說明 處理方式
kubectl delete namespace Tier 0 (禁止) 刪除整個 namespace 絕對禁止
kubectl delete pvc Tier 1 (審批) 刪除持久化資料 需簽核
kubectl rollout restart Tier 2 (告警) 重啟服務 發送告警
kubectl scale Tier 2 (告警) 調整副本數 發送告警
kubectl get Tier 3 (自動) 只讀操作 允許

Database

操作 分級 說明 處理方式
DROP DATABASE Tier 0 (禁止) 刪除資料庫 絕對禁止
TRUNCATE TABLE Tier 1 (審批) 清空表格 需簽核
DELETE FROM (無 WHERE) Tier 1 (審批) 刪除所有資料 需簽核
ALTER TYPE Tier 2 (告警) 修改 schema 發送告警

實施計畫

Phase 1: CI/CD 攔截 (Week 1)

# .github/workflows/ci.yml
- name: Scan for Dangerous Operations
  run: |
    echo "🔍 掃描危險操作..."

    # Tier 0: 禁止的操作
    BANNED_PATTERNS="logOut|\.close\(\)|revokeToken|DROP DATABASE|TRUNCATE TABLE"

    if grep -rn --include="*.py" -E "$BANNED_PATTERNS" apps/; then
      echo "❌ 發現 Tier 0 禁止操作!"
      echo "請移除以下危險程式碼後重新提交"
      exit 1
    fi

    # Tier 1: 需要審批的操作
    REVIEW_PATTERNS="deleteWebhook|setWebhook|DELETE FROM"

    if grep -rn --include="*.py" -E "$REVIEW_PATTERNS" apps/; then
      echo "⚠️ 發現 Tier 1 操作,需要 CTO/CISO 審核"
      echo "::warning::此 PR 包含危險操作,請確認已通過審核"
    fi

    echo "✅ 危險操作掃描完成"

Phase 2: CODEOWNERS (Week 1)

# .github/CODEOWNERS

# 涉及外部服務的檔案需要額外審核
apps/api/src/services/telegram_*.py @awoooi/cto @awoooi/ciso
apps/api/src/services/executor.py @awoooi/cto @awoooi/ciso
k8s/**/02-network-policy.yaml @awoooi/cio @awoooi/sre

# 危險操作相關
apps/api/src/services/*gateway*.py @awoooi/cto

Phase 3: 執行時控制 (Week 2)

# apps/api/src/core/dangerous_ops.py

from enum import Enum
from typing import Callable, Any
import structlog

logger = structlog.get_logger()

class OperationTier(Enum):
    FORBIDDEN = 0      # 絕對禁止
    REQUIRES_APPROVAL = 1  # 需要簽核
    REQUIRES_ALERT = 2     # 需要告警
    AUTOMATIC = 3          # 自動允許

DANGEROUS_OPERATIONS = {
    "telegram.logOut": OperationTier.FORBIDDEN,
    "telegram.close": OperationTier.FORBIDDEN,
    "telegram.deleteWebhook": OperationTier.REQUIRES_APPROVAL,
    "telegram.setWebhook": OperationTier.REQUIRES_APPROVAL,
    "k8s.deleteNamespace": OperationTier.FORBIDDEN,
    "k8s.deletePVC": OperationTier.REQUIRES_APPROVAL,
    "k8s.rolloutRestart": OperationTier.REQUIRES_ALERT,
}

async def execute_dangerous_operation(
    operation_name: str,
    operation_fn: Callable,
    *args,
    **kwargs
) -> Any:
    """執行危險操作的統一入口"""

    tier = DANGEROUS_OPERATIONS.get(operation_name, OperationTier.AUTOMATIC)

    if tier == OperationTier.FORBIDDEN:
        logger.error(
            "forbidden_operation_blocked",
            operation=operation_name,
        )
        raise PermissionError(f"操作 {operation_name} 被禁止執行")

    if tier == OperationTier.REQUIRES_APPROVAL:
        # 產生簽核卡片,等待 Y/n
        approval = await create_approval_request(
            title=f"危險操作: {operation_name}",
            description=f"即將執行 {operation_name},需要人工確認",
            tier="Tier 1",
        )
        if not approval.approved:
            raise PermissionError("操作被拒絕")

    if tier in [OperationTier.REQUIRES_APPROVAL, OperationTier.REQUIRES_ALERT]:
        # 發送告警
        await send_telegram_alert(
            f"⚠️ 危險操作執行中: {operation_name}"
        )

    # 記錄審計日誌
    await log_audit_trail(
        operation=operation_name,
        tier=tier.name,
        args=str(args),
        kwargs=str(kwargs),
    )

    # 執行操作
    result = await operation_fn(*args, **kwargs)

    # 記錄結果
    await log_audit_trail(
        operation=operation_name,
        result="success",
    )

    return result

Phase 4: 審計日誌 (Week 2)

-- 新增審計日誌表
CREATE TABLE IF NOT EXISTS audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    operation VARCHAR(255) NOT NULL,
    tier VARCHAR(50) NOT NULL,
    actor VARCHAR(255),  -- 執行者 (系統/用戶)
    target VARCHAR(255),  -- 影響目標
    args JSONB,
    result VARCHAR(50),  -- success/failure/blocked
    error_message TEXT,
    incident_id VARCHAR(50),  -- 關聯的 Incident
    trace_id VARCHAR(50),  -- SignOz Trace ID
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_audit_logs_operation ON audit_logs(operation);
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);
CREATE INDEX idx_audit_logs_tier ON audit_logs(tier);

變更管理流程

Token/Secret 更新 SOP

┌─────────────────────────────────────────────────────────────────┐
│  Token/Secret 更新 SOP                                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Step 1: 停止所有使用該 Token 的服務                            │
│  ────────────────────────────────────                          │
│  - kubectl scale deployment --replicas=0                       │
│  - docker stop <container>                                     │
│  - 等待所有實例完全終止                                        │
│                                                                 │
│  Step 2: 驗證沒有殘留實例                                       │
│  ────────────────────────────                                  │
│  - kubectl get pods (確認沒有 Running/Terminating)             │
│  - docker ps (確認沒有相關容器)                                │
│                                                                 │
│  Step 3: 取得新 Token                                           │
│  ────────────────────────                                      │
│  - @BotFather → Revoke current token                           │
│  - 複製新 Token                                                │
│                                                                 │
│  Step 4: 更新到唯一的服務                                       │
│  ────────────────────────                                      │
│  - 更新 .env 或 K8s Secret                                     │
│  - 記錄到 memory/reference_telegram_token.md                   │
│                                                                 │
│  Step 5: 啟動服務並驗證                                         │
│  ────────────────────────                                      │
│  - 啟動服務                                                    │
│  - 檢查日誌確認無錯誤                                          │
│  - 測試 Telegram 連線                                          │
│                                                                 │
│  Step 6: 更新文檔                                               │
│  ────────────────                                              │
│  - 更新 memory 相關 MD                                         │
│  - 發送 Telegram 確認訊息                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

驗收標準

項目 狀態
CI/CD 危險模式掃描
CODEOWNERS 危險檔案審核
Tier 0 操作絕對禁止
Tier 1 操作需簽核卡片
Tier 2 操作發送告警
審計日誌表建立
Token 更新 SOP 文檔
Memory 文檔更新

附錄: 2026-03-23 事故完整時間線

11:09:47 - AWOOOI API 啟動,呼叫 logOut (第一個 Token 失效)
11:09:52 - OpenClaw 嘗試啟動失敗 "Logged out"
...
19:31:16 - 給第二個 Token
19:31:27 - 舊 AWOOOI Pod 呼叫 logOut (第二個 Token 失效)
19:31:31 - OpenClaw 啟動失敗 "Logged out"
19:33:00 - 新 AWOOOI Pod 部署完成 (但 Token 已死)
...
19:35:00 - 清空 AWOOOI API Telegram Token
19:35:30 - 重啟 AWOOOI API (不再碰 Telegram)
19:39:01 - 給第三個 Token
19:39:17 - OpenClaw 啟動成功
19:39:36 - 所有系統健康