diff --git a/PROXMOX_WEBSOCKET_INTEGRATION_PLAN.md b/PROXMOX_WEBSOCKET_INTEGRATION_PLAN.md new file mode 100644 index 0000000..091ea75 --- /dev/null +++ b/PROXMOX_WEBSOCKET_INTEGRATION_PLAN.md @@ -0,0 +1,458 @@ +# Proxmox VNC WebSocket 직접 연결 해결 방안 + +## 🎯 목표 + +Flask 웹 애플리케이션에서 Proxmox VNC WebSocket에 **직접 연결**하여 브라우저 내에서 seamless한 VNC 경험 제공 + +## 🔍 현재 문제 상황 + +### 1. 문제 증상 +- Flask에서 VNC 버튼 클릭 시 `"Unsupported"... is not valid JSON` 오류 발생 +- noVNC 클라이언트가 Proxmox WebSocket 서버에 연결 실패 +- HTTP 501 응답 (nginx에서 WebSocket 요청 거부) + +### 2. 근본 원인 분석 +``` +[브라우저] --> [Flask:5002] --> [Proxmox:443] --> [실제 WebSocket:5900] + ❌ CORS 이슈 ❌ nginx 차단 ✅ VM VNC 서버 +``` + +**주요 차단 요인:** +- Proxmox nginx가 외부 WebSocket 연결 차단 +- CORS (Cross-Origin Resource Sharing) 정책 +- SSL/TLS 인증서 불일치 +- WebSocket Upgrade 헤더 처리 문제 + +## 💡 해결 방안 (3가지 접근법) + +## 방안 1: Flask WebSocket 프록시 서버 (권장) + +### 개념도 +``` +[브라우저] <--WebSocket--> [Flask Proxy] <--WebSocket--> [Proxmox VNC] + ws://localhost:5002/ws/vnc/vm123 wss://pve7:443/api2/... +``` + +### 구현 방법 + +#### 1.1 Flask-SocketIO 기반 프록시 +```python +# requirements.txt에 추가 +flask-socketio==5.3.6 +python-socketio==5.8.0 +websockets==11.0.3 + +# app.py 수정 +from flask_socketio import SocketIO, emit +import websockets +import asyncio +import ssl + +socketio = SocketIO(app, cors_allowed_origins="*") + +@socketio.on('vnc_connect') +def handle_vnc_connect(data): + """VNC WebSocket 프록시 연결""" + vm_id = data['vm_id'] + node = data['node'] + + # Proxmox VNC 티켓 생성 + client = ProxmoxClient(PROXMOX_HOST, PROXMOX_USERNAME, PROXMOX_PASSWORD) + vnc_data = client.get_vnc_ticket(node, vm_id) + + if vnc_data: + # 백그라운드에서 프록시 실행 + socketio.start_background_task(proxy_vnc_connection, + request.sid, + vnc_data['websocket_url']) + +def proxy_vnc_connection(session_id, proxmox_ws_url): + """Proxmox VNC WebSocket과 브라우저 간 프록시""" + asyncio.run(proxy_websocket(session_id, proxmox_ws_url)) + +async def proxy_websocket(session_id, proxmox_ws_url): + """비동기 WebSocket 프록시""" + try: + # SSL 컨텍스트 설정 (인증서 검증 무시) + ssl_context = ssl.create_default_context() + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + # Proxmox WebSocket 연결 + async with websockets.connect( + proxmox_ws_url, + ssl=ssl_context, + extra_headers={'Origin': f'https://{PROXMOX_HOST}'} + ) as proxmox_ws: + + @socketio.on('vnc_data', namespace=f'/vnc/{session_id}') + def forward_to_proxmox(data): + asyncio.create_task(proxmox_ws.send(data)) + + # Proxmox → 브라우저 + async for message from proxmox_ws: + socketio.emit('vnc_data', message, + room=session_id, + namespace=f'/vnc/{session_id}') + + except Exception as e: + socketio.emit('vnc_error', {'error': str(e)}, room=session_id) +``` + +#### 1.2 noVNC 클라이언트 수정 +```html + + + +``` + +### 장점 +- ✅ 완전한 제어 가능 +- ✅ CORS 문제 해결 +- ✅ Flask 애플리케이션 내 통합 +- ✅ 추가 인증/로깅 가능 + +### 단점 +- ❌ 구현 복잡성 높음 +- ❌ 성능 오버헤드 존재 +- ❌ WebSocket 프로토콜 깊이 이해 필요 + +--- + +## 방안 2: Nginx 리버스 프록시 설정 + +### 개념도 +``` +[브라우저] --> [Nginx Proxy] --> [Proxmox WebSocket] + ws://proxy/vnc/vm123 wss://pve7:443/api2/... +``` + +### 구현 방법 + +#### 2.1 별도 Nginx 프록시 서버 설정 +```nginx +# /etc/nginx/sites-available/proxmox-vnc-proxy +server { + listen 8080; + server_name localhost; + + # WebSocket 프록시 설정 + location /vnc/ { + # URL 경로에서 VM 정보 추출: /vnc/pve7/100 + # pve7 = node, 100 = vmid + rewrite ^/vnc/([^/]+)/(\d+)$ /api2/json/nodes/$1/qemu/$2/vncwebsocket break; + + proxy_pass https://pve7.0bin.in:443; + proxy_http_version 1.1; + + # WebSocket 업그레이드 헤더 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host pve7.0bin.in; + proxy_set_header Origin https://pve7.0bin.in; + + # 인증 헤더 전달 (VNC 티켓 포함) + proxy_set_header Authorization $http_authorization; + + # SSL 설정 + proxy_ssl_verify off; + proxy_ssl_server_name on; + + # 타임아웃 설정 (VNC 세션용) + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 86400s; # 24시간 + + # 버퍼링 비활성화 (실시간 데이터용) + proxy_buffering off; + proxy_cache off; + } + + # CORS 헤더 추가 + location / { + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; + add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization"; + + if ($request_method = 'OPTIONS') { + return 204; + } + } +} +``` + +#### 2.2 Docker Compose로 Nginx 프록시 실행 +```yaml +# docker-compose.vnc-proxy.yml +version: '3.8' +services: + vnc-proxy: + image: nginx:alpine + ports: + - "8080:80" + volumes: + - ./nginx-vnc-proxy.conf:/etc/nginx/conf.d/default.conf + restart: unless-stopped + networks: + - farmq-network + +networks: + farmq-network: + external: true +``` + +#### 2.3 Flask 코드 수정 +```python +# VNC WebSocket URL을 프록시 서버로 변경 +@app.route('/api/vm/vnc', methods=['POST']) +def api_vm_vnc(): + # ... (기존 코드) + + # 프록시 서버를 통한 WebSocket URL 생성 + proxy_ws_url = f"ws://localhost:8080/vnc/{node}/{vmid}?port={vnc_data['port']}&vncticket={vnc_data['ticket']}" + + vnc_sessions[session_id] = { + 'websocket_url': proxy_ws_url, + # ... + } +``` + +### 장점 +- ✅ Flask 코드 변경 최소화 +- ✅ 높은 성능 (Nginx 네이티브) +- ✅ 확장성 우수 +- ✅ SSL 종료 지원 + +### 단점 +- ❌ 추가 인프라 필요 +- ❌ Nginx 설정 복잡성 +- ❌ 디버깅 어려움 + +--- + +## 방안 3: Proxmox 서버 설정 변경 + +### 개념도 +``` +[브라우저] ----직접연결----> [Proxmox WebSocket (수정됨)] + wss://pve7:443/api2/json/... +``` + +### 구현 방법 + +#### 3.1 Proxmox Nginx 설정 수정 +```bash +# Proxmox 서버에 SSH 접속 +ssh root@pve7.0bin.in + +# nginx 설정 백업 +cp /etc/nginx/sites-available/proxmox /etc/nginx/sites-available/proxmox.backup + +# nginx 설정 편집 +nano /etc/nginx/sites-available/proxmox +``` + +```nginx +# /etc/nginx/sites-available/proxmox 수정 +server { + listen 443 ssl http2; + server_name pve7.0bin.in; + + # ... 기존 설정 유지 ... + + # CORS 헤더 추가 (VNC WebSocket용) + location /api2/json/nodes/*/qemu/*/vncwebsocket { + # CORS 헤더 + add_header Access-Control-Allow-Origin "http://localhost:5002"; + add_header Access-Control-Allow-Methods "GET, POST, OPTIONS"; + add_header Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization"; + add_header Access-Control-Allow-Credentials true; + + # Preflight 요청 처리 + if ($request_method = 'OPTIONS') { + return 204; + } + + # 기존 proxy_pass 설정 유지 + proxy_pass http://localhost:8006; + proxy_http_version 1.1; + + # WebSocket 업그레이드 헤더 + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # 타임아웃 설정 + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + + # 버퍼링 비활성화 + proxy_buffering off; + proxy_cache off; + } + + # ... 나머지 설정 ... +} +``` + +#### 3.2 Proxmox nginx 재시작 +```bash +# 설정 검증 +nginx -t + +# nginx 재시작 +systemctl restart nginx + +# 방화벽 확인 (필요시) +iptables -L -n | grep 8006 +``` + +#### 3.3 Flask noVNC 클라이언트 수정 +```javascript +// 직접 Proxmox WebSocket 연결 +const websocketUrl = '{{ websocket_url }}'; // wss://pve7.0bin.in:443/api2/json/... + +// CORS 설정으로 직접 연결 가능 +const rfb = new RFB(canvas, websocketUrl, { + credentials: { password: '' }, // VNC 티켓으로 인증 + shared: true +}); +``` + +### 장점 +- ✅ 가장 직접적인 해결책 +- ✅ 최고 성능 +- ✅ 추가 인프라 불필요 + +### 단점 +- ❌ Proxmox 서버 수정 필요 +- ❌ 업데이트 시 설정 초기화 위험 +- ❌ 보안 정책 변경 + +--- + +## 🛠️ 구현 우선순위 및 권장사항 + +### 1단계: Flask WebSocket 프록시 (권장) +**기간**: 2-3일 +**난이도**: 중상 + +```bash +# 구현 단계 +1. Flask-SocketIO 설치 및 설정 +2. VNC WebSocket 프록시 함수 구현 +3. noVNC 클라이언트 수정 +4. 테스트 및 디버깅 +``` + +### 2단계: Nginx 프록시 (백업 방안) +**기간**: 1-2일 +**난이도**: 중 + +```bash +# 구현 단계 +1. nginx 프록시 설정 작성 +2. Docker Compose로 프록시 실행 +3. Flask WebSocket URL 수정 +4. 연결 테스트 +``` + +### 3단계: Proxmox 설정 변경 (최후 수단) +**기간**: 1일 +**난이도**: 하 (위험도: 상) + +```bash +# 주의사항 +- Proxmox 서버 직접 수정 +- 백업 필수 +- 업데이트 시 재설정 필요 +``` + +## 🔧 즉시 구현 가능한 임시 해결책 + +현재 리다이렉트 방식을 개선하여 사용성 향상: + +```python +# app.py - 자동 팝업 + 인증 정보 전달 +@app.route('/vnc/') +def vnc_console(session_id): + # Proxmox 자동 로그인 URL 생성 + client = ProxmoxClient(PROXMOX_HOST, PROXMOX_USERNAME, PROXMOX_PASSWORD) + login_ticket = client.ticket + + proxmox_url = f"https://{PROXMOX_HOST}:443/?console=kvm&vmid={vmid}&node={node}&PVEAuthCookie={login_ticket}" + + return render_template('vnc_auto_redirect.html', + proxmox_url=proxmox_url, + auto_open=True) +``` + +## 📋 구현 체크리스트 + +### Flask WebSocket 프록시 구현 +- [ ] Flask-SocketIO 설치 및 기본 설정 +- [ ] Proxmox WebSocket 연결 테스트 +- [ ] 비동기 프록시 함수 구현 +- [ ] noVNC 클라이언트 SocketIO 연동 +- [ ] 에러 처리 및 재연결 로직 +- [ ] 성능 테스트 및 최적화 + +### 테스트 시나리오 +- [ ] 단일 VM VNC 연결 테스트 +- [ ] 동시 다중 VNC 연결 테스트 +- [ ] 네트워크 끊김 시 재연결 테스트 +- [ ] 브라우저별 호환성 테스트 +- [ ] 모바일 디바이스 테스트 + +### 운영 준비 +- [ ] 로깅 및 모니터링 설정 +- [ ] 에러 알림 시스템 구축 +- [ ] 백업 연결 방법 준비 +- [ ] 사용자 매뉴얼 작성 + +--- + +## 💡 결론 + +**권장 접근법**: Flask WebSocket 프록시 (방안 1) + +1. **완전한 제어**: Flask 애플리케이션 내에서 모든 것을 제어 +2. **확장성**: 향후 추가 기능 (녹화, 공유 등) 쉽게 추가 가능 +3. **안정성**: Proxmox 서버 수정 없이 구현 +4. **통합성**: 기존 Flask 인증/권한 시스템과 완벽 통합 + +**다음 단계**: Flask-SocketIO 기반 WebSocket 프록시 구현 시작 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 106cb47..5598486 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: networks: - headscale-net healthcheck: - test: ["CMD-SHELL", "nc -z localhost 8080 || exit 1"] + test: ["CMD", "/ko-app/headscale", "version"] interval: 30s timeout: 10s retries: 3 diff --git a/quick-install.sh b/quick-install.sh new file mode 100755 index 0000000..14e8d97 --- /dev/null +++ b/quick-install.sh @@ -0,0 +1,445 @@ +#!/bin/bash + +# 팜큐(FARMQ) Headscale 원클릭 설치 및 등록 스크립트 +# 사용법: curl -fsSL https://raw.githubusercontent.com/your-repo/headscale-setup/main/quick-install.sh | sudo bash +# 또는: wget -qO- https://raw.githubusercontent.com/your-repo/headscale-setup/main/quick-install.sh | sudo bash + +set -e + +# ================================ +# 설정 (필요시 수정) +# ================================ +HEADSCALE_SERVER="http://192.168.0.151:8070" # Headscale 서버 주소 +PREAUTH_KEY="8b3df41d37cb158ea39f41fc32c9af46e761de817ad06038" # 7일간 재사용 가능한 키 +FARMQ_NETWORK="100.64.0.0/10" # 팜큐 네트워크 대역 + +# ================================ +# 색상 출력 함수 +# ================================ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +PURPLE='\033[0;35m' +CYAN='\033[0;36m' +WHITE='\033[1;37m' +NC='\033[0m' # No Color + +print_header() { + echo -e "\n${PURPLE}============================================${NC}" + echo -e "${WHITE}$1${NC}" + echo -e "${PURPLE}============================================${NC}\n" +} + +print_status() { + echo -e "\n${BLUE}🔧 $1${NC}" +} + +print_success() { + echo -e "\n${GREEN}✅ $1${NC}" +} + +print_error() { + echo -e "\n${RED}❌ $1${NC}" +} + +print_info() { + echo -e "\n${CYAN}📋 $1${NC}" +} + +print_warning() { + echo -e "\n${YELLOW}⚠️ $1${NC}" +} + +# ================================ +# 운영체제 감지 +# ================================ +detect_os() { + if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$ID + VERSION=$VERSION_ID + CODENAME=$VERSION_CODENAME + else + print_error "지원하지 않는 운영체제입니다." + exit 1 + fi + + print_info "감지된 OS: $OS $VERSION ($CODENAME)" +} + +# ================================ +# 시스템 요구사항 확인 +# ================================ +check_requirements() { + print_status "시스템 요구사항 확인 중..." + + # Root 권한 확인 + if [ "$EUID" -ne 0 ]; then + print_error "이 스크립트는 root 권한으로 실행해야 합니다." + print_info "sudo $0 로 다시 실행해주세요." + exit 1 + fi + + # curl 또는 wget 확인 + if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then + print_error "curl 또는 wget이 필요합니다." + exit 1 + fi + + # 네트워크 연결 확인 + if ! ping -c 1 8.8.8.8 >/dev/null 2>&1; then + print_warning "인터넷 연결을 확인해주세요." + fi + + print_success "시스템 요구사항 확인 완료" +} + +# ================================ +# Tailscale 설치 +# ================================ +install_tailscale() { + print_status "Tailscale 클라이언트 설치 중..." + + # 이미 설치되어 있는지 확인 + if command -v tailscale >/dev/null 2>&1; then + print_info "Tailscale이 이미 설치되어 있습니다." + TAILSCALE_VERSION=$(tailscale version | head -n1) + print_info "현재 버전: $TAILSCALE_VERSION" + return + fi + + case $OS in + ubuntu|debian) + print_info "Ubuntu/Debian용 Tailscale 설치 중..." + + # GPG 키 추가 + curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null + curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/jammy.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list + + # 패키지 설치 + apt-get update -qq + apt-get install -y tailscale + ;; + + centos|rhel|rocky|almalinux) + print_info "CentOS/RHEL/Rocky용 Tailscale 설치 중..." + + # 리포지토리 추가 + curl -fsSL https://pkgs.tailscale.com/stable/rhel/tailscale.repo | tee /etc/yum.repos.d/tailscale.repo + + # 패키지 설치 + if command -v dnf >/dev/null 2>&1; then + dnf install -y tailscale + else + yum install -y tailscale + fi + ;; + + fedora) + print_info "Fedora용 Tailscale 설치 중..." + dnf install -y tailscale + ;; + + arch) + print_info "Arch Linux용 Tailscale 설치 중..." + pacman -S --noconfirm tailscale + ;; + + *) + print_warning "지원하지 않는 배포판입니다. 수동 설치를 시도합니다." + # Universal binary 다운로드 + ARCH=$(uname -m) + case $ARCH in + x86_64) TAILSCALE_ARCH="amd64" ;; + aarch64) TAILSCALE_ARCH="arm64" ;; + armv7l) TAILSCALE_ARCH="arm" ;; + *) + print_error "지원하지 않는 아키텍처: $ARCH" + exit 1 + ;; + esac + + # 최신 버전 다운로드 + TAILSCALE_VERSION=$(curl -s https://api.github.com/repos/tailscale/tailscale/releases/latest | grep '"tag_name"' | cut -d'"' -f4) + DOWNLOAD_URL="https://pkgs.tailscale.com/stable/tailscale_${TAILSCALE_VERSION#v}_linux_${TAILSCALE_ARCH}.tgz" + + cd /tmp + curl -LO "$DOWNLOAD_URL" + tar xzf "tailscale_${TAILSCALE_VERSION#v}_linux_${TAILSCALE_ARCH}.tgz" + + # 바이너리 복사 + cp "tailscale_${TAILSCALE_VERSION#v}_linux_${TAILSCALE_ARCH}/tailscale" /usr/bin/ + cp "tailscale_${TAILSCALE_VERSION#v}_linux_${TAILSCALE_ARCH}/tailscaled" /usr/sbin/ + + # 시스템 서비스 파일 생성 + cat > /etc/systemd/system/tailscaled.service << 'EOF' +[Unit] +Description=Tailscale node agent +Documentation=https://tailscale.com/kb/ +Wants=network-pre.target +After=network-pre.target NetworkManager.service systemd-resolved.service + +[Service] +EnvironmentFile=/etc/default/tailscaled +ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port=$PORT $FLAGS +ExecStopPost=/usr/bin/tailscale logout +Restart=on-failure +RestartSec=5 + +Type=notify +RuntimeDirectory=tailscale +RuntimeDirectoryMode=0755 +StateDirectory=tailscale +StateDirectoryMode=0700 +CacheDirectory=tailscale +CacheDirectoryMode=0750 + +[Install] +WantedBy=multi-user.target +EOF + + # 환경 설정 파일 + mkdir -p /etc/default + echo 'FLAGS=""' > /etc/default/tailscaled + echo 'PORT="41641"' >> /etc/default/tailscaled + + systemctl daemon-reload + ;; + esac + + print_success "Tailscale 설치 완료" + + # 버전 확인 + TAILSCALE_VERSION=$(tailscale version | head -n1) + print_info "설치된 버전: $TAILSCALE_VERSION" +} + +# ================================ +# Tailscale 서비스 시작 +# ================================ +start_tailscale() { + print_status "Tailscale 서비스 시작 중..." + + # systemd 서비스 활성화 및 시작 + systemctl enable tailscaled >/dev/null 2>&1 || true + systemctl start tailscaled >/dev/null 2>&1 || true + + # 서비스 상태 확인 + sleep 3 + if systemctl is-active --quiet tailscaled; then + print_success "Tailscaled 서비스가 실행 중입니다." + else + print_error "Tailscaled 서비스 시작에 실패했습니다." + print_info "수동으로 시작을 시도합니다..." + /usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state & + sleep 5 + fi +} + +# ================================ +# Headscale 등록 +# ================================ +register_headscale() { + print_status "Headscale 서버에 등록 중..." + + # 기존 연결 확인 + if tailscale status >/dev/null 2>&1; then + print_warning "이미 Tailscale/Headscale에 연결되어 있습니다." + tailscale status + + read -p "기존 연결을 해제하고 새로 등록하시겠습니까? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + print_info "기존 연결을 해제합니다..." + tailscale logout >/dev/null 2>&1 || true + sleep 2 + else + print_info "등록을 건너뜁니다." + return + fi + fi + + print_info "Headscale 서버: $HEADSCALE_SERVER" + print_info "Pre-auth Key: ${PREAUTH_KEY:0:8}***************" + + # Headscale 등록 시도 + print_status "등록 명령 실행 중..." + + if tailscale up \ + --login-server="$HEADSCALE_SERVER" \ + --authkey="$PREAUTH_KEY" \ + --accept-routes \ + --accept-dns=false >/dev/null 2>&1; then + + print_success "Headscale 등록 성공!" + + else + print_error "자동 등록에 실패했습니다. 수동 등록을 진행합니다." + + # 수동 등록 모드 + print_info "다음 명령을 실행하여 수동 등록하세요:" + echo "" + echo "tailscale up --login-server=\"$HEADSCALE_SERVER\" --authkey=\"$PREAUTH_KEY\"" + echo "" + + # 등록 URL 시도 + REGISTER_URL=$(tailscale up --login-server="$HEADSCALE_SERVER" 2>&1 | grep -o 'https://[^[:space:]]*' | head -1) + if [ -n "$REGISTER_URL" ]; then + print_info "또는 다음 URL을 방문하여 등록하세요:" + echo "$REGISTER_URL" + fi + + return 1 + fi +} + +# ================================ +# 연결 상태 확인 +# ================================ +verify_connection() { + print_status "연결 상태 확인 중..." + + # 잠시 대기 (연결 안정화) + sleep 5 + + # Tailscale 상태 확인 + if ! tailscale status >/dev/null 2>&1; then + print_error "Tailscale 연결에 문제가 있습니다." + return 1 + fi + + # IP 주소 확인 + TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "N/A") + TAILSCALE_IP6=$(tailscale ip -6 2>/dev/null || echo "N/A") + + print_success "Headscale 네트워크 연결 완료!" + print_info "할당된 IPv4: $TAILSCALE_IP" + print_info "할당된 IPv6: $TAILSCALE_IP6" + + # 네트워크 테스트 + print_status "네트워크 연결 테스트 중..." + + if ping -c 3 -W 5 100.64.0.1 >/dev/null 2>&1; then + print_success "팜큐 네트워크($FARMQ_NETWORK) 연결 정상!" + else + print_warning "네트워크 테스트 실패. 방화벽을 확인해주세요." + fi + + # 연결된 노드 확인 + print_info "네트워크 상태:" + tailscale status | head -10 +} + +# ================================ +# 방화벽 설정 (선택사항) +# ================================ +configure_firewall() { + print_status "방화벽 설정 확인 중..." + + # UFW (Ubuntu/Debian) + if command -v ufw >/dev/null 2>&1; then + print_info "UFW 방화벽 감지됨" + if ufw status | grep -q "Status: active"; then + print_info "Tailscale 트래픽 허용 중..." + ufw allow in on tailscale0 >/dev/null 2>&1 || true + ufw allow 41641/udp comment "Tailscale" >/dev/null 2>&1 || true + fi + fi + + # firewalld (CentOS/RHEL/Fedora) + if command -v firewall-cmd >/dev/null 2>&1; then + print_info "firewalld 방화벽 감지됨" + if firewall-cmd --state >/dev/null 2>&1; then + print_info "Tailscale 트래픽 허용 중..." + firewall-cmd --permanent --add-service=tailscale >/dev/null 2>&1 || true + firewall-cmd --permanent --add-port=41641/udp >/dev/null 2>&1 || true + firewall-cmd --reload >/dev/null 2>&1 || true + fi + fi + + print_success "방화벽 설정 완료" +} + +# ================================ +# 정리 작업 +# ================================ +cleanup() { + print_status "정리 작업 수행 중..." + + # 임시 파일 정리 + rm -rf /tmp/tailscale_* >/dev/null 2>&1 || true + + # 시스템 정보 업데이트 + if command -v updatedb >/dev/null 2>&1; then + updatedb >/dev/null 2>&1 & + fi + + print_success "정리 작업 완료" +} + +# ================================ +# 최종 정보 출력 +# ================================ +show_final_info() { + print_header "팜큐 Headscale 설치 완료!" + + # 시스템 정보 + HOSTNAME=$(hostname) + TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "N/A") + + echo -e "${GREEN}🎉 설치가 성공적으로 완료되었습니다!${NC}\n" + + echo -e "${CYAN}📋 시스템 정보:${NC}" + echo -e " 호스트명: $HOSTNAME" + echo -e " Tailscale IP: $TAILSCALE_IP" + echo -e " OS: $OS $VERSION" + echo -e " Headscale 서버: $HEADSCALE_SERVER" + + echo -e "\n${YELLOW}🔧 유용한 명령어:${NC}" + echo -e " tailscale status # 연결 상태 확인" + echo -e " tailscale ip # 할당된 IP 확인" + echo -e " tailscale ping # 다른 노드와 연결 테스트" + echo -e " tailscale logout # 네트워크에서 해제" + + echo -e "\n${PURPLE}🌐 팜큐 관리자 페이지:${NC}" + echo -e " http://192.168.0.151:5002" + echo -e " http://192.168.0.151:5002/vms (VM 관리)" + + echo -e "\n${WHITE}문제가 있을 경우 로그를 확인하세요:${NC}" + echo -e " journalctl -u tailscaled -f" + + print_header "설치 완료 - 팜큐 네트워크를 사용할 수 있습니다!" +} + +# ================================ +# 메인 함수 +# ================================ +main() { + print_header "팜큐(FARMQ) Headscale 원클릭 설치" + + # 사전 체크 + detect_os + check_requirements + + # 설치 과정 + install_tailscale + start_tailscale + register_headscale + + # 사후 설정 + configure_firewall + verify_connection + + # 정리 및 완료 + cleanup + show_final_info +} + +# ================================ +# 에러 핸들링 +# ================================ +trap 'echo -e "\n❌ 설치 중 오류가 발생했습니다. 로그를 확인해주세요."; exit 1' ERR + +# 스크립트 실행 +main "$@" \ No newline at end of file