guardia-docs/14_라이선스_키_발급_가이드.md
DESKTOP-TKLFCPRython 938b25f286 feat(itsm): G-1~G-12 확장 기능 + 하네스/봇/설치스크립트 구현
G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현
G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건)
G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST)
G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직
G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트
G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트
G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API
G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트
G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트
G-10: core/push_notify.py + routers/push.py + PushSubscription 모델
G-11: approvals 다중승인 (위임/서명/기한초과/마감연장)
G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh

하네스: guardia-orchestrator 확장기능 Phase 반영
봇명령어: /sr /status /license /bulk 슬래시 명령어 추가
설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 18:18:52 +09:00

19 KiB

GUARDiA ITSM — 라이선스 키 발급 및 적용 가이드

문서번호: GUARDIA-LIC-001
버전: 1.0
작성일: 2026-05-28
작성자: GUARDiA 개발팀
보안등급: 내부용 (대외비) — 마스터 키 및 생성 절차는 벤더 내부 한정


목차

  1. 라이선스 시스템 개요
  2. 에디션별 제한 및 기능
  3. 마스터 키 생성 및 보관
  4. 라이선스 키 발급 (벤더 전용)
  5. 라이선스 키 고객 적용 절차
  6. 라이선스 갱신 절차
  7. 라이선스 상태 모니터링
  8. CLI 레퍼런스
  9. API 레퍼런스
  10. 문제 해결

1. 라이선스 시스템 개요

1.1 보안 설계

GUARDiA 라이선스는 완전 오프라인 검증 방식으로, 외부 인터넷 연결 없이 검증된다.

라이선스 키 구조:
  GRD-{base64url(iv[12B] + ciphertext + gcm_tag[16B] + hmac_sig[8B])}

암호화: AES-256-GCM (iv 12B + GCM tag 16B)
서명:   HMAC-SHA256 (앞 8B prefix — 위변조 즉시 탐지)
키 파생: SHA-256(master_key + "guardia-aes-v1")    → AES 키
        SHA-256(master_key + "guardia-license-hmac-v1") → HMAC 키

검증 흐름:

고객 서버가 키 수신
  │
  ▼ HMAC 서명 검증 (위변조 즉시 차단)
  │
  ▼ AES-256-GCM 복호화 (마스터 키 불일치 시 실패)
  │
  ▼ JSON 페이로드 파싱 (만료일, 에디션, 고객명 등)
  │
  ▼ 만료일 비교 → valid: true / expired: true

1.2 라이선스 페이로드 구성

{
  "license_id":  "GRD-A1B2C3",
  "edition":     "ENTERPRISE",
  "customer":    "서울특별시청",
  "issued_at":   "2026-05-28T00:00:00+00:00",
  "expires_at":  "2027-05-28T00:00:00+00:00",
  "limits": {
    "max_institutions": -1,
    "max_users":        -1,
    "max_servers":      -1,
    "features":         ["MFA", "LDAP", "PAM", "AI_AGENTS",
                         "VULN_SCAN", "CICD", "ANALYTICS", "FINOPS"]
  }
}

2. 에디션별 제한 및 기능

에디션 기관 수 사용자 수 서버 수 활성화 기능 대상
COMMUNITY 1 10 20 MFA 소규모 단일 기관, 평가판
STANDARD 50 200 500 MFA, LDAP, PAM, AI_AGENTS 중규모 기관·지자체
ENTERPRISE 무제한(-1) 무제한(-1) 무제한(-1) 전체 기능 광역단체, 대규모 멀티테넌트

기능별 에디션 요구사항

기능 코드 기능명 최소 에디션
MFA 2단계 인증 (TOTP/OTP) COMMUNITY
LDAP LDAP/AD 디렉토리 연동 STANDARD
PAM 특권 접근 관리 STANDARD
AI_AGENTS AI 에이전트 오케스트레이션 STANDARD
VULN_SCAN 보안 취약점 자동 스캔 ENTERPRISE
CICD CI/CD Jenkins 연동 ENTERPRISE
ANALYTICS 고급 분석 대시보드 ENTERPRISE
FINOPS 비용 분석 (FinOps) ENTERPRISE

3. 마스터 키 생성 및 보관

경고: 마스터 키는 절대 외부 유출 금지. 분실 시 기존 발급 라이선스 모두 무효화됨.

3.1 마스터 키 생성

마스터 키는 GUARDiA 벤더 내부 시스템에서 1회 생성하여 안전하게 보관한다.

# 방법 1: Python (권장)
python3 -c "import secrets; print(secrets.token_hex(32))"

# 방법 2: OpenSSL
openssl rand -hex 32

# 결과 예시 (64자리 hex = 32바이트):
# a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607

3.2 마스터 키 보관 정책

보관 방법 설명
운영 서버 환경변수 GUARDIA_LICENSE_KEY — systemd 서비스 파일 또는 .env (권한 600)
비상 백업 오프라인 금고 (USB 암호화 드라이브)
접근 권한 벤더 최고 관리자 2인 이상 알 고리즘 — 단독 접근 금지

3.3 .env 파일 설정 (운영 서버)

# C:\GUARDiA\itsm\.env (권한: 소유자 읽기/쓰기만)
GUARDIA_LICENSE_KEY=a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
# Linux: 파일 권한 보호
chmod 600 /opt/guardia/itsm/.env
chown guardia:guardia /opt/guardia/itsm/.env

4. 라이선스 키 발급 (벤더 전용)

이 절차는 GUARDiA 벤더(개발사) 내부 운영자만 실행한다.

4.1 발급 전 확인 사항

□ 고객명 (정식 기관명, 계약서 표기 명칭과 동일)
□ 에디션 (COMMUNITY / STANDARD / ENTERPRISE)
□ 유효 기간 (일수)  예: 365일(1년), 730일(2년)
□ 커스텀 한도 여부 (특수 계약 시: max_institutions, max_users, max_servers 별도 지정)
□ GUARDIA_LICENSE_KEY 환경변수 설정 확인

4.2 CLI를 이용한 발급

# 발급 서버(벤더 내부 PC)에서 실행
cd C:\GUARDiA\itsm

# 방법 1: 환경변수에 마스터 키 설정 후 발급
$env:GUARDIA_LICENSE_KEY = "a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607"
python -m core.license --customer "서울특별시청" --edition ENTERPRISE --days 365

# 방법 2: --key 인수로 직접 지정 (환경변수 없어도 가능)
python -m core.license \
  --customer "인천광역시청" \
  --edition STANDARD \
  --days 730 \
  --key a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607

# 커스텀 라이선스 ID 지정 (선택)
python -m core.license --customer "부산광역시청" --edition ENTERPRISE --days 365 --lid GRD-BUSAN2026

4.3 발급 결과 예시

============================================================
  GUARDiA 라이선스 키 생성 완료
============================================================
  고객명    : 서울특별시청
  에디션    : ENTERPRISE
  라이선스ID: GRD-A1B2C3
  발급일시  : 2026-05-28T00:00:00+00:00
  만료일시  : 2027-05-28T00:00:00+00:00
  유효기간  : 365일
============================================================

라이선스 키:
GRD-eyJsaWNlbnNlX2lkIjoiR1JELUExQjJDMyIsImVkaXRpb24iOiJFTlRFUlBSSVNFIn0...

4.4 Python 코드를 이용한 발급 (자동화)

from datetime import datetime, timedelta, timezone
from core.license import generate_license_key, validate_license, LicenseEdition

MASTER_KEY = "a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607"

# 1년짜리 ENTERPRISE 라이선스 생성
lic_key = generate_license_key(
    customer       = "서울특별시청",
    edition        = LicenseEdition.ENTERPRISE,
    expires_at     = datetime.now(timezone.utc) + timedelta(days=365),
    master_key_hex = MASTER_KEY,
)
print(f"키: {lic_key}")

# 검증
status = validate_license(lic_key, master_key_hex=MASTER_KEY)
print(f"유효: {status['valid']}, 남은 일수: {status['days_remaining']}")

4.5 커스텀 한도 지정 (특수 계약)

custom_limits = {
    "max_institutions": 10,     # 기관 10개
    "max_users":        100,    # 사용자 100명
    "max_servers":      200,    # 서버 200대
    "features":         ["MFA", "LDAP", "PAM"],
}

lic_key = generate_license_key(
    customer       = "경기도청",
    edition        = LicenseEdition.STANDARD,
    expires_at     = datetime.now(timezone.utc) + timedelta(days=365),
    custom_limits  = custom_limits,
    master_key_hex = MASTER_KEY,
)

5. 라이선스 키 고객 적용 절차

5.1 방법 A: 웹 UI (권장)

  1. ITSM 관리자 계정으로 로그인
  2. 사이드바 🔏 라이선스 관리 클릭 → /license
  3. 라이선스 키 등록/갱신 섹션에 GRD-... 키 붙여넣기
  4. 라이선스 등록 버튼 클릭
  5. 상단 배너에서 에디션·만료일 확인
[상태 배너 예시]
✅ ENTERPRISE 라이선스 활성 — 365일 남음 (서울특별시청)

5.2 방법 B: API (자동화/스크립트)

# 1. 관리자 로그인 후 토큰 획득
TOKEN=$(curl -s -X POST http://localhost:8001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"Admin!1234"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# 2. 라이선스 등록
curl -X POST http://localhost:8001/api/license/activate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "license_key": "GRD-eyJsaWNlbnNlX2lkIjoiR1JELUExQjJDMyI..."
  }'

# 성공 응답 예시:
# {
#   "message": "ENTERPRISE 라이선스가 활성화되었습니다 (365일 남음).",
#   "license_id": "GRD-A1B2C3",
#   "edition": "ENTERPRISE",
#   "customer": "서울특별시청",
#   "expires_at": "2027-05-28T00:00:00+00:00",
#   "days_remaining": 365,
#   "valid": true
# }

5.3 방법 C: .env 파일 사전 구성 (설치 시점 적용)

배포 자동화 파이프라인에서 설치 시점에 라이선스를 미리 구성할 때:

# C:\GUARDiA\itsm\.env
SECRET_KEY=<jwt-secret>
GUARDIA_LICENSE_KEY=<마스터_키_64자리_hex>

서버 기동 시 ITSM이 자동으로 라이선스 상태를 출력한다:

[LICENSE] ENTERPRISE 라이선스 활성 (365일 남음) — 서울특별시청

단, .env에 마스터 키만 있고 DB에 라이선스 레코드가 없으면 활성화는 안 된다.
DB에 키를 등록하는 것은 API 호출(방법 A/B) 필수.


6. 라이선스 갱신 절차

6.1 갱신 타이밍

시점 시스템 반응 권장 조치
만료 30일 전 웹 UI 황색 배너 경고, 서버 시작 시 경고 로그 갱신 라이선스 발급 준비
만료 7일 전 매일 경고 즉시 갱신 신청
만료 당일 expired: true, 기능 제한 시작 긴급 갱신
만료 후 기존 데이터 보존, 신규 등록만 차단 갱신 라이선스 즉시 적용

중요: 만료 후에도 기존 등록된 기관·서버·사용자 데이터는 삭제되지 않는다.
신규 생성(기관 추가, 서버 추가)만 한도 초과 시 차단된다.

6.2 갱신 라이선스 발급

동일 CLI로 새 키를 발급하면 된다. 라이선스 ID는 새로 자동 생성된다:

python -m core.license \
  --customer "서울특별시청" \
  --edition ENTERPRISE \
  --days 365 \
  --key <마스터_키>

# 기존 라이선스는 자동 비활성화되고 새 라이선스로 교체된다.

6.3 갱신 적용

웹 UI: /license → 새 GRD-... 키 입력 → 라이선스 등록
(기존 활성 라이선스는 자동으로 비활성화됨)

API:

curl -X POST http://localhost:8001/api/license/activate \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"license_key": "GRD-<새_키>"}'

7. 라이선스 상태 모니터링

7.1 웹 UI 상태 배너

/license 페이지의 상단 배너로 즉시 확인:

배너 색상 의미
초록 정상 활성
노랑 만료 30일 이내 경고
빨강 만료됨
회색 라이선스 미등록 (Community 제한 모드)

7.2 사용량 현황

/license 페이지 사용량 현황 섹션에서 기관/사용자/서버 현재 수 vs 한도를 확인할 수 있다:

기관  [██████████░] 48/50
사용자 [████░░░░░░░] 80/200
서버  [███░░░░░░░░] 150/500

7.3 API로 상태 확인

curl http://localhost:8001/api/license/status \
  -H "Authorization: Bearer $TOKEN"
{
  "activated": true,
  "valid": true,
  "expired": false,
  "expiry_warning": false,
  "license_id": "GRD-A1B2C3",
  "edition": "ENTERPRISE",
  "customer": "서울특별시청",
  "issued_at": "2026-05-28T00:00:00+00:00",
  "expires_at": "2027-05-28T00:00:00+00:00",
  "days_remaining": 365,
  "limits": {
    "max_institutions": -1,
    "max_users": -1,
    "max_servers": -1,
    "features": ["MFA","LDAP","PAM","AI_AGENTS","VULN_SCAN","CICD","ANALYTICS","FINOPS"]
  },
  "message": "ENTERPRISE 라이선스 활성 (365일 남음)"
}

7.4 서버 로그 모니터링

# 시스템 기동 시 라이선스 상태 로그 확인
journalctl -u guardia-itsm -n 50 | grep LICENSE

# 정상:
# [LICENSE] ENTERPRISE 라이선스 활성 (365일 남음) — 서울특별시청

# 경고:
# [LICENSE] 경고: 라이선스 만료 25일 남음

# 만료:
# [LICENSE] 경고: 라이선스가 만료되었습니다. 갱신이 필요합니다.

# 미설정:
# [LICENSE] GUARDIA_LICENSE_KEY 미설정 — Community 모드로 실행됩니다.

8. CLI 레퍼런스

usage: python -m core.license [-h] --customer CUSTOMER
                               --edition {COMMUNITY,STANDARD,ENTERPRISE}
                               --days DAYS
                               [--lid LID]
                               [--key KEY]

GUARDiA 라이선스 키 생성 도구

필수 인수:
  --customer  고객/기관 이름 (예: "서울특별시청")
  --edition   라이선스 에디션: COMMUNITY | STANDARD | ENTERPRISE
  --days      유효 기간 (일, 예: 365)

선택 인수:
  --lid       라이선스 ID 직접 지정 (기본: GRD-{6자리랜덤hex})
  --key       마스터 키 hex 64자리 (기본: GUARDIA_LICENSE_KEY 환경변수)
  -h, --help  도움말 출력

사용 예시

# ENTERPRISE 1년
python -m core.license --customer "서울특별시청" --edition ENTERPRISE --days 365

# STANDARD 2년
python -m core.license --customer "경기도청" --edition STANDARD --days 730

# COMMUNITY 30일 평가판
python -m core.license --customer "테스트기관" --edition COMMUNITY --days 30

# 마스터 키 직접 지정 (환경변수 없을 때)
python -m core.license --customer "부산광역시청" --edition ENTERPRISE --days 365 \
  --key a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607

# 커스텀 라이선스 ID
python -m core.license --customer "인천광역시청" --edition STANDARD --days 365 \
  --lid GRD-INCHEON26

9. API 레퍼런스

모든 API는 Authorization: Bearer <JWT_TOKEN> 헤더 필요.
ADMIN 전용 API는 ADMIN 역할 계정만 호출 가능.

GET /api/license/status

현재 라이선스 상태 조회 (로그인한 모든 사용자 접근 가능).

응답:

{
  "activated": true,
  "valid": true,
  "expired": false,
  "expiry_warning": false,
  "license_id": "GRD-A1B2C3",
  "edition": "ENTERPRISE",
  "customer": "서울특별시청",
  "issued_at": "2026-05-28T00:00:00+00:00",
  "expires_at": "2027-05-28T00:00:00+00:00",
  "days_remaining": 365,
  "limits": { ... },
  "message": "ENTERPRISE 라이선스 활성 (365일 남음)"
}

POST /api/license/activate

라이선스 키 등록 및 활성화 (ADMIN 전용).

요청 본문:

{ "license_key": "GRD-eyJ..." }

응답 (성공):

{
  "message": "ENTERPRISE 라이선스가 활성화되었습니다 (365일 남음).",
  "license_id": "GRD-A1B2C3",
  "edition": "ENTERPRISE",
  "customer": "서울특별시청",
  "expires_at": "2027-05-28T00:00:00+00:00",
  "days_remaining": 365,
  "valid": true
}

오류:

코드 원인
400 키 형식 오류, 서명 불일치, 복호화 실패
403 ADMIN 권한 없음
500 GUARDIA_LICENSE_KEY 환경변수 미설정

POST /api/license/verify

등록 없이 키 검증만 수행 (ADMIN 전용).

요청 본문: { "license_key": "GRD-eyJ..." }

응답: 라이선스 페이로드 전체 반환 (status와 동일 구조)


DELETE /api/license/

활성 라이선스 비활성화 (ADMIN 전용).
시스템이 Community 제한 모드로 전환된다.

응답: { "message": "라이선스가 비활성화되었습니다..." }


GET /api/license/history

등록 이력 전체 조회 (ADMIN 전용).

응답:

[
  {
    "id": 2,
    "license_id": "GRD-A1B2C3",
    "edition": "ENTERPRISE",
    "customer": "서울특별시청",
    "issued_at": "2026-05-28T00:00:00",
    "expires_at": "2027-05-28T00:00:00",
    "is_active": true,
    "activated_by": "admin",
    "activated_at": "2026-05-28T09:00:00"
  },
  {
    "id": 1,
    "license_id": "GRD-OLD001",
    "edition": "STANDARD",
    "is_active": false,
    ...
  }
]

10. 문제 해결

10.1 "GUARDIA_LICENSE_KEY 환경변수가 설정되지 않았습니다"

원인: 서버 .env 파일에 GUARDIA_LICENSE_KEY가 없거나 서비스가 .env를 읽지 못함
해결:
  1. .env 파일에 키 추가:
     GUARDIA_LICENSE_KEY=<64자리 hex>
  2. 서비스 재시작:
     systemctl restart guardia-itsm      (Linux)
     Restart-Service GUARDiA-ITSM        (Windows)

10.2 "라이선스 서명 검증 실패 — 위변조 또는 잘못된 키"

원인 1: 키가 다른 마스터 키로 생성됨 (고객사 서버의 GUARDIA_LICENSE_KEY와 발급 시 키가 다름)
원인 2: 키 문자열이 복사 중 잘림
해결:
  1. 키를 처음부터 끝까지 정확히 복사했는지 확인 (앞뒤 공백 없이)
  2. 발급 시 사용한 마스터 키 == 운영 서버의 GUARDIA_LICENSE_KEY 확인
  3. 다른 환경에서 발급된 키라면 해당 환경의 마스터 키로 서버를 재설정

10.3 "라이선스 복호화 실패 — 마스터 키 불일치"

원인: HMAC 통과했으나 AES 복호화 실패 — 마스터 키 파생 문자가 다름
해결: 마스터 키가 정확히 64자리 hex인지 확인
     python3 -c "print(len('YOUR_KEY'))"  # 반드시 64 출력

10.4 "라이선스 한도 초과: ENTERPRISE 에디션으로 업그레이드하세요"

원인: 현재 등록된 기관/서버/사용자 수가 에디션 한도를 초과함
해결:
  방법 1: 상위 에디션 라이선스 발급 후 적용
  방법 2: 불필요한 기관/서버를 삭제하여 한도 이하로 줄임

10.5 만료된 라이선스 등록 시도

동작: 만료된 키도 DB에 등록 가능하나 valid: false, expired: true 반환됨
      등록 시 경고 메시지: "경고: 만료된 라이선스입니다 (만료일: ...). 갱신이 필요합니다."
해결: 새 라이선스를 발급받아 즉시 교체 등록

10.6 라이선스 캐시 갱신 안 됨 (변경 내용 미반영)

원인: 인메모리 캐시(TTL 1시간)가 남아있어 이전 상태를 반환
해결: 라이선스를 activate/deactivate하면 캐시가 자동 무효화됨
     긴급 시 ITSM 서비스 재시작으로 강제 캐시 초기화

부록 A: 라이선스 발급 체크리스트 (벤더용)

고객사 발급 전 체크리스트:
□ 계약서에 명시된 기관명과 동일한지 확인
□ 에디션이 계약 내용과 일치하는지 확인
□ 유효 기간이 계약 기간과 일치하는지 확인
□ 마스터 키가 해당 고객사 서버에 적용된 키와 동일한지 확인
□ 발급된 키를 암호화된 채널(이메일 암호화, 보안 메신저)로 전달
□ 발급 이력 대장에 기록 (날짜, 고객명, 에디션, 만료일, 발급자)

부록 B: 라이선스 발급 이력 대장 양식

번호 발급일 고객명 에디션 라이선스 ID 만료일 발급자 비고
001 2026-05-28 서울특별시청 ENTERPRISE GRD-A1B2C3 2027-05-28 홍길동 신규
002 2026-05-28 경기도청 STANDARD GRD-D4E5F6 2028-05-28 홍길동 신규 2년