udpduct is an SSH-authenticated UDP forwarding tool with ssh-style -L and -R semantics.
It uses the system ssh client to authenticate to the remote machine and start a hidden helper there, then switches the data plane to a direct encrypted UDP tunnel. The intent is closer to "UDP port forwarding with an SSH login path" than "UDP tunneled inside SSH/TCP".
This is an early implementation. The core pieces exist:
sshbootstrap and hidden remoteagentmode- direct encrypted UDP tunnel with replay protection
-Land-Rforwarding rules- per-flow UDP socket handling for request/reply traffic
- keepalives and idle flow cleanup
Current limitations:
- the remote host must already have
udpductinstalled and inPATH, or you must pass--remote-path - the client must be able to reach the remote host over UDP after SSH bootstrap succeeds
- no roaming, resume, multicast, broadcast, fragmentation/reassembly, or source-IP spoofing
- no live smoke-test coverage against a real SSH server yet
Build from source with Cargo:
cargo build --releaseInstall locally:
cargo install --path .The same binary must be available on both the local and remote machine.
Local forwarding, equivalent in shape to ssh -L:
udpduct user@example.com -L 5353:127.0.0.1:53This binds UDP port 5353 locally and forwards traffic to 127.0.0.1:53 on the remote side.
Remote forwarding, equivalent in shape to ssh -R:
udpduct user@example.com -R 5353:127.0.0.1:53This binds UDP port 5353 on the remote side and forwards traffic to 127.0.0.1:53 on the local side.
Multiple rules are allowed:
udpduct user@example.com \
-L 5000:127.0.0.1:5000 \
-R 6000:127.0.0.1:6000Useful options:
-p,-i,-J,-o,-F,-l: passed through to the systemsshclient-4/-6: constrain address family--remote-path: remote binary path, defaults toudpduct--udp-host: override which hostname/IP the client uses for the UDP data-plane peer--udp-port: request a specific remote UDP tunnel port--udp-port-range: constrain remote UDP tunnel allocation--keepalive: tunnel heartbeat interval, default15s--idle-timeout: per-flow idle expiry, default60s--max-dgram: maximum forwarded datagram size, default1200
Show full CLI help:
udpduct --help- The local client parses forwarding rules and binds any local listeners needed for
-L. - It starts
ssh -T <destination> 'udpduct agent --stdio'. - Client and agent exchange a bootstrap message over stdio that includes the forwarding rules and a session secret.
- The remote agent binds any remote listeners needed for
-Rplus a UDP tunnel socket. - The client connects to that UDP socket, performs an authenticated handshake, and then all forwarded datagrams move over the encrypted UDP tunnel.
Each observed UDP source behind a forwarding rule becomes an internal flow, so replies can be routed back to the original sender.
- SSH handles remote authentication and process startup.
- Tunnel traffic is encrypted with
ChaCha20-Poly1305. - Directional keys are derived from a per-session secret via HKDF-SHA256.
- Tunnel packets carry sequence numbers and a replay window.
This does not inherit OpenSSH's built-in TCP forwarding policy controls, because forwarding is implemented by a remote program launched after login rather than by the SSH server itself.
Run tests:
cargo testFormat the code:
cargo fmtLicensed under either of these, at your option:
- Apache License, Version 2.0, in LICENSE-APACHE
- MIT license, in LICENSE-MIT