Sign inSign up

ulfrasark/turnable-wg

By ulfrasark

Updated 13 days ago

All-in-one VK-call tunnel for MikroTik RouterOS - multi-stream DTLS proxy + WireGuard

Image
Networking
Security
0

142

ulfrasark/turnable-wg repository overview

All-in-one VK-call tunnel for MikroTik RouterOS: multi-stream DTLS proxy + WireGuard in ONE container. Traffic rides VK Calls' TURN relay (disguised as call media), terminates on the built-in WireGuard, which NATs out to the internet. No separate WG host, no external backend, no reverse proxy.

Pairs with a WireGuard-TURN Android client that supports Session-ID stream aggregation: multiple DTLS streams from one client are grouped by its UUID into ONE backend connection — more throughput and stability while the call still shows a single participant.


Image size

LayerSize
Compressed (Docker Hub)~20 MB
Unpacked (on device)~45 MB

Multi-stage build — proxy server + userspace wireguard-go on Alpine, no Go toolchain in the final image. Multi-arch: amd64 / arm64 / armv7.


What's inside

ComponentRole
DTLS multi-stream proxyListens for VK-relayed DTLS, decrypts, forwards to local WireGuard. Session-ID aggregation + Round-Robin.
WireGuardThe actual VPN that routes to the internet. Userspace wireguard-go (works in containers without the kernel module).
NATMasquerades the WG subnet out the container's egress. Set up automatically.

Ports — set in config.json (no reverse proxy)

If 56000 is taken by another container, change the port in ONE place — config.json in the mount:

{
  "public_ip": "YOUR.VPS.IP",
  "listen_port": 56001,
  "wg_port": 51821,
  "wg_dns": "1.1.1.1",
  "stream_num": 4
}
  • listen_port — where the VK relay sends DTLS. Change this to avoid clashes; it's auto-written into the client config.
  • wg_port — internal WireGuard port (not exposed; the client reaches WG through the tunnel).
  • Priority: config.json > env vars > defaults. Defaults 56001 / 51821 so it won't collide with a turnable-hy2 on 56000.

MikroTik settings

/interface/veth add name=veth-wg address=172.16.184.2/24 gateway=172.16.184.1
/ip/address add address=172.16.184.1/24 interface=veth-wg
/ip/firewall/nat/add chain=srcnat src-address=172.16.184.0/24 action=masquerade comment="turnable-wg out"
/ip/firewall/nat/add chain=dstnat protocol=udp dst-port=56001 action=dst-nat to-addresses=172.16.184.2 to-ports=56001 comment="turnable-wg listen"

Only your listen_port (default 56001) is forwarded in. The WG port stays internal — the client reaches WireGuard THROUGH the TURN tunnel, not directly.


Mount for config + persistent keys

/container/mounts/add name=vkwg_conf src=/usb1/turnable-wg-confs dst=/etc/vkturn

Holds config.json, plus auto-created .keys/ (WG server + client keypairs + PSK) and clients/client-wg.conf (the ready client config). Without this mount the keys regenerate on every restart and old client configs break.


Environment variables

/container/envs/add name=vkwg-env key=PUBLIC_IP value=your.vps.ip
/container/envs/add name=vkwg-env key=VKTURN_LISTEN_PORT value=56001
/container/envs/add name=vkwg-env key=WG_PORT value=51821

Config.json takes priority over env if both are set.


Install container

/container/add
remote-image=ulfrasark/turnable-wg:v1
interface=veth-wg
mounts=vkwg_conf
envlist=vkwg-env
root-dir=/usb1/turnable-wg
dns=8.8.8.8
start-on-boot=yes
restart-policy=on-failure
restart-interval=60s
logging=yes

RouterOS caches images by name:tag. When updating, use a NEW tag AND a NEW root-dir. WireGuard in a container needs the tun device and NET_ADMIN. If the platform blocks kernel WG, the built-in userspace wireguard-go handles it (slightly higher CPU).


Start

/container/start [find interface=veth-wg]


Check logs

/log print where topics~"container"

On startup the container prints the WireGuard client config. It's also saved to the mount:
/usb1/turnable-wg-confs/clients/client-wg.conf


Client config

The generated client-wg.conf is for a WireGuard-TURN Android client (with Session-ID aggregation), NOT for Xray-based clients. Before importing, fill in:

FieldWhat to set
VKLinkYour VK call link (https://vk.com/call/join/...)
public_ipYour VPS white IP (via config.json, else placeholder)
PeerTypeproxy_v2 (Session-ID aggregation — this server supports it)
StreamNumParallel streams (4 is a good start)
UseUDPClient-side only: TCP (off) is safer, UDP (on) is faster where allowed. Server is unchanged either way.

How it works

phone: WG-TURN client (multi-stream DTLS) -> VK TURN relay -> turnable-wg :56001 -> decrypt -> local WireGuard -> NAT -> internet

The client briefly joins the VK call to get TURN credentials, then rides the allocation. Session-ID aggregation packs several DTLS streams under one call presence — more speed at the antifraud profile of a single participant.


Bandwidth and antifraud

Session-ID aggregation groups N streams under ONE call presence: you get the throughput of N streams while the call still shows a single participant. This beats raw per-allocation scaling, where each extra stream is another "participant" and raises the antifraud signal. Tune StreamNum to taste — more streams, more speed, still one participant.


Credits

This image packages and wires existing open-source work for RouterOS — all protocol credit goes to the upstream authors:

Use the Android client above — it's the one that speaks the Session-ID protocol this server implements.


GitHub

Source and Dockerfile: coming soon

Tag summary

Content type

Image

Digest

sha256:a41375bb1

Size

11.8 MB

Last updated

13 days ago

docker pull ulfrasark/turnable-wg:v1