# [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} 롤백 실패 — 수동 점검 필요") ```