SagerNet / SagerNet/sing-box

New outbound: turnrelay (tunnel UDP through WebRTC TURN relays, e.g. VK Calls, for censorship circumvention)

Open
#4,534 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
38.2k
Forks
4.6k
Avg merge
19d 15h
Merged PRs (30d)
1

Description

Summary

I would like to propose a UDP-only outbound, turnrelay, that carries datagrams through WebRTC TURN relays disguised as call media, for the existing wireguard endpoint to use as detour. It exists as a GPL-3.0 Go library and, as of now, as a ready external sing-box module (github.com/romanrublev/turnrelay, the outbound lives in singbox/). The open question for you is whether it is also worth landing in-tree behind a with_turnrelay build tag, and whether you have objections to the outbound shape.

Motivation

Russian networks increasingly enforce whitelists where only a few domestic services are reachable. VK Calls relies on TURN relays that are inside those whitelists. Several open source tools already tunnel traffic through them, but all are standalone VPN apps: no domain or geosite routing, and on iOS they collide with the one-VPN limit when combined with a routing client such as Happ. In anton48/vk-turn-proxy-ios#25 the iOS client maintainer suggested the ideal solution would be adding this functionality as a transport inside clients like V2Box/Happ, some of which can run a sing-box core, while noting it is a larger undertaking. This does that.

Proposed design

turnrelay is a UDP-only outbound with pluggable credential providers: vk (anonymous join of a VK call link) and static (fixed credentials for any TURN relay, e.g. self-hosted coturn). It opens N TURN allocations, obfuscates each as a media stream and presents them as one datagram pipe: Write stripes one datagram over the pool, Read returns one merged datagram. TCP returns "only udp is supported".

L4 comes from the wireguard endpoint through detour: transport/wireguard/client_bind.go already dials UDP on the detour and uses the conn as the WireGuard bind, so nothing changes in the endpoint. Rule-sets decide which connections enter it:

{
  "outbounds": [
    { "type": "turnrelay", "tag": "relay",
      "provider": "vk", "call_link": "https://vk.ru/call/join/<hash>",
      "server": "203.0.113.5", "server_port": 56004,
      "connections": 30, "mode": "srtp" },
    { "type": "direct", "tag": "direct" }
  ],
  "endpoints": [
    { "type": "wireguard", "tag": "wg", "detour": "relay",
      "address": ["10.8.0.2/32"], "private_key": "<client wg key>",
      "peers": [{ "address": "203.0.113.5", "port": 56004,
                  "public_key": "<server wg key>",
                  "allowed_ips": ["0.0.0.0/0", "::/0"] }] }
  ],
  "route": { "rules": [
      { "rule_set": "geosite-ru", "outbound": "direct" },
      { "rule_set": "geoip-ru",   "outbound": "direct" } ],
    "final": "wg" }
}

Config schema

Option Type Meaning
provider vk | static credential source, default vk
call_link / call_links string(s) VK call link(s); about 20 connections per link
turn_server host:port static: the relay; vk: optional override
turn_username, turn_password string static only
server, server_port address, port the VPS running the relay-side server
connections int allocations, default 30, max 60
mode srtp | wrap | dtls obfuscation, default srtp
password string wrap only: HKDF input for the envelope key
udp bool transport to the relay, default true (false selects TCP)
captcha auto | fail | wait on a VK captcha, default auto
DialerOptions for the sockets towards the VK API and the relay (bind_interface, detour), passed to the library as one DialContext hook

Transport details

The wire protocol (credential chain, TURN usage, byte layouts, control frames, multiplexing, failure handling) is documented in the repository. Modes: srtp is real DTLS-SRTP (RFC 5764, RTP payload type 100), the default; wrap is an RTP-framed ChaCha20-Poly1305 envelope around plain DTLS; dtls is legacy plain DTLS, deprecated.

Security notes

  • srtp and dtls use ephemeral ECDHE; the wrap envelope uses a static key shared with the server (no forward secrecy at that layer), but the DTLS session inside it and the WireGuard session inside that both do.
  • WireGuard remains the layer that authenticates both ends.
  • VK sees the anonymous call participants (roughly one per 10 connections) and the bytes relayed per allocation, not destinations.
  • Plaintext modes are not offered; the ban footprint grows with the connection count (default 30, hard maximum 60).

Licensing

GPL-3.0, compatible with sing-box. It already depends on github.com/sagernet/sing for the N.Dialer interface and M.Socksaddr.

Ask

The external module (option b) exists today, so this is not blocking anyone's use. The questions for maintainers are:

  1. Would you accept an in-tree turnrelay outbound behind a with_turnrelay build tag (like with_wireguard): protocol/turnrelay, option/turnrelay.go, include/turnrelay.go?
  2. Any objections to the outbound shape itself, i.e. a UDP-only outbound used as the detour of the wireguard endpoint?

If in-tree is welcome, I will open a PR against that shape.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the external module's singbox/ implementation and transport/wireguard/client_bind.go, then read the referenced transport and configuration details in the turnrelay repository. Done means maintainers have decided whether an in-tree turnrelay outbound and its proposed UDP-only wireguard detour shape are acceptable, after which an implementation PR can be opened if invited.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.