폐쇄망 내부 리소스에 브라우저만으로 안전하게 접근할 수 있게 해주는 리버스 터널 게이트웨이.
VPN 없이 NAT/방화벽 뒤 환경에 접근할 수 있고, 매 연결마다 mTLS + JWT로 신원을 검증하는 Zero Trust 구조다. 원격 데스크톱(RDP/VNC), 터미널(SSH), 웹 페이지(HTTPS headless)를 모두 브라우저 단일 창에서 제어할 수 있다.
웹 클라이언트(브라우저 앱)는 별도 레포로 관리한다. 이 레포는 게이트웨이 서버만 담당한다.
[ 브라우저 ]
↕ WSS:443 (WebSocket Secure)
[ tunnel-proxy 게이트웨이 ]
├─ mTLS + JWT + AccessPolicy ← Zero Trust 인증 레이어
├─ Guacamole 프로토콜 ← RDP / VNC / SSH 렌더링
├─ webkit headless 스트리밍 ← HTTPS 웹페이지 렌더링
└─ 리버스 터널 ← NAT 뒤 에이전트로 연결
↕ TCP (역방향 연결 유지)
[ TunnelAgent ] ←── NAT/방화벽 뒤 내부 환경
↕
[ 내부 타겟: RDP 서버 / VNC 서버 / SSH 서버 / 내부 웹 서비스 ]
일반적인 터널은 서버가 클라이언트에 연결한다. 폐쇄망 환경에서는 외부에서 내부로 들어오는 연결이 방화벽에 막힌다. 리버스 터널은 반대로 내부 에이전트가 먼저 게이트웨이 서버에 연결을 맺고 유지한다. 외부 요청이 들어오면 이미 열려있는 터널을 역방향으로 이용해 내부 리소스에 도달한다.
외부 요청 → 게이트웨이 → (기존 터널 역방향) → 에이전트 → 내부 타겟
↑
에이전트가 먼저 연결해둔 상태
네트워크 위치를 신뢰하지 않는다. 내부망에서 온 요청도 동일하게 검증한다.
연결
↓
mTLS 핸드셰이크 — CA 서명 클라이언트 인증서 검증 (장치 인증)
↓
JWT verify — Bearer 토큰 서명/만료 검증 (사용자 인증)
↓
AccessPolicy — subject / tunnel_id / target_ip / port 규칙 매칭 (리소스 접근 제어)
↓
허용 or 차단 (매칭 규칙 없으면 default-deny)
Apache Guacamole 프로토콜을 게이트웨이에서 직접 구현한다. 서버가 RDP/VNC/SSH 세션을 열고 화면을 렌더링한 뒤, Guacamole instruction 스트림으로 브라우저에 전달한다. 브라우저는 키보드/마우스 이벤트를 역방향으로 전송해 원격 데스크톱/터미널을 제어한다.
Guacamole 프로토콜은 길이 접두사 텍스트 포맷이다:
4.draw,1.0,2.10,3.100,3.200; ← opcode.length.arg, ...
Guacamole는 HTTPS 웹페이지를 지원하지 않는다. 이를 위해 게이트웨이 서버에서 headless 브라우저(webkit/Chromium)가 페이지를 렌더링하고, 변경된 영역(delta)만 압축해 WebSocket으로 브라우저에 스트리밍한다. 사용자 입력은 역방향으로 headless 브라우저에 주입된다.
브라우저 ←─ WSS (delta frame) ─→ 게이트웨이
─ input event ─→ headless webkit
↕ HTTPS
내부 웹 서비스
NAT/방화벽 뒤 환경에서 게이트웨이 서버로 TCP 연결을 먼저 맺고 유지한다. 서버로부터 OPEN 메시지를 받으면 내부 타겟으로 연결을 중계한다. heartbeat 타임아웃으로 단절을 감지하고 지수 백오프로 자동 재연결한다.
에이전트 연결을 수신하고 session_id를 발급해 세션 맵을 관리한다. 외부 요청이 들어오면 해당 에이전트 터널로 OPEN 메시지를 보내 세션을 개설하고 양방향 데이터를 중계한다.
CA가 서명한 클라이언트 인증서를 검증한다. TLS 1.2 이상만 허용한다. 인증 성공 후 CN(Common Name)을 추출해 AccessPolicy의 subject로 사용한다.
HTTP Authorization 헤더의 Bearer 토큰 서명을 검증한다. HS256(HMAC-SHA256), RS256(RSA-SHA256)을 지원한다. 만료(exp), 알고리즘 불일치, 서명 위조를 모두 거부한다.
subject / tunnel_id / target_ip / target_port 4개 필드로 접근을 제어한다.
first-match 방식이며 매칭 규칙이 없으면 default-deny로 차단된다.
* 와일드카드를 지원하고 JSON 파일로 정책을 로드한다.
단일 스레드 epoll ET 모드로 다수의 TCP 연결을 처리한다. splice 시스템 콜로 커널-유저 간 메모리 복사 없이 데이터를 전달한다.
OpenSSL 기반 TLS 암호화 레이어. 핸드셰이크, SSL_read/SSL_write 논블로킹 처리를 담당한다.
SOCK_DGRAM 기반 UDP 포워딩. (src_ip, src_port) → target_fd 세션 테이블로 클라이언트를 분리하고 만료된 세션을 자동 정리한다.
에이전트와 게이트웨이 서버 간 통신은 16바이트 고정 헤더 바이너리 프로토콜을 사용한다.
[ magic(4) | type(1) | flags(1) | reserved(2) | session_id(4) | length(4) ] + payload
0x544E4C50 0=컨트롤 / 1+=터널 세션
| 타입 | 방향 | 설명 |
|---|---|---|
| HELLO / HELLO_ACK | 에이전트 → 서버 | 최초 등록 (agent_id 전달) |
| OPEN / OPEN_ACK | 서버 → 에이전트 | 세션 개설 (target_ip:port 전달) |
| DATA | 양방향 | 페이로드 중계 |
| CLOSE | 양방향 | 세션 종료 |
| HEARTBEAT / HEARTBEAT_ACK | 양방향 | 연결 유지 확인 |
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
make -j$(nproc)외부 의존성은 cmake 시점에 FetchContent로 자동 다운로드된다. apt 설치 불필요.
cd build && ctest --output-on-failure| 경로 | 대상 |
|---|---|
tests/phase1/ |
Config JSON 파싱, Logger |
tests/phase2/ |
EpollProxy 비동기 I/O |
tests/phase3/ |
splice zero-copy 처리량 벤치마크 |
tests/phase4/ |
TLS 인증서 유효/만료/위조 검증 |
tests/phase5/ |
UDP 에코, 세션 분리, 타임아웃 |
tests/phase6/ |
터널 프레임 직렬화/파싱/검증 |
tests/phase7/ |
mTLS CN 추출, JWT 위조·만료·알고리즘 불일치, AccessPolicy |
각 Phase별 시나리오 설명은 tests/phaseN/phaseN.md 를 참고한다.
| 항목 | 버전 | 관리 방법 |
|---|---|---|
| CMake | 3.14+ | 시스템 |
| GCC | 11+ (C++17) | 시스템 |
| OpenSSL | 3.x | 시스템 (find_package) |
| nlohmann/json | v3.11.3 | FetchContent |
| Google Test | v1.14.0 | FetchContent |
tunnel-proxy/
├── include/
│ ├── core/
│ │ ├── basic_proxy.h # TCP 멀티스레드 프록시
│ │ ├── epoll_proxy.h # epoll ET + splice zero-copy
│ │ ├── tls_proxy.h # OpenSSL TLS 프록시
│ │ ├── udp_proxy.h # UDP 세션 테이블 프록시
│ │ ├── tunnel_protocol.h # 바이너리 프레임 프로토콜
│ │ ├── tunnel_agent.h # 역방향 연결 에이전트
│ │ ├── tunnel_server.h # 에이전트 수신 + 세션 라우터
│ │ ├── mtls_context.h # mTLS SSL_CTX + CN 추출
│ │ ├── jwt_verifier.h # HS256/RS256 JWT 검증
│ │ └── access_policy.h # 접근 제어 정책 (first-match)
│ └── utils/
│ ├── config.h
│ └── logger.h
├── src/
│ ├── main.cpp
│ ├── basic_proxy.cpp
│ ├── epoll_proxy.cpp
│ ├── tls_proxy.cpp
│ ├── udp_proxy.cpp
│ ├── tunnel_protocol.cpp
│ ├── tunnel_agent.cpp
│ ├── tunnel_server.cpp
│ ├── mtls_context.cpp
│ ├── jwt_verifier.cpp
│ ├── access_policy.cpp
│ └── utils/
│ ├── config.cpp
│ └── logger.cpp
├── tests/
│ ├── phase1/ … phase7/ # 각 phaseN.md에 시나리오 설명
├── scripts/
│ ├── build.sh
│ ├── test.sh
│ └── gen_cert.sh
├── config.json
└── CMakeLists.txt