Skip to content

TCP and UDP services

Forward anything that isn’t TLS-with-SNI. The agent declares the listen port; the edge picks it up automatically.

Terminal window
TOWONEL_AGENT_TCP_SERVICES='[
{"name":"forgejo-ssh", "origin":"forgejo:22", "listen_port":2222},
{"name":"prom-write", "origin":"vm:8428", "listen_port":9090}
]'
Terminal window
TOWONEL_AGENT_UDP_SERVICES='[
{"name":"dns", "origin":"127.0.0.1:5353", "listen_port":5353},
{"name":"wireguard", "origin":"10.0.0.1:51820", "listen_port":51820, "idle_timeout_secs":120}
]'

Datagrams are framed onto the agent↔edge QUIC tunnel (length-prefixed, up to 64 KiB). The edge reaps a session after 60s of inactivity; set idle_timeout_secs per service (1–3600) for flows whose keepalives are further apart.

Use listen_port_range ([start, end], inclusive) instead of listen_port to claim a contiguous block. A datagram on any port in the range is forwarded to the origin host at the same port, so origin is host-only (no :port):

Terminal window
TOWONEL_AGENT_UDP_SERVICES='[
{"name":"relay", "origin":"10.0.0.5", "listen_port_range":[49160,49660], "idle_timeout_secs":600}
]'

The hub caps one range at TOWONEL_HUB_MAX_UDP_PORT_RANGE ports (default 512). Ranges aren’t available when TOWONEL_HUB_PORTS_REQUIRE_RESERVATION is on.

Expose the control port as a single service and the relay range as a port range:

Terminal window
TOWONEL_AGENT_UDP_SERVICES='[
{"name":"turn", "origin":"10.0.0.5:3478", "listen_port":3478, "idle_timeout_secs":300},
{"name":"turn-relay", "origin":"10.0.0.5", "listen_port_range":[49160,49660], "idle_timeout_secs":600}
]'

coturn advertises the edge’s public IP (it sits behind the tunnel) and pins its relay range to what you exposed:

listening-port=3478
min-port=49160
max-port=49660
external-ip=<edge public IP>
realm=turn.example.eu
  • coturn sees the agent as the client source (NAT-like) — fine for credential auth, but IP-based allow/deny won’t work.
  • STUN/XOR-MAPPED-ADDRESS answers reflect the agent’s socket, so server-reflexive (srflx) candidates are wrong — point clients at a separate STUN server. TURN relay candidates work correctly.
  • Keep idle_timeout_secs at or above coturn’s allocation lifetime (~300s+) so a quiet allocation isn’t reaped between refreshes.
  • TCP TURN (3478/tcp) rides the regular TCP service support.

Each agent boot reconciles the hub against the agent’s env: added entries are upserted, removed entries are deleted. The env is the source of truth — run at most one agent per tenant.

  • Each port is unique across all tenants. Claiming a port already bound to a different service is rejected at submission time.
  • TCP and UDP live in independent port namespaces — 2222/tcp and 2222/udp can coexist.
  • Privileged ports (< 1024) are blocked by default. Set TOWONEL_HUB_ALLOW_PRIVILEGED_PORTS=true on the hub to allow them.
  • A listen_port_range claims every port in the block; the privileged gate applies to the start and the whole range must be free.