MASQUE (HTTP3 CONNECT) client for MikroTik ROS v7.21+ container — transparent gw SOCKS5 proxy.
443
MASQUE (HTTP/3 CONNECT) client for MikroTik RouterOS containers — routes your LAN through the tunnel transparently, or serves a SOCKS5 proxy.
Based on Alpine Linux. Multi-arch: x86_64, ARM64, ARMv7. Pairs with
ulfrasark/masque-server over mutual TLS.
| Platform | Hardware |
|---|---|
linux/amd64 | CHR x86, x86 routers |
linux/arm64 | RB5009, hAP ax series, CCR2004 |
linux/arm/v7 | 32-bit ARM (hAP ac2 / ac3) |
The tag is a multi-arch manifest — RouterOS pulls the matching variant by image name.
| Variant | Compressed | Unpacked |
|---|---|---|
latest (gateway + SOCKS) | ~25 MB | ~65 MB |
socks (SOCKS only) | ~8 MB | ~20 MB |
LAN --> RouterOS (routing marks) --> veth --> container --> tunnel --> server
The container is a gateway: you point traffic at it, it carries that traffic over HTTP/3 CONNECT to the server. The server address itself stays reachable outside the tunnel automatically, so the tunnel never tries to carry its own packets.
Two modes, chosen automatically at startup:
| Mode | When | Result |
|---|---|---|
gateway | /dev/net/tun is available | transparent routing, clients need no config |
socks | no TUN available | SOCKS5 on 0.0.0.0:1080, clients set it up |
Pin one with MODE=gateway or MODE=socks if you don't want it decided for you.
/interface/veth add name=veth-masque address=172.20.0.2/24 gateway=172.20.0.1
/ip/address/add address=172.20.0.1/24 interface=veth-masque
/interface/list/member/add list=LAN interface=veth-masque
/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.20.0.0/24
The /ip/address/add line is separate on purpose: veth ... gateway= does not
create the router-side address. Without it, or without the masquerade rule, the
container starts but never reaches the server — the log shows
QUIC dial error: timeout while the server log stays empty.
/container/mounts/add name=masque-profile src=disk1/masque-client/config dst=/app/config
/container/mounts/add name=masque-dev src=/dev dst=/dev
| Mount | Purpose |
|---|---|
/app/config | the .masque profile issued by the server |
/dev | TUN access — required for transparent gateway mode |
Put the profile at disk1/masque-client/config/client.masque
(WinBox -> Files -> drag & drop). Any *.masque in that folder is picked up.
The profile needs
insecure_skip_verify = falseand filledca_pem/cert_pem/key_pem— the server enforces mutual TLS and rejects clients without a valid certificate.
All optional — defaults work.
/container/envs/add list=masque-client key=MODE value=auto
/container/envs/add list=masque-client key=TUN_MTU value=1280
/container/envs/add list=masque-client key=DNS_SERVER value=1.1.1.1
| Variable | Default | Description |
|---|---|---|
MODE | auto | auto / gateway / socks |
TUN_MTU | 1280 | tunnel MTU |
DNS_SERVER | 1.1.1.1 | DoH resolver used inside the tunnel |
MASQUE_SOCKS_PORT | 1080 | SOCKS port (relevant in socks mode) |
/container/config/set registry-url=https://registry-1.docker.io tmpdir=disk1/pull
/container/add \
remote-image=ulfrasark/masque-client:v1.1 \
interface=veth-masque \
envlist=masque-client \
mounts=masque-profile,masque-dev \
root-dir=disk1/masque-client/root \
dns=1.1.1.1 \
start-on-boot=yes \
logging=yes
Pin the version tag rather than
latest: RouterOS does not re-pull on its own, so withlatestyou keep running whatever was downloaded first.
/container/start [find remote-image~"masque-client"]
/log print where topics~"container"
A healthy start:
masque-client: version: v1.1 (arch aarch64)
masque-client: capabilities: sing-box=yes tun=yes ip_forward=yes
masque-client: mode: gateway
QUIC connected to your-ip-or-domain:4433
masque-client: SOCKS ready
inbound/tun[tun-in]: started at tun0
tun=no means this device or ROS build does not expose TUN — the container
comes up in socks mode instead. A failed first attempt is normal when the
container starts before the router's WAN is up; it retries with backoff rather
than exiting.
Start with a single host: easy to verify, easy to revert.
/routing/table/add name=via-masque fib
/ip/firewall/address-list/add list=local-nets address=192.168.0.0/16
/ip/firewall/address-list/add list=local-nets address=172.16.0.0/12
/ip/firewall/address-list/add list=local-nets address=10.0.0.0/8
/ip/firewall/mangle/add chain=prerouting action=accept \
dst-address=YOUR.SERVER.IP place-before=0 comment="masque server bypass"
/ip/firewall/mangle/add chain=prerouting action=accept \
dst-address-list=local-nets place-before=1 comment="keep LAN local"
/ip/firewall/mangle/add chain=prerouting action=mark-routing \
new-routing-mark=via-masque passthrough=no src-address=192.168.88.50
/ip/route/add dst-address=0.0.0.0/0 gateway=172.20.0.2 routing-table=via-masque
Whole LAN instead of one host:
/ip/firewall/mangle/add chain=prerouting action=mark-routing \
new-routing-mark=via-masque passthrough=no \
in-interface-list=LAN dst-address-list=!local-nets
The two
acceptrules must sit above themark-routingrule. If the server bypass ends up below it, the tunnel tries to carry its own traffic and nothing works. Check the order with/ip/firewall/mangle/print.
Handled inside the container over DoH — routed clients need no DNS setup, and the
router's own DNS stays untouched. Change the resolver with DNS_SERVER if you
prefer something other than 1.1.1.1.
curl https://2ip.io
curl -o /dev/null -w "%{size_download}\n" http://93.184.216.34
Do not test with
ping. The tunnel carries TCP only, so ICMP never traverses it: a reply means the packet went around the tunnel, andping some.hoststays silent even when DNS resolves fine — which looks like broken DNS while everything actually works.
None. The client only makes outbound connections — just don't block outbound UDP to the server port (default 4433).
socks mode clients point at 172.20.0.2:1080 themselves.Source and Dockerfile: coming soon
Content type
Image
Digest
sha256:3b00a254f…
Size
23.7 MB
Last updated
10 days ago
docker pull ulfrasark/masque-client