- CLAUDE.md: project context and architecture spec - docs/: system specs, DB schema, messenger integration, deployment engine - skills/: guardia-deploy, guardia-agent, guardia-messenger - .claude/settings.json: project-level permissions - .gitignore: Python/FastAPI project Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
184 lines
6.3 KiB
Markdown
184 lines
6.3 KiB
Markdown
# [Specification] 보안 & 거버넌스 정책
|
|
|
|
---
|
|
|
|
## 1. RBAC 권한 체계
|
|
|
|
### 1.1. 역할 정의
|
|
|
|
| 역할 | 권한 | 담당 |
|
|
|------|------|------|
|
|
| Developer | 소관 사이트 배포 요청 | 파일 전송 + 재기동 |
|
|
| SM 운영자 | 로그 조회, 자원 정비, 크론 관리 | 운영 유지보수 |
|
|
| 팀장 (Owner) | 1차 배포 승인 | 소관 시스템 |
|
|
| 대리 (Deputy) | 팀장 부재 시 대리 승인 | |
|
|
| PM | 최종 HITL 검증, 거버넌스 규칙 수정 | 전체 통제 |
|
|
| 보안책임자 | 인프라 변경/보안 작업 3차 승인 | |
|
|
|
|
### 1.2. 다단계 승인 워크플로우
|
|
|
|
```python
|
|
APPROVAL_POLICY = {
|
|
"MAINTENANCE": {"steps": 1, "approvers": ["OWNER"]},
|
|
"DEPLOY_NORMAL": {"steps": 2, "approvers": ["OWNER", "PM"]},
|
|
"DEPLOY_SECURE": {"steps": 3, "approvers": ["OWNER", "PM", "SECURITY"]},
|
|
}
|
|
|
|
def get_approval_policy(task_category: str) -> dict:
|
|
if task_category in ["LOG_ANALYSIS", "CRON_MANAGE"]:
|
|
return APPROVAL_POLICY["MAINTENANCE"]
|
|
elif task_category in ["DEPLOY", "DATA_CLEAN"]:
|
|
return APPROVAL_POLICY["DEPLOY_NORMAL"]
|
|
else: # SSL, INFRA_CHANGE
|
|
return APPROVAL_POLICY["DEPLOY_SECURE"]
|
|
```
|
|
|
|
---
|
|
|
|
## 2. 계층적 승인 엔진 (에스컬레이션)
|
|
|
|
```python
|
|
def get_approver_chain(system_id: str) -> str:
|
|
"""SLA 초과 시 자동 에스컬레이션"""
|
|
info = db.execute(
|
|
"SELECT owner_id, deputy_id, supervisor_id, sla_minutes, is_active "
|
|
"FROM TB_SYSTEM_OWNER WHERE system_id = %s", (system_id,)
|
|
).fetchone()
|
|
|
|
if info["is_active"] == "Y":
|
|
return info["owner_id"] # 1순위: 담당 팀장
|
|
elif info["deputy_id"]:
|
|
return info["deputy_id"] # 2순위: 대리 승인자
|
|
else:
|
|
return info["supervisor_id"] # 3순위: 상위 관리자
|
|
|
|
def check_escalation(task_id: str):
|
|
"""SLA 초과 여부 체크 → 자동 이관"""
|
|
task = db.get_task(task_id)
|
|
owner_info = db.get_system_owner(task["system_id"])
|
|
|
|
elapsed = (datetime.now() - task["created_at"]).seconds / 60
|
|
if elapsed > owner_info["sla_minutes"] and task["status"] == "PENDING_APPROVAL":
|
|
next_approver = get_approver_chain(task["system_id"])
|
|
db.update_task_approver(task_id, next_approver, "ESCALATED")
|
|
messenger.send_urgent_notification(next_approver, f"[긴급] {task_id} 승인 대기")
|
|
audit.log_action(task_id, f"Escalated to {next_approver}")
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 자격증명 보안
|
|
|
|
### 3.1. 암호화 저장 정책
|
|
```
|
|
- SSH/FTP 비밀번호: AES-256 암호화 → TB_SERVER_INFO.os_pw_enc
|
|
- 실행 시점에만 메모리 로드, 로그 출력 금지
|
|
- 메신저 응답창 노출 절대 금지
|
|
- 주기적 비밀번호 갱신 시 DB 암호화 업데이트
|
|
```
|
|
|
|
### 3.2. SSH 접속 보안
|
|
```bash
|
|
# opsagent 계정 sudoers 설정 (대상 서버에 1회 설정)
|
|
# /etc/sudoers.d/opsagent
|
|
opsagent ALL=(ALL) NOPASSWD: /usr/bin/kill, /usr/bin/systemctl restart *, /app/scripts/shutdown.sh, /app/scripts/startup.sh
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 감사 로그 해시 체이닝
|
|
|
|
```python
|
|
import hashlib, json
|
|
|
|
def compute_audit_hash(log_data: dict, prev_hash: str) -> str:
|
|
"""SHA-256 해시 체인 생성"""
|
|
content = json.dumps(log_data, ensure_ascii=False, sort_keys=True)
|
|
combined = f"{content}{prev_hash}"
|
|
return hashlib.sha256(combined.encode()).hexdigest()
|
|
|
|
def log_execution(task_id: str, server_ip: str, command: str, exit_code: int, result: str):
|
|
"""감사 로그 저장 — 이전 로그의 해시를 체인에 포함"""
|
|
prev = db.get_last_audit_log()
|
|
prev_hash = prev["result_hash"] if prev else "GENESIS"
|
|
|
|
log_data = {
|
|
"task_id": task_id,
|
|
"server_ip": server_ip,
|
|
"command": command,
|
|
"exit_code": exit_code,
|
|
"exec_time": datetime.utcnow().isoformat()
|
|
}
|
|
new_hash = compute_audit_hash(log_data, prev_hash)
|
|
|
|
db.insert_audit_log({**log_data, "result": result, "result_hash": new_hash})
|
|
|
|
def verify_audit_integrity() -> bool:
|
|
"""해시 체인 무결성 검증 — 위변조 탐지"""
|
|
logs = db.get_all_audit_logs_ordered()
|
|
prev_hash = "GENESIS"
|
|
for log in logs:
|
|
expected_hash = compute_audit_hash(
|
|
{k: v for k, v in log.items() if k != "result_hash"}, prev_hash
|
|
)
|
|
if expected_hash != log["result_hash"]:
|
|
alert_security_breach(log["log_id"])
|
|
return False
|
|
prev_hash = log["result_hash"]
|
|
return True
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Kill-Switch (비상 정지)
|
|
|
|
```python
|
|
@app.post("/api/emergency/kill")
|
|
async def emergency_kill(operator_id: str, reason: str):
|
|
"""메신저 '정지' 명령 → 전체 배포 프로세스 즉시 중단"""
|
|
# 진행 중인 모든 IN_PROGRESS 태스크 중단
|
|
active_tasks = db.get_tasks_by_status("IN_PROGRESS")
|
|
for task in active_tasks:
|
|
task_manager.abort(task["task_id"])
|
|
trigger_rollback(task["task_id"])
|
|
|
|
# 감사 로그
|
|
log_execution("EMERGENCY", "ALL", f"KILL_SWITCH by {operator_id}", 0, reason)
|
|
|
|
# PM에게 알림
|
|
messenger.send_to_all_pm(f"🚨 비상 정지 실행: {operator_id} — {reason}")
|
|
|
|
return {"status": "KILLED", "aborted_tasks": len(active_tasks)}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 보안 감사 체크리스트 (GS 인증 대응)
|
|
|
|
| 항목 | 검증 방법 | 주기 |
|
|
|------|-----------|------|
|
|
| 로그 무결성 | 해시 체인 전체 역추적 | 매일 00:00 |
|
|
| 계정 잠금 탐지 | faillock 명령어 원격 스캔 | 1시간마다 |
|
|
| 설정 파일 변조 | SHA-256 Checksum 비교 | 1시간마다 |
|
|
| 인증서 만료 | openssl enddate 파싱 | 매일 04:00 |
|
|
| 디스크 임계치 | df -h 자동 스캔 | 5분마다 |
|
|
| 비인가 포트 | ss -tlnp 스캔 | 1시간마다 |
|
|
|
|
---
|
|
|
|
## 7. 데이터 무결성 보호 (배포 중 롤백)
|
|
|
|
```python
|
|
class IntegrityShield:
|
|
def protect_rollback(self, task_id: str, executor: SSHExecutor):
|
|
"""롤백 수행 시에도 감사 기록 보존"""
|
|
# 롤백 직전 현재 해시 상태 기록
|
|
log_execution(task_id, executor.host, "ROLLBACK_START", 0, "롤백 시작")
|
|
# 실제 롤백
|
|
result = executor.execute_command("sh /app/scripts/rollback.sh")
|
|
if result["exit_code"] != 0:
|
|
# 롤백 실패 → 수동 점검 필요 상태로 변경 (다음 배포 차단)
|
|
db.update_task_status(task_id, "MANUAL_INTERVENTION_REQUIRED")
|
|
messenger.send_critical_alert(f"🔴 {task_id} 롤백 실패 — 수동 점검 필요")
|
|
```
|