Skip to content

About

A bounded, loopback-only reverse TCP tunnel over authenticated carriers

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Backstitch

An outbound connection, stitched into a return path.

Backstitch exposes a fixed loopback TCP service on one machine through a loopback listener on another. The service-hosting machine initiates an authenticated, encrypted carrier such as an SSH remote command. Two Rust processes exchange framed TCP streams through that carrier's stdin/stdout.

protected machine                       hub

Backstitch agent -- outbound carrier --> Backstitch relay
       |                                       ^
       v                                       |
loopback service                       loopback TCP client

Version 0.2 adds per-stream flow control, heartbeats, idle deadlines, optional reconnection with bounded backoff, and clean SIGINT/SIGTERM handling. It is intended for authorized self-hosted deployments, not as a replacement for a VPN or as an independent security boundary. It has not undergone an external security audit.

Build

Rust 1.85 or newer is required. The toolchain file selects stable Rust and Cargo.lock pins dependencies.

cargo build --release --locked
cargo test --locked

Build a native binary for each machine's OS and architecture. A macOS binary does not run on Linux. Install binaries at a user-owned executable path, for example ~/.local/bin/backstitch. Both ends must use wire protocol v2; v0.1 peers are not compatible with v0.2.

SSH example

The service machine must already be able to authenticate to peer. Assume its local SSH service listens on 127.0.0.1:22 and both machines have Backstitch:

backstitch agent --target 127.0.0.1:22 --reconnect -- \
  ssh -T \
    -o BatchMode=yes \
    -o StrictHostKeyChecking=yes \
    -o ServerAliveInterval=15 \
    -o ServerAliveCountMax=3 \
    peer '~/.local/bin/backstitch relay --listen 127.0.0.1:2222'

On the peer:

ssh -p 2222 service-user@127.0.0.1

Independently verify the service machine's SSH host key before enrolling the new address. Its normal SSH key authentication still applies. A new connection creates a new login; attaching to an existing terminal requires a terminal multiplexer separately.

Hub access for other peers

The relay remains loopback-only. Other authorized peers reach it through the hub's authenticated SSH service, without exposing a new LAN listening port:

ssh -J hub-user@peer -p 2222 service-user@127.0.0.1

Verify both the hub's and the service machine's host keys. Use distinct SSH aliases and HostKeyAlias entries when several hubs expose different machines at the same loopback address. The hub must authorize these users and the relevant SSH jump operation. Do not change shared access policies just to make a route work.

HTTPS/WebSocket carriers

Backstitch does not implement HTTP, TLS, WebSocket, or SSH. It launches an argv-based carrier command without a local shell. Configure the carrier outside this repository. An existing TLS-verified WSS transport to a hub's SSH service can be used by SSH's ProxyCommand, then the ordinary remote command above:

agent -> verified WSS -> hub SSH -> remote-command relay

This uses permission to execute the relay; it does not request SSH reverse forwarding or a WebSocket server's reverse-tunnel operation. Use it only where this application-level return path is authorized. Backstitch does not change firewalls, proxy allowlists, or system security settings.

The carrier must provide confidentiality, authenticated peers, integrity, and an ordered duplex stream. Relay stdout is exclusively binary protocol; diagnostics use stderr. Do not allocate a PTY or allow banners on stdout. Never check in keys, tokens, private endpoints, or credential-bearing proxy commands.

Operating limits

Option Default Meaning
--max-connections 64 Maximum connections per session, 1..1024.
--connect-timeout 10 s Handshake and target connection deadline.
--heartbeat-interval 15 s PING cadence, even when TCP streams are idle.
--idle-timeout 60 s Carrier receive, write, and outbound-queue deadline. Must exceed heartbeat interval.
--reconnect off Agent retries after carrier failure.
--reconnect-min-delay 1 s First retry delay.
--reconnect-max-delay 30 s Exponential-backoff ceiling.

Retry backoff resets after a carrier attempt lasts at least 30 seconds. Interrupts also stop retry sleeps promptly. Reconnection creates a fresh session; existing TCP connections close and are not resumed.

Frames carry at most 16 KiB. Each stream direction has an eight-frame credit window. Credits return only after bytes reach the local TCP socket, so a stalled TCP destination does not block sibling dispatch or heartbeat processing. Queues and connection/connect tasks are bounded. All streams still share one ordered carrier, so loss or congestion in that transport can slow all of them.

Excess relay clients wait in the OS accept backlog; excess agent opens are rejected. Keep limits aligned across peers. Only literal loopback targets and listeners are accepted. The remote peer cannot select arbitrary destinations.

See OPERATIONS.md for supervision, health checks and recovery; PROTOCOL.md for framing; SECURITY.md for the trust model; and CONTRIBUTING.md for checks.

License

MIT. No hosted service or external account is required.

About

A bounded, loopback-only reverse TCP tunnel over authenticated carriers

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages