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>
648 lines
19 KiB
Markdown
648 lines
19 KiB
Markdown
# GUARDiA ITSM — 라이선스 키 발급 및 적용 가이드
|
|
|
|
> **문서번호**: GUARDIA-LIC-001
|
|
> **버전**: 1.0
|
|
> **작성일**: 2026-05-28
|
|
> **작성자**: GUARDiA 개발팀
|
|
> **보안등급**: 내부용 (대외비) — 마스터 키 및 생성 절차는 벤더 내부 한정
|
|
|
|
---
|
|
|
|
## 목차
|
|
|
|
1. [라이선스 시스템 개요](#1-라이선스-시스템-개요)
|
|
2. [에디션별 제한 및 기능](#2-에디션별-제한-및-기능)
|
|
3. [마스터 키 생성 및 보관](#3-마스터-키-생성-및-보관)
|
|
4. [라이선스 키 발급 (벤더 전용)](#4-라이선스-키-발급-벤더-전용)
|
|
5. [라이선스 키 고객 적용 절차](#5-라이선스-키-고객-적용-절차)
|
|
6. [라이선스 갱신 절차](#6-라이선스-갱신-절차)
|
|
7. [라이선스 상태 모니터링](#7-라이선스-상태-모니터링)
|
|
8. [CLI 레퍼런스](#8-cli-레퍼런스)
|
|
9. [API 레퍼런스](#9-api-레퍼런스)
|
|
10. [문제 해결](#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 라이선스 페이로드 구성
|
|
|
|
```json
|
|
{
|
|
"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회 생성**하여 안전하게 보관한다.
|
|
|
|
```bash
|
|
# 방법 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 파일 설정 (운영 서버)
|
|
|
|
```ini
|
|
# C:\GUARDiA\itsm\.env (권한: 소유자 읽기/쓰기만)
|
|
GUARDIA_LICENSE_KEY=a3f8c2d1e5b4970682f1a9c3d7e2b5f4180c6e9a2d4b7f0e3c8a5d2b1f9e607
|
|
```
|
|
|
|
```bash
|
|
# 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를 이용한 발급
|
|
|
|
```bash
|
|
# 발급 서버(벤더 내부 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 코드를 이용한 발급 (자동화)
|
|
|
|
```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 커스텀 한도 지정 (특수 계약)
|
|
|
|
```python
|
|
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 (자동화/스크립트)
|
|
|
|
```bash
|
|
# 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 파일 사전 구성 (설치 시점 적용)
|
|
|
|
배포 자동화 파이프라인에서 설치 시점에 라이선스를 미리 구성할 때:
|
|
|
|
```ini
|
|
# 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는 새로 자동 생성된다:
|
|
|
|
```bash
|
|
python -m core.license \
|
|
--customer "서울특별시청" \
|
|
--edition ENTERPRISE \
|
|
--days 365 \
|
|
--key <마스터_키>
|
|
|
|
# 기존 라이선스는 자동 비활성화되고 새 라이선스로 교체된다.
|
|
```
|
|
|
|
### 6.3 갱신 적용
|
|
|
|
**웹 UI**: `/license` → 새 `GRD-...` 키 입력 → **라이선스 등록**
|
|
(기존 활성 라이선스는 자동으로 비활성화됨)
|
|
|
|
**API**:
|
|
```bash
|
|
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로 상태 확인
|
|
|
|
```bash
|
|
curl http://localhost:8001/api/license/status \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
```json
|
|
{
|
|
"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 서버 로그 모니터링
|
|
|
|
```bash
|
|
# 시스템 기동 시 라이선스 상태 로그 확인
|
|
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 도움말 출력
|
|
```
|
|
|
|
### 사용 예시
|
|
|
|
```bash
|
|
# 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
|
|
|
|
현재 라이선스 상태 조회 (로그인한 모든 사용자 접근 가능).
|
|
|
|
**응답:**
|
|
```json
|
|
{
|
|
"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 전용)**.
|
|
|
|
**요청 본문:**
|
|
```json
|
|
{ "license_key": "GRD-eyJ..." }
|
|
```
|
|
|
|
**응답 (성공):**
|
|
```json
|
|
{
|
|
"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 전용)**.
|
|
|
|
**응답:**
|
|
```json
|
|
[
|
|
{
|
|
"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년 |
|