feat(phase6-9): Complete modular architecture and Agent Teams
Phase 6.4 - Modular Architecture: - Add lewooogo-brain adapters for LLM providers - Add lewooogo-data dual memory (Redis + PostgreSQL) - Implement consensus engine for multi-agent decisions - Add incident memory service for historical context Phase 9 - Agent Teams (Claude Agent SDK): - Add base agent class with Claude Sonnet 4 integration - Implement action planner, blast radius, and security agents - Add agent API endpoints and proposal workflow - Integrate ADR-009 OpenClaw Agent Teams architecture DevOps & CI/CD: - Add GitHub Actions CI/CD workflows (ci.yaml, cd.yaml) - Add pre-commit hooks and secrets baseline - Add docker-compose for local development - Update Kubernetes network policies Frontend Improvements: - Add auto-healing error boundary component - Update i18n messages for agent features - Enhance dual-state incident card with execution feedback Documentation: - Add 7 ADRs covering MCP, design system, architecture decisions - Update ARCHITECTURE_MEMORY.md with modular design - Add GLOBAL_RULES.md and SOUL.md for project identity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
332
apps/api/src/agents/security.py
Normal file
332
apps/api/src/agents/security.py
Normal file
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
Security Agent - 安全風險評估專家
|
||||
=================================
|
||||
|
||||
職責:
|
||||
- 分析提案的安全風險
|
||||
- 檢查權限邊界
|
||||
- 評估潛在漏洞
|
||||
- 回傳風險評分 (0-10)
|
||||
|
||||
符合 ADR-009 SecurityAgent 規範
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
import structlog
|
||||
|
||||
from src.agents.base import AgentResult, AgentStatus, BaseAgent
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Security Result
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@dataclass
|
||||
class SecurityResult(AgentResult):
|
||||
"""
|
||||
SecurityAgent 分析結果
|
||||
|
||||
額外欄位:
|
||||
- risk_score: 風險評分 (0-10, 10 最高風險)
|
||||
- risk_factors: 風險因素列表
|
||||
- permission_issues: 權限問題
|
||||
- recommendations: 安全建議
|
||||
"""
|
||||
risk_score: float = 0.0
|
||||
risk_factors: list[str] = field(default_factory=list)
|
||||
permission_issues: list[str] = field(default_factory=list)
|
||||
recommendations: list[str] = field(default_factory=list)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
"""轉換為 dict"""
|
||||
base = super().to_dict()
|
||||
base.update({
|
||||
"risk_score": self.risk_score,
|
||||
"risk_factors": self.risk_factors,
|
||||
"permission_issues": self.permission_issues,
|
||||
"recommendations": self.recommendations,
|
||||
})
|
||||
return base
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Security Agent
|
||||
# =============================================================================
|
||||
|
||||
|
||||
# 安全規則引擎 (本地快速檢查)
|
||||
SECURITY_RULES: dict[str, dict[str, Any]] = {
|
||||
"delete_operation": {
|
||||
"patterns": ["delete", "rm", "remove", "destroy", "drop"],
|
||||
"risk_score": 8.0,
|
||||
"factor": "破壞性操作: 涉及刪除資源",
|
||||
"recommendation": "確保有備份,並考慮使用 --dry-run 先行測試",
|
||||
},
|
||||
"force_operation": {
|
||||
"patterns": ["--force", "-f", "--no-wait", "--grace-period=0"],
|
||||
"risk_score": 7.0,
|
||||
"factor": "強制操作: 跳過安全確認",
|
||||
"recommendation": "移除 --force 參數,使用標準流程",
|
||||
},
|
||||
"privileged_namespace": {
|
||||
"patterns": ["kube-system", "kube-public", "default"],
|
||||
"risk_score": 9.0,
|
||||
"factor": "敏感命名空間: 操作影響 K8s 核心組件",
|
||||
"recommendation": "確認是否真的需要操作系統命名空間",
|
||||
},
|
||||
"secret_operation": {
|
||||
"patterns": ["secret", "configmap", "credential", "password", "token"],
|
||||
"risk_score": 8.5,
|
||||
"factor": "敏感資料: 操作涉及機密資訊",
|
||||
"recommendation": "確保日誌不會記錄機密內容",
|
||||
},
|
||||
"network_policy": {
|
||||
"patterns": ["networkpolicy", "ingress", "egress", "firewall"],
|
||||
"risk_score": 7.5,
|
||||
"factor": "網路變更: 可能影響服務連通性",
|
||||
"recommendation": "變更前確認流量影響範圍",
|
||||
},
|
||||
"rbac_operation": {
|
||||
"patterns": ["role", "rolebinding", "clusterrole", "serviceaccount"],
|
||||
"risk_score": 9.0,
|
||||
"factor": "權限變更: 操作涉及 RBAC 設定",
|
||||
"recommendation": "最小權限原則,避免過度授權",
|
||||
},
|
||||
"scale_to_zero": {
|
||||
"patterns": ["replicas=0", "replicas 0", "scale --replicas=0"],
|
||||
"risk_score": 8.0,
|
||||
"factor": "服務中斷: 副本數設為 0",
|
||||
"recommendation": "確認是否為計畫性維護",
|
||||
},
|
||||
"rollback": {
|
||||
"patterns": ["rollout undo", "rollback"],
|
||||
"risk_score": 5.0,
|
||||
"factor": "回滾操作: 相對安全但需確認目標版本",
|
||||
"recommendation": "確認回滾目標版本是穩定的",
|
||||
},
|
||||
"restart": {
|
||||
"patterns": ["rollout restart", "restart"],
|
||||
"risk_score": 3.0,
|
||||
"factor": "重啟操作: 低風險但可能造成短暫中斷",
|
||||
"recommendation": "確認服務有足夠副本處理滾動重啟",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class SecurityAgent(BaseAgent[SecurityResult]):
|
||||
"""
|
||||
安全風險評估專家 Agent
|
||||
|
||||
分析流程:
|
||||
1. 本地規則引擎快速掃描 (毫秒級)
|
||||
2. LLM 深度分析 (可選,複雜場景)
|
||||
3. 綜合評分
|
||||
|
||||
使用方式:
|
||||
```python
|
||||
agent = SecurityAgent()
|
||||
result = await agent.analyze({
|
||||
"action": "kubectl delete pod nginx-xxx",
|
||||
"namespace": "awoooi-prod",
|
||||
"affected_services": ["nginx", "frontend"],
|
||||
})
|
||||
print(result.risk_score) # 0-10
|
||||
```
|
||||
"""
|
||||
|
||||
AGENT_NAME = "security-expert"
|
||||
AGENT_DESCRIPTION = "資安專家,評估安全風險與權限影響"
|
||||
AGENT_TOOLS = ["Read", "Grep"] # 只讀權限
|
||||
|
||||
def __init__(self, timeout_sec: float = 30.0, use_llm: bool = False):
|
||||
"""
|
||||
初始化 SecurityAgent
|
||||
|
||||
Args:
|
||||
timeout_sec: 執行超時時間
|
||||
use_llm: 是否啟用 LLM 深度分析 (Phase 9.4 擴展)
|
||||
"""
|
||||
super().__init__(timeout_sec)
|
||||
self.use_llm = use_llm
|
||||
|
||||
async def analyze(self, context: dict[str, Any]) -> SecurityResult:
|
||||
"""
|
||||
執行安全風險分析
|
||||
|
||||
Args:
|
||||
context: 分析上下文
|
||||
- action: 要執行的指令
|
||||
- namespace: 目標命名空間
|
||||
- affected_services: 受影響服務列表
|
||||
- incident_id: 事件 ID (可選)
|
||||
|
||||
Returns:
|
||||
SecurityResult 包含風險評分和詳細分析
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
self.logger.info(
|
||||
"security_analysis_start",
|
||||
action=context.get("action", "")[:100],
|
||||
namespace=context.get("namespace"),
|
||||
)
|
||||
|
||||
try:
|
||||
# Phase 1: 本地規則引擎 (同步、快速)
|
||||
rule_result = self._rule_engine_analyze(context)
|
||||
|
||||
# Phase 2: LLM 深度分析 (可選,未來擴展)
|
||||
if self.use_llm and rule_result["risk_score"] >= 7.0:
|
||||
# 高風險場景啟用 LLM 二次確認
|
||||
# TODO: Phase 9.4 實作 LLM 分析
|
||||
pass
|
||||
|
||||
latency_ms = int((time.time() - start_time) * 1000)
|
||||
|
||||
result = SecurityResult(
|
||||
agent_name=self.AGENT_NAME,
|
||||
status=AgentStatus.SUCCESS,
|
||||
confidence=rule_result["confidence"],
|
||||
analysis=rule_result["analysis"],
|
||||
latency_ms=latency_ms,
|
||||
risk_score=rule_result["risk_score"],
|
||||
risk_factors=rule_result["risk_factors"],
|
||||
permission_issues=rule_result["permission_issues"],
|
||||
recommendations=rule_result["recommendations"],
|
||||
raw_response=rule_result,
|
||||
)
|
||||
|
||||
self.logger.info(
|
||||
"security_analysis_complete",
|
||||
risk_score=result.risk_score,
|
||||
latency_ms=latency_ms,
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
latency_ms = int((time.time() - start_time) * 1000)
|
||||
|
||||
self.logger.exception(
|
||||
"security_analysis_error",
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
return SecurityResult(
|
||||
agent_name=self.AGENT_NAME,
|
||||
status=AgentStatus.FAILED,
|
||||
confidence=0.0,
|
||||
analysis=f"分析失敗: {str(e)}",
|
||||
latency_ms=latency_ms,
|
||||
error=str(e),
|
||||
risk_score=10.0, # 失敗時預設最高風險
|
||||
risk_factors=["分析過程發生錯誤"],
|
||||
recommendations=["請人工審核此操作"],
|
||||
)
|
||||
|
||||
def _rule_engine_analyze(self, context: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
本地規則引擎分析
|
||||
|
||||
快速檢查常見安全模式,毫秒級回應
|
||||
"""
|
||||
action = context.get("action", "").lower()
|
||||
namespace = context.get("namespace", "").lower()
|
||||
affected_services = context.get("affected_services", [])
|
||||
|
||||
risk_factors: list[str] = []
|
||||
recommendations: list[str] = []
|
||||
permission_issues: list[str] = []
|
||||
max_risk_score: float = 0.0
|
||||
|
||||
# 掃描所有安全規則
|
||||
for rule_name, rule in SECURITY_RULES.items():
|
||||
patterns = rule["patterns"]
|
||||
|
||||
# 檢查 action
|
||||
if any(pattern in action for pattern in patterns):
|
||||
risk_factors.append(rule["factor"])
|
||||
recommendations.append(rule["recommendation"])
|
||||
max_risk_score = max(max_risk_score, rule["risk_score"])
|
||||
|
||||
# 檢查 namespace
|
||||
if rule_name == "privileged_namespace":
|
||||
if any(pattern in namespace for pattern in patterns):
|
||||
risk_factors.append(rule["factor"])
|
||||
recommendations.append(rule["recommendation"])
|
||||
max_risk_score = max(max_risk_score, rule["risk_score"])
|
||||
|
||||
# 檢查受影響服務數量
|
||||
if len(affected_services) > 5:
|
||||
risk_factors.append(f"大範圍影響: 涉及 {len(affected_services)} 個服務")
|
||||
max_risk_score = max(max_risk_score, 6.0)
|
||||
recommendations.append("考慮分批執行,降低爆炸半徑")
|
||||
|
||||
# 檢查是否涉及生產環境
|
||||
if "prod" in namespace:
|
||||
if max_risk_score < 5.0:
|
||||
max_risk_score = 5.0 # 生產環境最低風險 5
|
||||
permission_issues.append("操作目標為生產環境")
|
||||
|
||||
# 如果沒有匹配任何規則,給予基礎評分
|
||||
if not risk_factors:
|
||||
risk_factors.append("未偵測到明顯風險因素")
|
||||
max_risk_score = 2.0 # 基礎低風險
|
||||
|
||||
# 計算信心分數 (規則匹配越多,信心越高)
|
||||
confidence = min(0.95, 0.7 + len(risk_factors) * 0.05)
|
||||
|
||||
# 生成分析摘要
|
||||
if max_risk_score >= 8.0:
|
||||
analysis = f"高風險操作 (Score: {max_risk_score}/10): 建議人工審核"
|
||||
elif max_risk_score >= 5.0:
|
||||
analysis = f"中等風險 (Score: {max_risk_score}/10): 確認影響範圍後執行"
|
||||
else:
|
||||
analysis = f"低風險操作 (Score: {max_risk_score}/10): 可安全執行"
|
||||
|
||||
return {
|
||||
"risk_score": max_risk_score,
|
||||
"risk_factors": risk_factors,
|
||||
"recommendations": list(set(recommendations)), # 去重
|
||||
"permission_issues": permission_issues,
|
||||
"confidence": confidence,
|
||||
"analysis": analysis,
|
||||
"rules_matched": len(risk_factors),
|
||||
}
|
||||
|
||||
def _build_prompt(self, context: dict[str, Any]) -> str:
|
||||
"""建構 LLM Prompt (Phase 9.4 擴展)"""
|
||||
return f"""你是 AWOOOI 的資安專家。
|
||||
分析以下操作的安全風險:
|
||||
|
||||
操作指令: {context.get("action", "N/A")}
|
||||
目標命名空間: {context.get("namespace", "N/A")}
|
||||
受影響服務: {", ".join(context.get("affected_services", []))}
|
||||
|
||||
評估:
|
||||
1. 是否涉及敏感資料
|
||||
2. 是否可能被利用
|
||||
3. 權限邊界是否被突破
|
||||
|
||||
輸出 JSON:
|
||||
```json
|
||||
{{
|
||||
"risk_score": 0-10,
|
||||
"risk_factors": ["...", "..."],
|
||||
"permission_issues": ["...", "..."],
|
||||
"recommendations": ["...", "..."],
|
||||
"analysis": "一句話摘要",
|
||||
"confidence": 0-1
|
||||
}}
|
||||
```"""
|
||||
|
||||
def _parse_response(self, response: str) -> dict[str, Any]:
|
||||
"""解析 LLM 回應"""
|
||||
return self._extract_json(response)
|
||||
Reference in New Issue
Block a user