WebRTC TURN을 통한 SOCKS5 터널링: Go로 구현하기
WB Stream 플랫폼에서 WebRTC TURN 세션 내에 SOCKS5 트래픽을 숨기는 도구가 개발되었습니다. 트래픽은 LiveKit SFU를 사용해 화상 회의로 위장합니다. 전체 사이클: 역방향 HTTP API, protobuf 파싱, TURN 릴레이를 통한 KCP, yamux 멀티플렉싱. 브라우저 없이 단일 실행 명령으로 작동합니다.
stream.wb.ru API 역공학
puppeteer를 통한 트래픽 수집으로 네 번의 HTTP 요청과 WebSocket 연결 순서가 밝혀졌습니다. 메인 페이지는 브라우저 없이 요청을 차단하지만(코드 498), API 엔드포인트는 접근 가능합니다.
순서:
- POST /auth/api/v1/auth/user/guest-register — displayName이 포함된 게스트 JWT 토큰.
- POST /api-room/api/v2/room —
roomType: ROOM_TYPE_ALL_ON_SCREEN,roomPrivacy: ROOM_PRIVACY_FREE로 방 생성. - POST /api-room/api/v1/room/{id}/join — 참가 (GET은 501 반환).
- GET /api-room-manager/api/v1/room/{id}/token — LiveKit 토큰 (Accept-Encoding에서 gzip 고려).
WebSocket: wss://wbstream01-el.wb.ru:7880/rtc?access_token=...&sdk=js&version=2.15.3&protocol=16. ICE 서버: turn:wb-stream-turn-1.wb.ru:3478.
쿠키/CSRF 없이 net/http로 구현.
rr, _ := wb(cl, "POST", "/auth/api/v1/auth/user/guest-register",
[]byte(`{"displayName":"lh_42"}`), "")
LiveKit SignalResponse를 위한 Protobuf 파서
LiveKit는 ICE 서버를 SignalResponse > JoinResponse > ICEServer(필드 1>5/9)로 전송합니다. 전체 google.golang.org/protobuf는 과도하므로 최소 40줄 파서를 작성했습니다.
Varint 디코딩:
func pbVar(d []byte, o int) (uint64, int) {
var v uint64
for s := 0; o < len(d) && s < 64; s += 7 {
b := d[o]; o++
v |= uint64(b&0x7f) << s
if b < 0x80 { return v, o }
}
return 0, o
}
길이 구분 필드 추출:
func pbAll(d []byte, f uint64) (r [][]byte) {
for o := 0; o < len(d); {
t, n := pbVar(d, o)
if n == o { break }
o = n
switch t & 7 {
case 2:
l, n := pbVar(d, o); o = n
e := o + int(l)
if e > len(d) || e < o { return }
if t>>3 == f { r = append(r, d[o:e]) }
o = e
// ...
}
}
return
}
파서는 필드 5(WB 포크)와 9(표준)에서 ICE 서버를 검색해 urls, username, credential을 추출합니다.
터널 아키텍처: KCP + TURN + yamux
SOCKS5의 TCP → yamux 스트림 → KCP(AES-256) → TURN 릴레이 → VPS → 인터넷.
주요 구성 요소:
- TURN 할당 via
pion/turn:tc.Allocate()는net.PacketConn반환. - 릴레이를 통한 KCP:
kcp.NewConn(peerVPS, blk, 10, 3, relay). - 스트림 멀티플렉싱을 위한 Yamux.
- VPS의 SOCKS5 서버.
uc, _ := net.ListenUDP("udp", nil)
tc, _ := turn.NewClient(&turn.ClientConfig{...})
relay, _ := tc.Allocate()
kc, _ := kcp.NewConn(peerVPS, blk, 10, 3, relay)
서버: KCP 리스너 + yamux 서버 + SOCKS5.
자동화 및 내결함성
- 스마트 키: base64(
IP|SHA256(password)). - 재연결: yamux ping/15s, 백오프 2-60s, TURN 자격 증명 캐시 5분.
- 자체 정리:
pgrep+ 이전 인스턴스 종료, systemd 덮어쓰기. - 우아한 종료: Ctrl+C → 2초 정리.
실행 및 제한 사항
go build -o lionheart .
# 서버
./lionheart # 키 복사
# 클라이언트
./lionheart # 키 붙여넣기 → 127.0.0.1:1080
제한 사항: WB Stream API 의존성, 하드코딩된 wbstream01-el.wb.ru. 계획: Android/iOS용 gomobile(VpnService/NEPacketTunnelProvider).
핵심 포인트
- DPI/방화벽을 위한 WebRTC 트래픽 위장, 기가비트 속도 지원.
- 의존성 없는 최소 protobuf 파서.
- KCP를 위한
net.PacketConn으로서의 TURN 릴레이. - 브라우저/API 키 없이 완전 자동화.
- 순수 Go, gomobile 준비 완료.
— Editorial Team
아직 댓글이 없습니다.