# 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*