No description
- Go 98.5%
- Makefile 0.9%
- Dockerfile 0.6%
| checker | ||
| plugin | ||
| .gitignore | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| NOTICE | ||
| README.md | ||
checker-stun-turn
happyDomain checker that probes STUN and TURN servers end-to-end:
DNS / SRV discovery, TCP/UDP reachability, TLS / DTLS handshake, STUN binding,
open-relay check, authenticated TURN Allocate (long-term creds or
REST API shared secret), relay address sanity, and a CreatePermission + Send
round-trip through the relay.
Backed by github.com/pion/stun +
github.com/pion/turn.
Tests performed per endpoint
| Test | What it proves |
|---|---|
dial:<transport> |
DNS resolves, port is reachable, TLS/DTLS handshake succeeds. |
tls / dtls |
Records TLS version + cipher + peer cert CN. |
stun_binding |
Server answers RFC 5389 Binding Request; measures RTT. |
stun_reflexive_public |
Server returned a public XOR-MAPPED address (not RFC1918). |
turn_open_relay_check |
Unauthenticated Allocate is rejected with 401 + REALM/NONCE. |
turn_allocate_auth |
Authenticated Allocate succeeds; relay address returned. |
turn_relay_public |
Relay address is publicly routable. |
turn_relay_echo |
CreatePermission + Send to the probe peer succeed. |
turn_channel_bind |
(optional) ChannelBind exercised via the relay conn. |
Most common failures surfaced with a fix
Each failing sub-test carries a Fix field that is rendered prominently in the
HTML report (yellow callout at the top of the card and inline with each row).
Mapping:
- UDP/TCP dial timeouts → firewall/NAT guidance
- TLS handshake errors → certificate reissue guidance (coturn
cert=/pkey=) - STUN binding returns RFC1918 →
external-ip=for coturn - Unauthenticated Allocate accepted → enable
lt-cred-mech, close the open relay - Allocate 401 loop → check NTP (TURN nonces are time-sensitive)
- Allocate 441 → wrong username/password or wrong REST shared secret
- Allocate 442 → try different transport or enable it server-side
- Allocate 486/508 → quota / port-range issues on the server
- Relay address is private → set
relay-ip=to a public IP - Relay echo fails →
min-port/max-portrange not publicly reachable
Rules
| Code | Description | Severity |
|---|---|---|
stun_turn.discovery |
Verifies that at least one STUN/TURN endpoint could be discovered (explicit URI or SRV lookup). | CRITICAL |
stun_turn.srv_stun |
Verifies that at least one STUN endpoint is reachable via SRV (_stun/_stuns) or an explicit URI. | WARNING |
stun_turn.srv_turn |
Verifies that at least one TURN endpoint is reachable via SRV (_turn/_turns) or an explicit URI. | CRITICAL |
stun_turn.dial |
Verifies that every discovered endpoint accepts a connection (TCP/TLS handshake or UDP socket). | CRITICAL |
stun_turn.tls_transport |
Verifies that at least one TLS/DTLS transport (stuns/turns) succeeds when present. | CRITICAL |
stun_turn.ipv6_coverage |
Verifies at least one STUN/TURN hostname resolves to an IPv6 address. | WARNING |
stun_turn.stun_binding |
Verifies that the STUN Binding request receives a XOR-MAPPED-ADDRESS reply. | CRITICAL |
stun_turn.reflexive_public |
Flags endpoints that return a private/loopback reflexive address (server unaware of its public IP). | CRITICAL |
stun_turn.stun_latency |
Compares the STUN Binding RTT against the configured warning/critical thresholds. | CRITICAL |
stun_turn.turn_open_relay |
Verifies the TURN server requires authentication (challenges unauthenticated Allocate with 401). | CRITICAL |
stun_turn.turn_auth |
Verifies the supplied TURN credentials (or REST shared secret) yield a successful Allocate. | CRITICAL |
stun_turn.relay_public |
Flags TURN servers whose allocated relay address is private/loopback (missing public relay-ip). | CRITICAL |
stun_turn.relay_echo |
Verifies the TURN relay path can carry traffic to the configured probe peer (CreatePermission + Send). | WARNING |
Usage
Build and run:
make # standalone binary
make plugin # .so plugin for happyDomain
./checker-stun-turn -listen :8080
Trigger a check:
curl -sX POST localhost:8080/collect -H 'content-type: application/json' -d '{
"options": {
"zone": "example.com",
"serverURI": "turns:turn.example.com:5349?transport=tcp",
"mode": "turn",
"username": "alice",
"credential": "s3cret",
"transports": "udp,tcp,tls",
"probePeer": "1.1.1.1:53",
"timeout": 5
}
}' | jq .
Options
| Scope | Option | Type | Default | Notes |
|---|---|---|---|---|
| run | zone |
string | (auto-filled) | used for _stun._udp / _turn._udp / _turns._tcp SRV discovery |
| run | serverURI |
string | explicit URI, RFC 7064/7065 | |
| run | mode |
choice | auto |
stun, turn, auto |
| user | username |
string | long-term credentials | |
| user | credential |
secret | long-term credentials | |
| user | sharedSecret |
secret | REST-API auth (draft-uberti), takes precedence | |
| user | realm |
string | optional explicit realm | |
| user | transports |
string | udp,tcp,tls |
comma-separated among udp,tcp,tls,dtls |
| user | probePeer |
string | 1.1.1.1:53 |
target for the relay echo test |
| user | testChannelBind |
bool | false |
|
| user | warningRTT |
uint | 200 ms |
|
| user | criticalRTT |
uint | 1000 ms |
|
| user | timeout |
uint | 5 s |
per-probe |
License
MIT (see LICENSE).