headscale-tailscale-replace.../VNC-WebSocket-Proxy-Solution.md
시골약사 ac620a0e15 VNC WebSocket 연결 문제 - 브라우저 보안 정책으로 인한 미해결 상태
WebSocket 1006 오류로 인해 브라우저에서 VNC 연결 실패
- 서버 환경에서는 연결 가능하나 브라우저 보안 정책으로 차단
- 역방향 프록시 솔루션 문서화 완료
- 추후 nginx 프록시 구현 필요

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-12 01:44:47 +09:00

8.1 KiB

VNC WebSocket 연결 문제 및 프록시 솔루션

🚨 현재 문제 상황

WebSocket 1006 연결 실패 원인

WebSocket connection to 'wss://pve7.0bin.in/api2/json/nodes/pve7/qemu/102/vncwebsocket...' failed:
Failed when connecting: Connection closed (code: 1006)

WebSocket Error Code 1006는 "Abnormal Closure"를 의미하며, 연결이 설정되기도 전에 종료되었음을 나타냅니다.

🔍 근본 원인 분석

1. 브라우저 보안 정책 (Mixed Content)

  • 현재 상황: HTTPS 웹사이트 (pqadmin.0bin.in)에서 자체 서명 인증서를 사용하는 WebSocket 연결 시도
  • 브라우저 제한: Chrome, Firefox 등 모던 브라우저는 HTTPS 페이지에서 신뢰할 수 없는 SSL 인증서로의 WebSocket 연결을 자동 차단
  • 에러 발생: 브라우저가 연결 시도 자체를 거부하여 1006 에러 발생

2. SSL/TLS 인증서 신뢰 문제

# Proxmox 서버의 자체 서명 인증서
Subject: CN=Proxmox Virtual Environment
Issuer: PVE Cluster Manager CA
  • 자체 서명 인증서: pve7.0bin.in이 공인 CA가 아닌 자체 서명 인증서 사용
  • 브라우저 불신: 사용자가 명시적으로 인증서를 신뢰하지 않으면 WebSocket 연결 거부

3. CORS (Cross-Origin Resource Sharing) 정책

  • Origin 불일치: pqadmin.0bin.in에서 pve7.0bin.in으로의 크로스 오리진 WebSocket 연결
  • Preflight 검사: 브라우저가 연결 전 OPTIONS 요청을 보내지만 Proxmox가 적절한 CORS 헤더를 반환하지 않을 가능성

💡 해결책 1: 역방향 프록시 (Reverse Proxy)

구현 방식: nginx를 통한 WebSocket 프록시

# /etc/nginx/sites-available/pqadmin-vnc
server {
    listen 443 ssl;
    server_name pqadmin.0bin.in;
    
    ssl_certificate /path/to/ssl/cert.pem;
    ssl_certificate_key /path/to/ssl/key.pem;
    
    # VNC WebSocket 프록시
    location /vnc-proxy/ {
        # WebSocket 업그레이드 헤더
        proxy_pass https://pve7.0bin.in/api2/json/nodes/pve7/qemu/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $proxy_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;
        
        # SSL 검증 비활성화 (자체 서명 인증서 때문)
        proxy_ssl_verify off;
        proxy_ssl_server_name on;
        
        # 타임아웃 설정
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

장점

  • 같은 도메인: pqladmin.0bin.in/vnc-proxy/로 WebSocket 연결하여 CORS 문제 해결
  • 신뢰할 수 있는 SSL: nginx가 공인 인증서를 사용하여 브라우저 신뢰 확보
  • 투명한 프록시: 클라이언트는 Proxmox 직접 연결과 동일하게 작동

구현 단계

# 1. nginx 설정 파일 생성
sudo nano /etc/nginx/sites-available/pqadmin-vnc

# 2. 심볼릭 링크 생성
sudo ln -s /etc/nginx/sites-available/pqadmin-vnc /etc/nginx/sites-enabled/

# 3. nginx 설정 테스트
sudo nginx -t

# 4. nginx 재시작
sudo systemctl reload nginx

클라이언트 코드 수정

// 기존: 직접 Proxmox 연결
// const websocketUrl = 'wss://pve7.0bin.in/api2/json/nodes/pve7/qemu/102/vncwebsocket?...'

// 변경: nginx 프록시를 통한 연결
const websocketUrl = 'wss://pqladmin.0bin.in/vnc-proxy/102/vncwebsocket?...'

💡 해결책 2: WebSocket 프록시 서버

구현 방식: Node.js/Python WebSocket 중계 서버

# vnc_websocket_proxy.py
import asyncio
import websockets
import ssl
import json
from urllib.parse import parse_qs

class VNCWebSocketProxy:
    def __init__(self):
        self.proxmox_host = "pve7.0bin.in"
        self.proxmox_port = 443
        
    async def proxy_websocket(self, websocket, path):
        # 클라이언트 요청 파싱
        query_params = parse_qs(path.split('?', 1)[1] if '?' in path else '')
        vmid = query_params.get('vmid', [''])[0]
        ticket = query_params.get('vncticket', [''])[0]
        
        # Proxmox WebSocket URL 생성
        proxmox_url = f"wss://{self.proxmox_host}:{self.proxmox_port}/api2/json/nodes/pve7/qemu/{vmid}/vncwebsocket?port=5900&vncticket={ticket}"
        
        # SSL 컨텍스트 (자체 서명 인증서 허용)
        ssl_context = ssl.create_default_context()
        ssl_context.check_hostname = False
        ssl_context.verify_mode = ssl.CERT_NONE
        
        try:
            # Proxmox WebSocket 연결
            async with websockets.connect(proxmox_url, ssl=ssl_context) as proxmox_ws:
                # 양방향 데이터 중계
                await asyncio.gather(
                    self.forward_messages(websocket, proxmox_ws),
                    self.forward_messages(proxmox_ws, websocket)
                )
        except Exception as e:
            await websocket.send(json.dumps({"error": f"Proxmox connection failed: {str(e)}"}))
    
    async def forward_messages(self, source, destination):
        try:
            async for message in source:
                await destination.send(message)
        except websockets.exceptions.ConnectionClosed:
            pass

# 프록시 서버 시작
if __name__ == "__main__":
    proxy = VNCWebSocketProxy()
    start_server = websockets.serve(proxy.proxy_websocket, "0.0.0.0", 8765)
    asyncio.get_event_loop().run_until_complete(start_server)
    asyncio.get_event_loop().run_forever()

장점

  • 완전한 제어: WebSocket 연결 과정을 완전히 제어 가능
  • 에러 처리: 연결 실패 시 상세한 에러 메시지 제공
  • 로깅: 모든 WebSocket 트래픽 로깅 및 디버깅 가능
  • 인증 확장: 추가적인 인증 로직 구현 가능

구현 단계

# 1. 의존성 설치
pip install websockets asyncio

# 2. 프록시 서버 실행
python vnc_websocket_proxy.py

# 3. systemd 서비스 등록
sudo systemctl enable vnc-websocket-proxy.service

클라이언트 코드 수정

// WebSocket 프록시 서버로 연결
const websocketUrl = 'ws://localhost:8765/vnc-proxy?vmid=102&vncticket=' + encodeURIComponent(ticket);

📊 솔루션 비교

기준 역방향 프록시 (nginx) WebSocket 프록시 서버
구현 복잡도 🟡 중간 🔴 높음
성능 🟢 우수 🟡 양호
유지보수 🟢 쉬움 🟡 보통
확장성 🟡 제한적 🟢 우수
디버깅 🟡 제한적 🟢 우수
보안 🟢 안전 🟡 주의 필요
리소스 사용량 🟢 낮음 🟡 보통

🎯 추천 솔루션

단기 해결책: 역방향 프록시 (nginx)

  1. 빠른 구현: 기존 nginx 설정에 location 블록만 추가
  2. 안정성: nginx의 검증된 WebSocket 프록시 기능 활용
  3. 보안: 공인 SSL 인증서로 브라우저 신뢰 확보

장기 해결책: 하이브리드 접근

  1. nginx 프록시: 기본적인 WebSocket 중계
  2. 커스텀 미들웨어: 인증, 로깅, 모니터링 기능 추가
  3. 로드 밸런싱: 다중 Proxmox 호스트 지원

🚀 구현 우선순위

Phase 1: nginx 역방향 프록시 구현

# 1. nginx 설정 파일 작성
# 2. SSL 인증서 설정
# 3. WebSocket 프록시 규칙 추가
# 4. 클라이언트 URL 변경

Phase 2: 에러 처리 및 모니터링 강화

# 1. 연결 실패 시 재시도 로직
# 2. WebSocket 상태 모니터링
# 3. 로그 수집 및 분석

Phase 3: 다중 호스트 지원

# 1. 동적 백엔드 라우팅
# 2. 헬스체크 구현
# 3. 로드 밸런싱 설정

결론: WebSocket 1006 에러는 브라우저 보안 정책과 인증서 신뢰 문제가 주요 원인입니다. nginx 역방향 프록시를 통해 이 문제를 근본적으로 해결할 수 있으며, 이는 가장 실용적이고 안정적인 솔루션입니다.