# 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 프록시 구현 시작