zioinfo-mail/workspace/guardia-itsm/routers/golden_config.py
DESKTOP-TKLFCPR\ython b8faec44e0 feat(advanced): GUARDiA 고급 확장 구현 — 20 routers + 754 endpoints
CMDB 자동 발견 (4개):
- autodiscovery.py: SSH 네트워크 스캔 + CMDB 자동 등록
- snmp_discovery.py: SNMP v2c/v3 장비 자동 발견
- dependency_map.py: 서비스 의존성 자동 매핑 (netstat)
- config_inventory.py: 서버 인벤토리 자동 수집 (SSH)

NL 쿼리 엔진 (3개):
- nlquery.py: Text-to-SQL (SELECT 전용, DML 차단)
- op_assistant.py: Multi-turn 대화형 운영 어시스턴트
- query_history.py: 쿼리 이력·즐겨찾기·공유

구성 드리프트 (3개):
- drift_detection.py: 골든 구성 vs 실제 비교·SR 자동 생성
- golden_config.py: 내장 CSAP 템플릿 + 버전 관리
- auto_remediation.py: 승인 기반 자동 교정 + 롤백

멀티클라우드 (4개):
- multicloud.py: 통합 관제 (NCloud+AWS+KT)
- aws_connector.py: AWS SigV4 직접 서명 연동
- cost_optimizer.py: AI 비용 최적화 권고
- cloud_migration.py: On-prem→K-Cloud 체크리스트

공공기관 특화 (6개):
- narasajang.py: 나라장터 OpenAPI 연동
- public_api_hub.py: data.go.kr KISA·기상청 허브
- isp_support.py: ISP 수립 지원 + AI 보고서
- network_zone.py: 행정망/인터넷망 분리 관리
- k_cloud.py: 정부 K-Cloud 전환 자동화
- e_procurement.py: 전자조달 계약·검수·납품

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 14:33:41 +09:00

212 lines
7.8 KiB
Python

"""
골든 구성(Golden Config) 정의·버전 관리
서버 유형별 기대 설정값을 정의하고 버전을 관리한다.
내장 CSAP/보안 템플릿 포함.
엔드포인트:
GET /api/goldenconfig/ — 골든 구성 목록
POST /api/goldenconfig/ — 새 골든 구성 생성
GET /api/goldenconfig/{id} — 상세 조회
PUT /api/goldenconfig/{id} — 수정
DELETE /api/goldenconfig/{id} — 삭제
GET /api/goldenconfig/templates — 내장 템플릿 목록
POST /api/goldenconfig/apply-template — 템플릿 적용
"""
from __future__ import annotations
import json
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from core.auth import get_current_user, require_admin_role
from database import get_db
from models import User, GoldenConfig
router = APIRouter(prefix="/api/goldenconfig", tags=["골든 구성"])
# 내장 보안 템플릿 (CSAP/행안부 보안지침 기반)
BUILTIN_TEMPLATES = {
"linux_security_baseline": {
"name": "Linux 보안 기준선 (행안부 지침)",
"server_type": "LINUX",
"items": [
{"key": "ssh_root_login", "cmd": "grep '^PermitRootLogin' /etc/ssh/sshd_config 2>/dev/null",
"expected": "PermitRootLogin no", "severity": "HIGH", "description": "SSH root 직접 접속 금지",
"auto_fix": "sed -i 's/.*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config && systemctl restart sshd"},
{"key": "password_max_days", "cmd": "grep '^PASS_MAX_DAYS' /etc/login.defs 2>/dev/null",
"expected_regex": "PASS_MAX_DAYS\\s+[1-9][0-9]$", "severity": "MEDIUM", "description": "비밀번호 최대 사용기간 90일 이하",
"auto_fix": "sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs"},
{"key": "password_min_days", "cmd": "grep '^PASS_MIN_DAYS' /etc/login.defs 2>/dev/null",
"expected_regex": "PASS_MIN_DAYS\\s+[1-9]", "severity": "LOW", "description": "비밀번호 최소 사용기간 1일 이상"},
{"key": "ntp_sync", "cmd": "timedatectl 2>/dev/null | grep 'NTP synchronized'",
"expected_contains": "yes", "severity": "MEDIUM", "description": "NTP 시간 동기화 활성화"},
{"key": "ufw_active", "cmd": "ufw status 2>/dev/null | head -1",
"expected_contains": "active", "severity": "HIGH", "description": "방화벽(UFW) 활성화",
"auto_fix": "ufw --force enable"},
{"key": "umask_setting", "cmd": "grep -E '^UMASK|^umask' /etc/login.defs /etc/profile 2>/dev/null | head -1",
"expected_contains": "022", "severity": "MEDIUM", "description": "UMASK 022 이상 설정"},
{"key": "passwd_perm", "cmd": "stat -c '%a' /etc/passwd 2>/dev/null",
"expected": "644", "severity": "HIGH", "description": "/etc/passwd 권한 644"},
{"key": "shadow_perm", "cmd": "stat -c '%a' /etc/shadow 2>/dev/null",
"expected": "640", "severity": "HIGH", "description": "/etc/shadow 권한 640"},
]
},
"web_server_baseline": {
"name": "웹서버 보안 기준선",
"server_type": "WEB",
"items": [
{"key": "nginx_version_hide", "cmd": "grep 'server_tokens' /etc/nginx/nginx.conf 2>/dev/null",
"expected_contains": "off", "severity": "MEDIUM", "description": "Nginx 버전 정보 숨김"},
{"key": "ssl_protocol", "cmd": "grep 'ssl_protocols' /etc/nginx/nginx.conf 2>/dev/null",
"expected_contains": "TLSv1.2", "severity": "HIGH", "description": "TLS 1.2 이상 사용"},
{"key": "http_headers", "cmd": "curl -sI http://localhost 2>/dev/null | grep -i 'x-powered-by\\|server'",
"expected_not_contains": "PHP", "severity": "MEDIUM", "description": "서버 기술 정보 미노출"},
]
},
}
class GoldenConfigCreate(BaseModel):
name: str = Field(..., max_length=200)
server_type: str = Field(..., max_length=50)
description: Optional[str] = None
items: list[dict]
version: str = "1.0"
class ApplyTemplateRequest(BaseModel):
template_keys: list[str]
@router.get("/templates")
async def list_templates(_: User = Depends(get_current_user)):
return [
{"key": k, "name": v["name"], "server_type": v["server_type"],
"item_count": len(v["items"])}
for k, v in BUILTIN_TEMPLATES.items()
]
@router.post("/apply-template")
async def apply_template(
req: ApplyTemplateRequest,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_admin_role),
):
created = []
for key in req.template_keys:
tpl = BUILTIN_TEMPLATES.get(key)
if not tpl:
continue
existing = await db.execute(
select(GoldenConfig).where(
GoldenConfig.tenant_id == user.tenant_id,
GoldenConfig.name == tpl["name"],
)
)
if existing.scalar_one_or_none():
continue
cfg = GoldenConfig(
tenant_id=user.tenant_id,
name=tpl["name"],
server_type=tpl["server_type"],
items_json=json.dumps(tpl["items"], ensure_ascii=False),
version="1.0",
is_active=True,
created_at=datetime.utcnow(),
)
db.add(cfg)
created.append(tpl["name"])
await db.commit()
return {"ok": True, "created": created}
@router.get("/")
async def list_golden_configs(
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
rows = await db.execute(
select(GoldenConfig).where(
GoldenConfig.tenant_id == user.tenant_id,
GoldenConfig.is_active == True,
)
)
cfgs = rows.scalars().all()
return [
{"id": c.id, "name": c.name, "server_type": c.server_type,
"version": c.version,
"item_count": len(json.loads(c.items_json or "[]")),
"created_at": c.created_at}
for c in cfgs
]
@router.post("/")
async def create_golden_config(
req: GoldenConfigCreate,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_admin_role),
):
cfg = GoldenConfig(
tenant_id=user.tenant_id,
name=req.name, server_type=req.server_type,
description=req.description,
items_json=json.dumps(req.items, ensure_ascii=False),
version=req.version, is_active=True,
created_at=datetime.utcnow(),
)
db.add(cfg)
await db.commit()
await db.refresh(cfg)
return {"ok": True, "id": cfg.id}
@router.get("/{config_id}")
async def get_golden_config(
config_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(get_current_user),
):
row = await db.execute(
select(GoldenConfig).where(
GoldenConfig.id == config_id,
GoldenConfig.tenant_id == user.tenant_id,
)
)
cfg = row.scalar_one_or_none()
if not cfg:
raise HTTPException(404)
return {
"id": cfg.id, "name": cfg.name, "server_type": cfg.server_type,
"version": cfg.version,
"items": json.loads(cfg.items_json or "[]"),
"created_at": cfg.created_at,
}
@router.delete("/{config_id}")
async def delete_golden_config(
config_id: int,
db: AsyncSession = Depends(get_db),
user: User = Depends(require_admin_role),
):
row = await db.execute(
select(GoldenConfig).where(
GoldenConfig.id == config_id,
GoldenConfig.tenant_id == user.tenant_id,
)
)
cfg = row.scalar_one_or_none()
if not cfg:
raise HTTPException(404)
cfg.is_active = False
await db.commit()
return {"ok": True}