diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e596b96..ca8ad0a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -32,6 +32,7 @@ const AiPlatform = lazy(() => import('./pages/AiPlatform')) // ── GUARDiA 기능 개선 v4 ── const AppDistribution = lazy(() => import('./pages/AppDistribution')) const NotificationRules = lazy(() => import('./pages/NotificationRules')) +const InstallGuide = lazy(() => import('./pages/InstallGuide')) function Loading() { return ( @@ -79,6 +80,7 @@ export default function App() { {/* GUARDiA 기능 개선 v4 */} } /> } /> + } /> } /> diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 21cd425..7054305 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -46,6 +46,7 @@ const NAV: NavItem[] = [ // ── GUARDiA 기능 개선 v4 ── { label: '앱 배포', icon: '📱', path: '/app-distribution' }, { label: '알림 규칙', icon: '🔔', path: '/notification-rules' }, + { label: '설치 가이드', icon: '📦', path: '/install-guide' }, ] /* Variant 스타일 색상 상수 */ diff --git a/frontend/src/pages/InstallGuide.tsx b/frontend/src/pages/InstallGuide.tsx new file mode 100644 index 0000000..62356cf --- /dev/null +++ b/frontend/src/pages/InstallGuide.tsx @@ -0,0 +1,789 @@ +import { useState } from 'react' + +type EnvType = 'closed' | 'open' +type SystemType = 'itsm' | 'manager' | 'messenger' | 'all' + +const S = { + page: { padding: '24px 28px', maxWidth: 1100 }, + tabBar: { display: 'flex', gap: 4, marginBottom: 24, borderBottom: '2px solid #e2e8f0', paddingBottom: 0 }, + tab: (active: boolean): React.CSSProperties => ({ + padding: '10px 20px', border: 'none', background: 'none', cursor: 'pointer', + fontSize: 14, fontWeight: active ? 700 : 400, + color: active ? '#003366' : '#64748b', + borderBottom: active ? '2px solid #003366' : '2px solid transparent', + marginBottom: -2, + }), + card: { background: '#fff', border: '1px solid #e2e8f0', borderRadius: 10, padding: 20, marginBottom: 16 }, + step: (color: string): React.CSSProperties => ({ + display: 'flex', gap: 0, marginBottom: 20, + }), + stepNum: (color: string): React.CSSProperties => ({ + width: 32, height: 32, borderRadius: '50%', background: color, color: '#fff', + fontWeight: 800, fontSize: 14, display: 'flex', alignItems: 'center', + justifyContent: 'center', flexShrink: 0, marginRight: 16, marginTop: 2, + }), + code: { background: '#0f172a', color: '#e2e8f0', borderRadius: 8, padding: '12px 16px', + fontSize: 12, fontFamily: 'monospace', lineHeight: 1.8, + overflowX: 'auto' as const, whiteSpace: 'pre' as const, margin: '8px 0 0' }, + warn: { background: '#fef3c7', border: '1px solid #fcd34d', borderRadius: 8, padding: 12, marginBottom: 16, fontSize: 12 }, + info: { background: '#eff6ff', border: '1px solid #bfdbfe', borderRadius: 8, padding: 12, marginBottom: 16, fontSize: 12 }, + danger: { background: '#fef2f2', border: '1px solid #fca5a5', borderRadius: 8, padding: 12, marginBottom: 16, fontSize: 12 }, + success: { background: '#f0fdf4', border: '1px solid #bbf7d0', borderRadius: 8, padding: 12, marginBottom: 16, fontSize: 12 }, + badge: (color: string): React.CSSProperties => ({ + display: 'inline-block', padding: '2px 8px', borderRadius: 8, fontSize: 11, + fontWeight: 700, background: color, color: '#fff', marginLeft: 8, + }), + h2: { fontSize: 18, fontWeight: 800, color: '#003366', margin: '0 0 4px' }, + h3: { fontSize: 15, fontWeight: 700, color: '#1e293b', margin: '16px 0 8px' }, + tag: (c: string, bg: string): React.CSSProperties => ({ + padding: '3px 10px', borderRadius: 10, fontSize: 11, fontWeight: 700, + color: c, background: bg, display: 'inline-block', + }), +} + +// ── 단계 컴포넌트 ────────────────────────────────────────────────────────────── +function Step({ num, title, color, children }: { num: number; title: string; color: string; children: React.ReactNode }) { + return ( +
+
{num}
+
+
{title}
+ {children} +
+
+ ) +} + +function Code({ children }: { children: string }) { + const [copied, setCopied] = useState(false) + const copy = () => { + navigator.clipboard?.writeText(children).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000) }) + } + return ( +
+
{children}
+ +
+ ) +} + +function Req() { + return ( +
+
📋 시스템 요구사항
+
+ {[ + { k: 'OS', v: 'Ubuntu 22.04 LTS / RHEL 8+' }, + { k: 'CPU', v: '8코어 이상 (4코어 최소)' }, + { k: 'RAM', v: '16GB 이상 권장' }, + { k: 'GPU', v: '8GB VRAM (파인튜닝 선택)' }, + { k: 'Storage', v: '100GB SSD 이상' }, + { k: 'Python', v: '3.11 이상' }, + { k: 'Node.js', v: '20 LTS 이상' }, + { k: 'PostgreSQL', v: '16 이상' }, + ].map(r => ( +
+
{r.k}
+
{r.v}
+
+ ))} +
+
+ 내부망 필수 포트: ITSM 8443(HTTPS), Manager 8090(HTTPS), Webmail 8025, API 9001, Ollama 11434, PostgreSQL 5432, Gitea 3000, Jenkins 8080 +
+
+ ) +} + +// ── 폐쇄망 설치 ─────────────────────────────────────────────────────────────── +function ClosedNetwork({ system }: { system: SystemType }) { + const COLORS = ['#003366','#005A8C','#1d4ed8','#7c3aed','#059669','#dc2626','#d97706','#0891b2'] + return ( +
+
+ 🔒 폐쇄망 보안 주의사항
+ • 설치 파일 SHA-256 무결성 검증 필수 — 변조 여부 반드시 확인
+ • USB·이동식 미디어 반입 시 보안 부서 승인 및 바이러스 검사 선행
+ • 설치 중 root 계정 사용 후 반드시 비활성화, opsagent 전용 계정으로 운영
+ • 설치 완료 즉시 기본 admin 비밀번호 변경 (초기값 사용 금지)
+ • 자격증명(IP·비밀번호·SSH 키)을 설치 로그·화면에 노출 금지 +
+ + + + {/* STEP 1: 오프라인 패키지 준비 */} + +
외부 인터넷이 가능한 PC에서 아래 작업 후 폐쇄망으로 이전합니다.
+ {`# Python 패키지 다운로드 +mkdir guardia-offline && cd guardia-offline +pip download fastapi sqlalchemy asyncpg pgvector httpx \\ + paramiko cryptography pydantic uvicorn gunicorn \\ + pillow qrcode --dest ./packages/ + +# Node.js 패키지 다운로드 (npm pack) +npm pack @vitejs/plugin-react react react-dom axios zustand --pack-destination ./npm-packages/ + +# Ollama 바이너리 다운로드 +curl -Lo ollama-linux-amd64 https://ollama.ai/download/ollama-linux-amd64 +chmod +x ollama-linux-amd64 + +# Ollama 모델 다운로드 (약 20GB) +ollama pull llama3 # 일반 NLP (4.7GB) +ollama pull llava:7b # 비전·이미지 분석 (4.7GB) +ollama pull nomic-embed-text # 임베딩 (0.27GB) +ollama pull codellama:7b # 코드 분석 (3.8GB) + +# Ollama 모델 파일 압축 +tar czf ollama-models.tar.gz ~/.ollama/models/ + +# GUARDiA 소스코드 압축 +git clone https://github.com/zio/guardia-itsm.git +tar czf guardia-src.tar.gz guardia-itsm/ + +# 무결성 체크섬 생성 (필수) +sha256sum packages/* ollama-models.tar.gz guardia-src.tar.gz > SHA256SUMS.txt +cat SHA256SUMS.txt`} +
+ + {/* STEP 2: 보안 반입 */} + +
승인된 이동식 미디어 또는 내부망 파일 서버를 통해서만 반입합니다.
+ {`# 폐쇄망 서버에서 수행 +# 1. 파일 수신 후 무결성 검증 (필수!) +sha256sum -c SHA256SUMS.txt +# "OK" 출력 확인 — FAILED 시 즉시 중단 + +# 2. 작업 디렉토리 설정 +mkdir -p /opt/guardia/{app,venv,logs,uploads,backups} +mkdir -p /var/www/{manager,zioinfo,mail} +chmod 750 /opt/guardia + +# 3. 파일 압축 해제 +tar xzf guardia-src.tar.gz -C /opt/guardia/ +tar xzf ollama-models.tar.gz -C ~/`} +
+ + {/* STEP 3: 시스템 패키지 */} + + {`# Ubuntu 22.04 — 오프라인 패키지 (USB에 미리 다운로드) +apt install --no-install-recommends \\ + ./postgresql-16_amd64.deb \\ + ./python3.11_amd64.deb \\ + ./nodejs_20_amd64.deb \\ + ./nginx_amd64.deb + +# PostgreSQL 초기화 +systemctl enable --now postgresql +su postgres -c "createuser guardia" +su postgres -c "createdb guardia_db -O guardia" +su postgres -c "psql -c \\"ALTER USER guardia PASSWORD 'G@urd1a_2026!'\\"" + +# pgvector 확장 설치 (오프라인 빌드 또는 패키지) +su postgres -c "psql -d guardia_db -c 'CREATE EXTENSION IF NOT EXISTS vector;'" + +# Python 가상환경 + 오프라인 패키지 설치 +python3 -m venv /opt/guardia/venv +/opt/guardia/venv/bin/pip install --no-index \\ + --find-links=/opt/guardia-offline/packages/ \\ + fastapi sqlalchemy asyncpg pgvector httpx \\ + paramiko cryptography pydantic uvicorn`} + + + {/* STEP 4: Ollama */} + +
Ollama는 외부 API 없이 온프레미스에서 완전히 동작합니다. 인터넷 연결 불필요.
+ {`# Ollama 바이너리 설치 +cp ./ollama-linux-amd64 /usr/local/bin/ollama +chmod +x /usr/local/bin/ollama + +# systemd 서비스 등록 +cat > /etc/systemd/system/ollama.service << 'EOF' +[Unit] +Description=Ollama AI Engine +After=network.target + +[Service] +ExecStart=/usr/local/bin/ollama serve +Restart=always +Environment=OLLAMA_HOST=0.0.0.0 +Environment=OLLAMA_MODELS=/root/.ollama/models + +[Install] +WantedBy=multi-user.target +EOF + +# 오프라인 모델 복원 (USB에서 복사한 모델) +# 모델 파일은 이미 ~/.ollama/models/ 에 위치해야 함 +systemctl enable --now ollama + +# 모델 등록 확인 (자동 인식) +sleep 3 && ollama list +# 출력 예시: +# NAME SIZE +# llama3:8b 4.7 GB +# llava:7b 4.7 GB +# nomic-embed-text 0.27 GB`} +
+ + {/* STEP 5: ITSM 설정 */} + {(system === 'itsm' || system === 'all') && ( + + {`# 소스 복사 +cp -r /opt/guardia/guardia-itsm/. /opt/guardia/app/ + +# 환경변수 설정 (폐쇄망 전용) +cat > /opt/guardia/app/.env << 'EOF' +# ── 네트워크 모드 ── +GUARDIA_NETWORK_MODE=closed # 폐쇄망 모드 + +# ── 데이터베이스 ── +DATABASE_URL=postgresql+asyncpg://guardia:G%40urd1a_2026%21@localhost:5432/guardia_db + +# ── 보안 키 (반드시 랜덤 값으로 교체) ── +SECRET_KEY=$(openssl rand -hex 32) +AES_KEY=$(openssl rand -hex 16) +AES_IV=$(openssl rand -hex 8) + +# ── Ollama (온프레미스, 외부 API 완전 차단) ── +OLLAMA_BASE_URL=http://localhost:11434 +OLLAMA_MODEL=llama3 +OLLAMA_VISION_MODEL=llava:7b + +# ── 서버 설정 ── +HOST=127.0.0.1 +PORT=9001 +WORKERS=4 +EOF + +# systemd 서비스 등록 +cat > /etc/systemd/system/guardia.service << 'EOF' +[Unit] +Description=GUARDiA ITSM Platform +After=network.target postgresql.service ollama.service + +[Service] +User=root +WorkingDirectory=/opt/guardia/app +EnvironmentFile=/opt/guardia/app/.env +ExecStart=/opt/guardia/venv/bin/uvicorn main:app \\ + --host 127.0.0.1 --port 9001 --workers 4 +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable --now guardia +sleep 4 && systemctl is-active guardia`} + + )} + + {/* STEP 6: Manager */} + {(system === 'manager' || system === 'all') && ( + + {`# Manager 소스 복사 및 빌드 (오프라인 npm) +cp -r /opt/guardia/guardia-manager/. /opt/manager/src/ +cd /opt/manager/src/frontend + +# 오프라인 npm 패키지 설치 +npm install --prefer-offline --legacy-peer-deps + +# 빌드 +npm run build +cp -r dist/. /var/www/manager/ + +# Manager 백엔드 서비스 +cat > /etc/systemd/system/guardia-manager.service << 'EOF' +[Unit] +Description=GUARDiA Manager Backend +After=network.target guardia.service + +[Service] +User=root +WorkingDirectory=/opt/manager/src/backend +ExecStart=/opt/guardia/venv/bin/uvicorn main:app \\ + --host 127.0.0.1 --port 8002 --workers 2 +Restart=on-failure +EOF + +systemctl enable --now guardia-manager`} + + )} + + {/* STEP 7: Messenger */} + {(system === 'messenger' || system === 'all') && ( + +
+ 💡 앱스토어 없이 배포: Manager → 📱 앱 배포 → APK 업로드 → QR 생성 → 사용자 스캔 → 설치 +
+ {`# EAS 빌드 APK를 내부망 서버에 복사 +cp GUARDiA_Messenger_v1.0.0.apk /opt/guardia/app/uploads/apk/ + +# Manager에서 APK 등록 (API) +curl -X POST https://localhost:8443/api/app/upload \\ + -H "Authorization: Bearer " \\ + -F "file=@/opt/guardia/app/uploads/apk/GUARDiA_Messenger_v1.0.0.apk" \\ + -F "version=1.0.0" \\ + -F "release_notes=초기 배포" + +# QR 코드 생성 확인 +# Manager → 앱 배포 → QR 이미지 → 사용자에게 공유 + +# 사용자 설치 순서: +# 1. Android 기기에서 QR 스캔 +# 2. 랜딩 페이지 → "Android 다운로드" 클릭 +# 3. 설정 → 보안 → "알 수 없는 출처" 허용 (최초 1회) +# 4. APK 설치 → 서버 주소 입력 → 로그인`} +
+ )} + + {/* STEP 8: nginx + SSL */} + +
폐쇄망에서는 내부 CA(Certificate Authority)가 발급한 인증서를 사용합니다.
+ {`# 내부 CA로 인증서 생성 (이미 CA가 있는 경우 해당 CA 사용) +openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/guardia/server.key \\ + -out /etc/ssl/guardia/server.crt -days 3650 -nodes \\ + -subj "/C=KR/ST=Seoul/O=Your-Org/CN=guardia.internal" + +# nginx 설정 +cat > /etc/nginx/sites-available/guardia << 'EOF' +# ITSM (8443) +server { + listen 8443 ssl; + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + + location / { root /opt/guardia/app/static; try_files $uri /index.html; } + location /api/ { proxy_pass http://127.0.0.1:9001; } + location /ws { proxy_pass http://127.0.0.1:9001; proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; } +} + +# Manager (8090) +server { + listen 8090 ssl; + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + root /var/www/manager; + location / { try_files $uri /index.html; } + location /api/ { proxy_pass http://127.0.0.1:8002; } + location /guardia-api/ { proxy_pass http://127.0.0.1:9001/; } +} +EOF + +ln -sf /etc/nginx/sites-available/guardia /etc/nginx/sites-enabled/ +nginx -t && systemctl reload nginx`} +
+ + {/* STEP 9: 방화벽 */} + +
+ ⚠️ 보안 필수: 관리 포트는 허가된 관리자 IP에서만 접근 허용. 외부 인터넷 차단 확인. +
+ {`# UFW 방화벽 (Ubuntu) +ufw default deny incoming +ufw default allow outgoing + +# SSH — 관리자 전용 (IP 제한) +ufw allow from <관리자-IP>/32 to any port 22 + +# ITSM — 내부망 허용 +ufw allow from <내부망-CIDR> to any port 8443 + +# Manager — 내부망 허용 +ufw allow from <내부망-CIDR> to any port 8090 + +# Webmail — 내부망 허용 +ufw allow from <내부망-CIDR> to any port 8025 + +# Messenger 앱 API (선택) +ufw allow from <내부망-CIDR> to any port 8443 + +# 외부 인터넷 차단 확인 (폐쇄망 필수) +ufw deny out to any port 80 # HTTP 외부 차단 +ufw deny out to any port 443 # HTTPS 외부 차단 + +ufw enable +ufw status verbose`} +
+ + {/* 완료 확인 */} +
+ ✅ 설치 완료 후 필수 점검 사항
+ □ 기본 admin 비밀번호 변경 완료
+ □ SSL 인증서 적용 및 브라우저 경고 없음
+ □ 방화벽 규칙 검토 (ufw status verbose)
+ □ Ollama 모델 정상 로드 (ollama list)
+ □ ITSM 헬스체크: curl -k https://localhost:8443/health
+ □ 감사 로그 경로 확인 (/opt/guardia/logs/)
+ □ PostgreSQL 자동 백업 설정 (crontab)
+ □ CSAP 보안 점검 실행 (Manager → CSAP 점검) +
+
+ ) +} + +// ── 개방망 설치 ─────────────────────────────────────────────────────────────── +function OpenNetwork({ system }: { system: SystemType }) { + const COLORS = ['#059669','#0891b2','#7c3aed','#d97706','#dc2626','#003366','#16a34a','#9333ea'] + return ( +
+
+ ⚠️ 개방망 보안 주의사항
+ • HTTPS 반드시 적용 — HTTP는 자격증명 노출 위험
+ • 관리 포트(8443, 8090) 방화벽으로 허가 IP만 허용
+ • Ollama는 localhost에서만 동작 (외부 노출 금지)
+ • 정기 보안 패치 적용 (Ubuntu: unattended-upgrades 권장)
+ • 기본 자격증명 설치 후 즉시 변경 +
+ + + + + {`# Ubuntu 22.04 기준 +apt update && apt upgrade -y +apt install -y curl wget git unzip software-properties-common \\ + build-essential libssl-dev libffi-dev python3-dev + +# Python 3.11 +add-apt-repository ppa:deadsnakes/ppa -y +apt install -y python3.11 python3.11-venv python3.11-dev + +# Node.js 20 LTS +curl -fsSL https://deb.nodesource.com/setup_20.x | bash - +apt install -y nodejs + +# PostgreSQL 16 +sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \\ + > /etc/apt/sources.list.d/pgdg.list' +curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - +apt update && apt install -y postgresql-16 postgresql-16-pgvector + +# PostgreSQL 초기화 +systemctl enable --now postgresql +su postgres -c "createuser guardia -P" # 비밀번호 입력 +su postgres -c "createdb guardia_db -O guardia" +su postgres -c "psql -d guardia_db -c 'CREATE EXTENSION IF NOT EXISTS vector;'"`} + + + +
Ollama는 온프레미스 전용 — 외부 API 호출 없이 서버 내부에서만 동작합니다.
+ {`# Ollama 설치 +curl -fsSL https://ollama.ai/install.sh | sh +systemctl enable --now ollama + +# 필수 AI 모델 설치 (총 약 15GB, 시간 소요) +ollama pull llama3 # 일반 NLP (4.7GB) +ollama pull llava:7b # 비전·이미지·스크린샷 분석 (4.7GB) +ollama pull nomic-embed-text # 임베딩·RAG 검색 (0.27GB) +ollama pull codellama:7b # 코드·CSS 생성 (3.8GB) + +# 서비스 확인 +curl http://localhost:11434/api/tags | python3 -c \\ + "import sys,json; [print(m['name']) for m in json.load(sys.stdin)['models']]" + +# GPU 가속 설정 (NVIDIA GPU 있는 경우) +# CUDA toolkit 설치 후 Ollama 자동 감지`} +
+ + + {`# Gitea에서 소스 클론 (개방망) +git clone https://zioinfo.co.kr:3000/zio/guardia-itsm.git /opt/guardia/app +# 또는 +git clone <내부-Gitea-URL>/zio/guardia-itsm.git /opt/guardia/app + +# Python 가상환경 +python3.11 -m venv /opt/guardia/venv +/opt/guardia/venv/bin/pip install -r /opt/guardia/app/requirements.txt + +# 환경변수 설정 +cat > /opt/guardia/app/.env << 'ENVEOF' +GUARDIA_NETWORK_MODE=open +DATABASE_URL=postgresql+asyncpg://guardia:<비밀번호>@localhost:5432/guardia_db +SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") +AES_KEY=$(python3 -c "import secrets; print(secrets.token_hex(16))") +AES_IV=$(python3 -c "import secrets; print(secrets.token_hex(8))") +OLLAMA_BASE_URL=http://localhost:11434 +GUARDIA_ALLOWED_ORIGINS=https://<도메인>:8443,https://<도메인>:8090 +ENVEOF + +# DB 마이그레이션 및 초기 데이터 +cd /opt/guardia/app +/opt/guardia/venv/bin/python3 -c " +from database import engine, Base +import asyncio +asyncio.run(engine.begin().__aenter__()) +print('DB 초기화 완료') +" + +# systemd 등록 및 시작 +cp deploy/guardia.service /etc/systemd/system/ +systemctl daemon-reload && systemctl enable --now guardia +sleep 5 && curl http://127.0.0.1:9001/health`} + + + + {`# Manager 소스 +git clone /zio/guardia-manager.git /opt/manager/src + +# Frontend 빌드 +cd /opt/manager/src/frontend +npm ci --legacy-peer-deps +npm run build +cp -r dist/. /var/www/manager/ + +# Backend +cd /opt/manager/src/backend +/opt/guardia/venv/bin/pip install -r requirements.txt +cat > .env << 'EOF' +GUARDIA_ITSM_URL=http://127.0.0.1:9001 +SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") +EOF + +# 서비스 등록 +cp deploy/guardia-manager.service /etc/systemd/system/ +systemctl enable --now guardia-manager`} + + + +
공인 도메인이 있는 경우 Let's Encrypt, 없으면 자체 서명 인증서를 사용합니다.
+ {`# 방법 1: Let's Encrypt (공인 도메인 있는 경우) +apt install -y certbot +certbot certonly --standalone \\ + -d guardia.your-domain.go.kr \\ + --agree-tos --email admin@your-org.go.kr + +# 인증서 경로 +# /etc/letsencrypt/live/guardia.your-domain.go.kr/fullchain.pem +# /etc/letsencrypt/live/guardia.your-domain.go.kr/privkey.pem + +# 방법 2: 자체 서명 인증서 (도메인 없는 경우) +mkdir -p /etc/ssl/guardia +openssl req -x509 -newkey rsa:4096 \\ + -keyout /etc/ssl/guardia/server.key \\ + -out /etc/ssl/guardia/server.crt \\ + -days 3650 -nodes \\ + -subj "/C=KR/O=Your-Org/CN=<서버-IP-또는-도메인>" + +# 자동 갱신 (Let's Encrypt) +echo "0 3 * * * certbot renew --quiet && systemctl reload nginx" | crontab -`} +
+ + + {`apt install -y nginx + +cat > /etc/nginx/sites-available/guardia << 'NGINXEOF' +# ── GUARDiA ITSM (8443) ────────────────────────────── +server { + listen 8443 ssl http2; + server_name _; + + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_session_cache shared:SSL:10m; + + # 정적 파일 (React SPA) + root /opt/guardia/app/static; + location / { try_files $uri /index.html; } + + # API 프록시 + location /api/ { + proxy_pass http://127.0.0.1:9001; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Proto https; + proxy_read_timeout 300s; + } + + # WebSocket (실시간 알림) + location /ws { + proxy_pass http://127.0.0.1:9001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} + +# ── GUARDiA Manager (8090) ────────────────────────── +server { + listen 8090 ssl http2; + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + + root /var/www/manager; + location / { try_files $uri /index.html; } + location /api/ { proxy_pass http://127.0.0.1:8002; } + location /guardia-api/ { proxy_pass http://127.0.0.1:9001/; } +} + +# ── Webmail (8025) ────────────────────────────────── +server { + listen 8025 ssl; + ssl_certificate /etc/ssl/guardia/server.crt; + ssl_certificate_key /etc/ssl/guardia/server.key; + + root /var/www/mail; + location / { try_files $uri /index.html; } + location /api/ { proxy_pass http://127.0.0.1:8026; } +} +NGINXEOF + +ln -sf /etc/nginx/sites-available/guardia /etc/nginx/sites-enabled/ +nginx -t && systemctl enable --now nginx`} + + + + {`# UFW 방화벽 +ufw default deny incoming +ufw default allow outgoing + +# SSH (관리자 IP만) +ufw allow from <관리자-IP> to any port 22 + +# ITSM/Manager/Webmail (허가 IP 대역만) +ufw allow from <허용-IP-대역>/24 to any port 8443 +ufw allow from <허용-IP-대역>/24 to any port 8090 +ufw allow from <허용-IP-대역>/24 to any port 8025 + +# Ollama는 localhost만 (외부 노출 금지!) +# PostgreSQL은 localhost만 +# Gitea, Jenkins (선택) +ufw allow from <내부망-CIDR> to any port 3000 +ufw allow from <내부망-CIDR> to any port 8080 + +ufw enable +ufw status numbered`} + + + + {`# 전체 서비스 상태 확인 +systemctl is-active guardia guardia-manager ollama postgresql nginx + +# API 헬스체크 +curl -sk https://localhost:8443/health | python3 -m json.tool +curl -sk https://localhost:8090/api/health | python3 -m json.tool + +# Ollama 모델 확인 +ollama list + +# 초기 admin 로그인 (즉시 비밀번호 변경!) +# URL: https://<서버IP>:8443 +# ID: admin PW: 1111 ← 즉시 변경 필수! + +# 보안 점검 실행 +curl -sk https://localhost:8443/api/n2sf/checklist \\ + -H "Authorization: Bearer " | python3 -m json.tool`} + + +
+ ✅ 개방망 설치 완료 체크리스트
+ □ admin 기본 비밀번호 변경 완료
+ □ HTTPS 인증서 적용 (브라우저 자물쇠 확인)
+ □ 방화벽 규칙 검토 (ufw status numbered)
+ □ Ollama 외부 노출 없음 (curl http://외부IP:11434 실패 확인)
+ □ PostgreSQL 외부 노출 없음
+ □ Let's Encrypt 자동 갱신 설정
+ □ 보안 패치 자동화 (unattended-upgrades)
+ □ 백업 스케줄 설정 (PostgreSQL + 파일) +
+
+ ) +} + +// ── 메인 페이지 ─────────────────────────────────────────────────────────────── +export default function InstallGuide() { + const [env, setEnv] = useState('closed') + const [sys, setSys] = useState('all') + + return ( +
+

+ 📦 GUARDiA 설치 가이드 +

+

+ 폐쇄망(공공기관 내부망)과 개방망 환경별 단계별 설치 방법 +

+ + {/* 환경 선택 */} +
+
+
설치 환경
+
+ {([['closed','🔒 폐쇄망','공공기관 내부망·인터넷 차단'],['open','🌐 개방망','인터넷 연결 가능']] as const).map(([v,label,desc])=>( + + ))} +
+
+ +
+
설치 대상
+
+ {([['all','전체','ITSM+Manager+Messenger'],['itsm','ITSM','API 서버'],['manager','Manager','관제 포털'],['messenger','Messenger','모바일 앱']] as const).map(([v,label,desc])=>( + + ))} +
+
+
+ + {/* 선택 배지 */} +
+ 현재 선택: {env === 'closed' ? '🔒 폐쇄망' : '🌐 개방망'} +  +  + {sys === 'all' ? '전체 시스템' : `GUARDiA ${sys.toUpperCase()}`} +  설치 가이드를 표시합니다. +
+ + {/* 설치 가이드 렌더링 */} + {env === 'closed' + ? + : + } + + {/* 지원 문의 */} +
+
+ 설치 지원이 필요하신가요? +
+

+ 지오정보기술 전문 엔지니어가 기관 환경에 맞는 설치를 지원합니다. +

+
+ {[ + { label: '📞 기술 지원 전화', val: '02-0000-0000' }, + { label: '📧 이메일 문의', val: 'support@zioinfo.co.kr' }, + { label: '🌐 홈페이지', val: 'www.zioinfo.co.kr' }, + ].map(c => ( +
+ {c.label}: + {c.val} +
+ ))} +
+
+
+ ) +} diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo index da16460..3bc3612 100644 --- a/frontend/tsconfig.tsbuildinfo +++ b/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/api/clients.ts","./src/api/types.ts","./src/components/common/btn.tsx","./src/components/common/datatable.tsx","./src/components/common/protectedroute.tsx","./src/components/common/slidepanel.tsx","./src/components/common/statcard.tsx","./src/components/common/statusbadge.tsx","./src/components/layout/applayout.tsx","./src/components/layout/gnb.tsx","./src/components/layout/sidebar.tsx","./src/config/env.ts","./src/hooks/useapi.ts","./src/hooks/useauth.ts","./src/pages/aiplatform.tsx","./src/pages/apikeys.tsx","./src/pages/appdistribution.tsx","./src/pages/auditlog.tsx","./src/pages/bianalytics.tsx","./src/pages/billingmanage.tsx","./src/pages/cmdb.tsx","./src/pages/configenv.tsx","./src/pages/confignginx.tsx","./src/pages/csapconsole.tsx","./src/pages/dashboard.tsx","./src/pages/deployments.tsx","./src/pages/drconsole.tsx","./src/pages/exportimport.tsx","./src/pages/institutions.tsx","./src/pages/integrationhub.tsx","./src/pages/kpidashboard.tsx","./src/pages/llmmanager.tsx","./src/pages/licenses.tsx","./src/pages/login.tsx","./src/pages/networkconsole.tsx","./src/pages/notificationrules.tsx","./src/pages/notifications.tsx","./src/pages/repos.tsx","./src/pages/scrapingmanager.tsx","./src/pages/servers.tsx","./src/pages/users.tsx"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/main.tsx","./src/api/clients.ts","./src/api/types.ts","./src/components/common/btn.tsx","./src/components/common/datatable.tsx","./src/components/common/protectedroute.tsx","./src/components/common/slidepanel.tsx","./src/components/common/statcard.tsx","./src/components/common/statusbadge.tsx","./src/components/layout/applayout.tsx","./src/components/layout/gnb.tsx","./src/components/layout/sidebar.tsx","./src/config/env.ts","./src/hooks/useapi.ts","./src/hooks/useauth.ts","./src/pages/aiplatform.tsx","./src/pages/apikeys.tsx","./src/pages/appdistribution.tsx","./src/pages/auditlog.tsx","./src/pages/bianalytics.tsx","./src/pages/billingmanage.tsx","./src/pages/cmdb.tsx","./src/pages/configenv.tsx","./src/pages/confignginx.tsx","./src/pages/csapconsole.tsx","./src/pages/dashboard.tsx","./src/pages/deployments.tsx","./src/pages/drconsole.tsx","./src/pages/exportimport.tsx","./src/pages/installguide.tsx","./src/pages/institutions.tsx","./src/pages/integrationhub.tsx","./src/pages/kpidashboard.tsx","./src/pages/llmmanager.tsx","./src/pages/licenses.tsx","./src/pages/login.tsx","./src/pages/networkconsole.tsx","./src/pages/notificationrules.tsx","./src/pages/notifications.tsx","./src/pages/repos.tsx","./src/pages/scrapingmanager.tsx","./src/pages/servers.tsx","./src/pages/users.tsx"],"version":"5.9.3"} \ No newline at end of file