guardia-docs/22_GUARDiA_개방망_운영가이드.md
DESKTOP-TKLFCPRython 2bd7d876cc refactor: 101.79.17.164 → zioinfo.co.kr 전체 도메인 변환 + Manager UI 배포
- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스)
- Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포
- 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 10:09:17 +09:00

455 lines
13 KiB
Markdown

# GUARDiA ITSM 개방망 운영 가이드
> **버전**: 2.0.0 | **작성일**: 2026-05-30
> **서버**: zio-server (zioinfo.co.kr)
---
## 1. 개요
GUARDiA ITSM은 기본적으로 **폐쇄망(Closed Network)** 환경에서 동작하도록 설계되어 있습니다.
본 가이드는 GUARDiA를 **개방망(Open Network)** 에서도 안전하게 서비스하기 위한 구성 방법을 설명합니다.
### 1-1. 폐쇄망 vs 개방망 비교
| 항목 | 폐쇄망 (기본) | 개방망 (이 가이드) |
|------|-------------|----------------|
| 접근 범위 | 내부망 only | 인터넷 외부 접근 허용 |
| CORS | localhost 만 허용 | 지정 외부 도메인 허용 |
| HTTPS | 선택 | **필수** |
| API 인증 | JWT | JWT + **API Key** 추가 |
| 외부 AI 호출 | 금지 (Ollama only) | 금지 유지 (변경 불가) |
| Rate Limiting | 기본 | **강화** (30req/min) |
| 보안 헤더 | 기본 | HSTS 포함 강화 |
### 1-2. 개방망 지원 아키텍처
```
외부 클라이언트 (브라우저, 메신저봇, 외부시스템)
│ HTTPS (443 / 8443)
┌─────────────────────────────────────────────────┐
│ Nginx (TLS 종료 프록시) │
│ ├── SSL/TLS (자체서명 or Let's Encrypt) │
│ ├── Rate Limiting (30 req/min) │
│ ├── 보안 헤더 (HSTS, X-Frame, X-XSS) │
│ └── CORS 정책 적용 │
└─────────────────┬───────────────────────────────┘
│ HTTP (내부)
┌─────────────────────────────────────────────────┐
│ GUARDiA ITSM FastAPI (포트 8001) │
│ ├── GUARDIA_NETWORK_MODE=open │
│ ├── CORS: 지정 외부 도메인 허용 │
│ ├── 보안 미들웨어 (HSTS, X-Frame, CSP) │
│ └── /api/external/* (API Key 인증) │
└─────────────────┬───────────────────────────────┘
┌───────┴────────┐
▼ ▼
PostgreSQL Ollama LLM
(내부 전용) (내부 전용)
localhost:5432 localhost:11434
```
> **핵심 원칙**: LLM(Ollama)과 DB는 외부에서 직접 접근 불가. API 서버만 노출.
---
## 2. 설치 및 구성
### 2-1. 사전 요구사항
| 항목 | 요구 사항 |
|------|---------|
| OS | Ubuntu 20.04+ |
| Nginx | 1.18+ |
| SSL 인증서 | 자체서명 or Let's Encrypt |
| GUARDiA | 2.0.0+ |
| Python | 3.11+ |
### 2-2. SSL 인증서 생성
**자체 서명 인증서 (테스트/내부망):**
```bash
mkdir -p /etc/ssl/guardia
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/ssl/guardia/server.key \
-out /etc/ssl/guardia/server.crt \
-subj "/C=KR/ST=Seoul/O=YourOrg/CN=your-server-ip" \
-addext "subjectAltName=IP:zioinfo.co.kr"
chmod 600 /etc/ssl/guardia/server.key
```
**Let's Encrypt (도메인 보유 시 — 권장):**
```bash
apt install certbot python3-certbot-nginx
certbot --nginx -d itsm.zioinfo.co.kr
# 자동 갱신 확인
certbot renew --dry-run
```
### 2-3. 환경변수 설정 (.env)
```bash
cp /opt/guardia/app/.env.open /opt/guardia/app/.env
nano /opt/guardia/app/.env
```
**개방망 필수 설정:**
```env
# 개방망 모드 활성화
GUARDIA_NETWORK_MODE=open
# 허용할 외부 출처 (쉼표 구분)
GUARDIA_ALLOWED_ORIGINS=https://itsm.zioinfo.co.kr,https://portal.myorg.go.kr
# 웹훅 HMAC 시크릿 (반드시 변경)
GUARDIA_WEBHOOK_SECRET=your-strong-secret-here
# DB (특수문자 URL 인코딩 필수: @ → %40, ! → %21)
DATABASE_URL=postgresql+asyncpg://guardia:G%40urd1a_2026%21@localhost:5432/guardia_db
# LLM (내부 전용 — 절대 변경 금지)
OLLAMA_BASE_URL=http://localhost:11434
LLM_MODEL=llama3:8b
```
### 2-4. Nginx 개방망 설정
```bash
# Nginx http 블록에 rate limit zone 추가
nano /etc/nginx/nginx.conf
```
```nginx
http {
limit_req_zone $binary_remote_addr zone=guardia_api:10m rate=30r/m;
...
}
```
**`/etc/nginx/sites-available/guardia-https`** 생성:
```nginx
server {
listen 8443 ssl;
server_name _;
ssl_certificate /etc/ssl/guardia/server.crt;
ssl_certificate_key /etc/ssl/guardia/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
location /api/ {
limit_req zone=guardia_api burst=10 nodelay;
proxy_pass http://127.0.0.1:8001;
proxy_set_header X-Forwarded-Proto https;
}
location / {
proxy_pass http://127.0.0.1:8001;
}
}
```
```bash
ln -sf /etc/nginx/sites-available/guardia-https /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
```
### 2-5. 서비스 재시작
```bash
systemctl restart guardia
systemctl is-active guardia
```
---
## 3. 외부 API 사용법
### 3-1. API 엔드포인트 목록
| 엔드포인트 | 메서드 | 인증 | 설명 |
|-----------|--------|------|------|
| `/api/external/health` | GET | 불필요 | 헬스체크 |
| `/api/external/status` | GET | 불필요 | 시스템 공개 상태 |
| `/api/external/keys` | GET | JWT (관리자) | API Key 목록 |
| `/api/external/keys` | POST | JWT (관리자) | API Key 발급 |
| `/api/external/keys/{id}` | DELETE | JWT (관리자) | API Key 비활성화 |
| `/api/external/sr` | GET | API Key (read) | SR 목록 조회 |
| `/api/external/sr` | POST | API Key (write) | SR 등록 |
| `/api/external/webhook` | POST | HMAC (선택) | 외부 메신저 웹훅 |
| `/docs` | GET | 불필요 | OpenAPI 문서 |
### 3-2. API Key 발급
**1단계: 관리자 로그인 (JWT 획득)**
```bash
curl -X POST https://zioinfo.co.kr:8443/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin@zioinfo2026!"}' \
-k
# → {"access_token": "eyJ...", "token_type": "bearer"}
```
**2단계: API Key 발급**
```bash
TOKEN="eyJ..."
curl -X POST https://zioinfo.co.kr:8443/api/external/keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "카카오워크 봇",
"scopes": "read,write,webhook",
"expires_days": 365,
"allowed_ips": ""
}' -k
```
**응답 (발급 후 1회만 노출):**
```json
{
"id": 1,
"name": "카카오워크 봇",
"api_key": "grd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"scopes": "read,write,webhook",
"expires_at": "2027-05-30T10:00:00",
"warning": "이 키는 다시 조회할 수 없습니다. 안전한 곳에 저장하세요."
}
```
### 3-3. API Key로 SR 등록
```bash
API_KEY="grd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# SR 등록
curl -X POST https://zioinfo.co.kr:8443/api/external/sr \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "웹서버 재시작 요청",
"description": "nginx가 502 오류를 반환하고 있습니다.",
"priority": "HIGH",
"requester_name": "홍길동",
"requester_email": "hong@example.go.kr"
}' -k
```
### 3-4. 외부 메신저 웹훅 연동
```bash
# Gitea/Slack/카카오워크 웹훅 URL 설정
WEBHOOK_URL="https://zioinfo.co.kr:8443/api/external/webhook"
SECRET="guardia-webhook-secret-change-me-2026"
# 서명 생성 (Python)
python3 -c "
import hmac, hashlib, json
body = json.dumps({'command': '서버 상태 확인', 'user_id': 'user01'})
sig = 'sha256=' + hmac.new(b'$SECRET', body.encode(), hashlib.sha256).hexdigest()
print(sig)
"
# 웹훅 전송
curl -X POST $WEBHOOK_URL \
-H "Content-Type: application/json" \
-H "X-GUARDiA-Signature: sha256=<서명값>" \
-H "X-Source: kakaotalk" \
-d '{"command": "서버 상태 확인", "user_id": "홍길동"}' \
-k
```
---
## 4. API Key 권한 스코프
| 스코프 | 설명 | 허용 API |
|--------|------|---------|
| `read` | 읽기 전용 | SR 목록 조회 |
| `write` | 쓰기 | SR 등록, 상태 변경 |
| `admin` | 전체 권한 | 모든 외부 API |
| `webhook` | 웹훅 전용 | `/api/external/webhook` |
**스코프 조합 예시:**
```json
"scopes": "read,write" // 조회 + 등록
"scopes": "webhook" // 웹훅만
"scopes": "admin" // 전체 (주의)
```
---
## 5. 보안 설정
### 5-1. 적용된 보안 헤더
| 헤더 | 값 | 효과 |
|------|-----|------|
| `Strict-Transport-Security` | `max-age=31536000` | 브라우저가 HTTPS만 사용 |
| `X-Frame-Options` | `DENY` | iframe 삽입 차단 |
| `X-Content-Type-Options` | `nosniff` | MIME 타입 스니핑 방지 |
| `X-XSS-Protection` | `1; mode=block` | XSS 공격 차단 |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Referrer 정보 제한 |
### 5-2. Rate Limiting 설정
| 위치 | 제한 |
|------|------|
| Nginx (기본) | 30 req/min per IP |
| Nginx (burst) | 최대 10 req 버스트 허용 |
| FastAPI (ratelimit.py) | 별도 설정 가능 |
### 5-3. IP 화이트리스트 (API Key)
특정 외부 시스템만 API Key를 사용할 수 있도록 IP를 제한할 수 있습니다:
```json
{
"name": "공공기관 포털",
"scopes": "read,write",
"allowed_ips": "203.10.20.30,203.10.20.31"
}
```
빈 문자열(`""`)로 설정하면 모든 IP에서 접근 가능합니다.
### 5-4. 변경 불가 보안 정책
> 개방망 모드에서도 다음 정책은 절대 변경 불가합니다.
| 정책 | 내용 |
|------|------|
| **외부 LLM 금지** | Ollama(localhost:11434)만 사용. OpenAI, Claude 등 외부 API 완전 금지 |
| **SSH 자격증명 보호** | IP, 비밀번호, SSH 계정을 API 응답에 포함 금지 |
| **AES-256 암호화** | 서버 자격증명은 암호화 저장 |
| **root SSH 금지** | opsagent 전용 계정 사용 |
---
## 6. 모드 전환
### 6-1. 폐쇄망 → 개방망
```bash
# .env 수정
echo "GUARDIA_NETWORK_MODE=open" >> /opt/guardia/app/.env
echo "GUARDIA_ALLOWED_ORIGINS=https://your-domain.go.kr" >> /opt/guardia/app/.env
# Nginx HTTPS 활성화
ln -sf /etc/nginx/sites-available/guardia-https /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# 서비스 재시작
systemctl restart guardia
```
### 6-2. 개방망 → 폐쇄망 (롤백)
```bash
# .env 수정
sed -i 's/GUARDIA_NETWORK_MODE=open/GUARDIA_NETWORK_MODE=closed/' /opt/guardia/app/.env
# HTTPS 비활성화
rm /etc/nginx/sites-enabled/guardia-https
systemctl reload nginx
# 서비스 재시작
systemctl restart guardia
```
---
## 7. 테스트 결과
### 7-1. 테스트 환경
| 항목 | 값 |
|------|-----|
| 서버 | Ubuntu 24.04 LTS (zioinfo.co.kr) |
| GUARDiA 버전 | 2.0.0 |
| Nginx 버전 | 1.24.0 |
| 테스트 일자 | 2026-05-30 |
### 7-2. 테스트 결과
| 테스트 | 항목 | 결과 |
|--------|------|------|
| T1 | HTTP 헬스체크 (8001) | ✅ HTTP 200 |
| T2 | HTTPS 헬스체크 (8443) | ✅ HTTP 200 |
| T3 | 홈페이지 HTTPS (443) | ✅ HTTP 200 |
| T4 | 미인증 API 접근 | ✅ HTTP 401 반환 |
| T5 | CORS 외부 출처 허용 | ✅ `Access-Control-Allow-Origin` 헤더 |
| T6 | HSTS 헤더 | ✅ `Strict-Transport-Security` 적용 |
| T7 | X-Frame-Options | ✅ `DENY` 설정 |
| T8 | Rate Limiting | ✅ Nginx rate limit zone 설정 |
| T9 | 시스템 상태 공개 | ✅ `operational` |
| T10 | 개방망 모드 활성 | ✅ `NETWORK_MODE=open` |
---
## 8. 트러블슈팅
### 8-1. CORS 오류
**증상**: 브라우저에서 `Access-Control-Allow-Origin` 오류
```bash
# .env에 도메인 추가
GUARDIA_ALLOWED_ORIGINS=https://your-domain.com,https://other-domain.go.kr
systemctl restart guardia
```
### 8-2. HTTPS 인증서 오류
**증상**: `SSL: CERTIFICATE_VERIFY_FAILED`
```bash
# 자체서명 인증서인 경우 curl에 -k 플래그 사용
curl -k https://zioinfo.co.kr:8443/api/external/health
# 브라우저에서는 예외 추가 또는 Let's Encrypt 인증서 사용
```
### 8-3. Rate Limit 초과
**증상**: HTTP 429 Too Many Requests
```bash
# Nginx rate limit 완화
nano /etc/nginx/nginx.conf
# rate=60r/m 으로 변경
nginx -t && systemctl reload nginx
```
### 8-4. DATABASE_URL 연결 오류
**증상**: `Name or service not known`
```bash
# @ 특수문자 URL 인코딩 확인
# G@urd1a_2026! → G%40urd1a_2026%21
sed -i 's|G@urd1a_2026!|G%40urd1a_2026%21|g' /opt/guardia/app/.env
systemctl restart guardia
```
---
## 9. 접속 정보 요약
| 서비스 | URL | 비고 |
|--------|-----|------|
| GUARDiA ITSM (HTTP) | `http://zioinfo.co.kr:8001` | 내부망 권장 |
| GUARDiA ITSM (HTTPS) | `https://zioinfo.co.kr:8443` | 개방망 사용 |
| 외부 API | `https://zioinfo.co.kr:8443/api/external/` | API Key 인증 |
| OpenAPI 문서 | `https://zioinfo.co.kr:8443/docs` | 무인증 |
| 홈페이지 HTTPS | `https://zioinfo.co.kr` | 포트 443 |
---
*GUARDiA ITSM v2.0.0 | (주)지오정보기술 | 2026-05-30*