linbit-tunl quick reference
Customer: open a support
session
Support provides the exact command to run (they generated the session
ID on the relay). Example:
# Default: support can type in your shell; tunnel requests show a confirmation popup.
linbit-tunl swift-fox-jumps-barrel
# Watch-only: support cannot type in your shell or request a tunnel.
linbit-tunl --restricted swift-fox-jumps-barrel
# Pre-open tunnel at start (auto-grant, no popup; also works without tmux).
linbit-tunl --tunnel swift-fox-jumps-barrel
# With a pre-registration token (required when relay enforces token auth):
linbit-tunl swift-fox-jumps-barrel --token 482917
# Preserve session logs after exit (for troubleshooting):
linbit-tunl --debug swift-fox-jumps-barrel
Customer tmux layout (2
visible panes)
+----------------------------------------------------------------------------+
| |
| [SHELL] user@host:~# (customer's working shell, focused) |
| |
+----------------------------------------------------------------------------+
| [LINBIT SHARE] session-id * relay state | viewers | mode (pinned) |
| [optional second pinned row: support note / warning] |
+----------------------------------------------------------------------------+
| customer> _ (chat input; pinned status above) |
+----------------------------------------------------------------------------+
(plus one hidden background window for the daemon; never visible)
Hotkeys: Ctrl-b Q=end session (orderly shutdown with watchdog)
Ctrl-b c=toggle chat/shell F1 or Ctrl-b Enter=menu Ctrl-b z=zoom
Ctrl-b u=pre-grant tunnel / unblock Ctrl-b U=revoke tunnel
Ctrl-b X=block tunnel requests
on dead shell pane: Enter=restart q=end session
Ctrl-b L / C-l=reset layout
# Observe the session from another terminal (in addition to the above)
tmux -S /tmp/linbit-tunl-0f05bf25e11e/tmux.sock attach -r
Support: connect to a session
# Create a session -- relay generates the session ID and prints the customer command
tunl create-session --case CASE-4711 --customer "Acme Corp"
tunl create-session --case CASE-4711 # customer name optional
# Generate a one-time token for the customer (when relay requires it)
tunl expect
tunl expect --ttl 30 # custom TTL in minutes
# List active and pending sessions (pending = created by support, not yet connected)
tunl list
# Interactively pick a session and connect (includes pending sessions)
tunl pick
# Show details for one session
tunl show swift-fox-jumps-barrel
# Join the customer's session (shared tmux: window 0=main, 1=chat)
tunl join swift-fox-jumps-barrel
# Join read-only (watch but do not type)
tunl join --read-only swift-fox-jumps-barrel
# Join with debug logging (preserve session dir after exit)
tunl join --debug swift-fox-jumps-barrel
# SSH directly to customer (also available from join session menu: Ctrl-b Enter, s)
tunl ssh swift-fox-jumps-barrel
# Open customer-routed browser session (Chrome + SOCKS5; named profile)
# Also available from join session menu: Ctrl-b Enter, b
tunl browser swift-fox-jumps-barrel
tunl browser swift-fox-jumps-barrel --profile acme-prod
# Detach from join tmux -- session stays alive, customer stays connected, rejoin later
Ctrl-b d
# Close join session -- kills local tunl-join-* tmux; customer stays connected
Ctrl-b Q
Relay: operations
# Check relay-api is running
systemctl status linbit-tunl-api
# View active sessions directly (on the relay)
curl -s http://127.0.0.1:8080/api/v1/sessions | python3 -m json.tool
# View relay info (host key, API version)
curl -s http://127.0.0.1:8080/api/v1/info
# Manually remove a stale session
curl -X DELETE http://127.0.0.1:8080/api/v1/sessions/swift-fox-jumps-barrel
# Create a pre-registration token manually (on the relay)
curl -s -X POST http://127.0.0.1:8080/api/v1/expected \
-H 'Content-Type: application/json' \
-d '{"ttl_minutes": 60}'
# Revoke a token before it is used
curl -X DELETE http://127.0.0.1:8080/api/v1/expected/482917
# Add a support engineer's key (so they can use 'tunl join/ssh')
echo "ssh-ed25519 AAAA... engineer@linbit.com" >> /home/support/.ssh/authorized_keys
# Reload after config changes
systemctl reload ssh
Data flow (share
mode via relay-share.py broker)
Customer system Relay Support laptop
--------------- ----- --------------
linbit-tunl.py sshd ForceCommand: tunl join SID
tmux session relay-share.py: local tmux (2 windows)
main (shell, top) broker per session 0: main (VT stream)
chat (bottom, pinned status) fan-out VT stream 1: chat (with pinned
(hidden inner window) status header)
| | |
ssh (control-mode stream) -------> broker <---------------- ssh (consume)
|
SOCKS5 / tunnel: join menu: SSH / proxy / browser
ssh -R port:localhost:22 cssh / cscp / cproxy (proxy-env)
Session creation flow
Support relay-api Customer
------- --------- --------
tunl create-session
POST /api/v1/pending
<-- {id, customer_cmd}
print customer command
linbit-tunl swift-fox-...
(optional:) ssh tunl@relay "linbit-tunl/1 id=..."
tunl expect relay-share.py produce:
POST /api/v1/expected register session
<-- {token} spawn broker
share token --> customer bridge SSH <-> broker socket
customer side:
2-pane tmux session
(main + chat with pinned status)
control-mode -> broker
tunl join swift-fox-...
ssh support@relay
relay-share.py consume swift-fox-...
broker fans out VT stream
Token is consumed on first use. Reconnects (same session ID) bypass
token check automatically.
Four lowercase words in adjective-noun-verb-noun order,
hyphen-separated. Example: swift-fox-jumps-barrel.
~1.6x10^9 combinations (200^4) from four curated word lists of ~200
words each. Generated by the relay on tunl create-session;
support reads it to the customer over the phone or pastes it into a
ticket.
Key paths
/etc/linbit-tunl/support-keys.pub |
Support staff SSH public keys |
/etc/linbit-tunl/tunl-ca.pub |
Customer certificate CA public key |
/etc/linbit-tunl/tunl-host-ca.pub |
Relay host certificate CA public key |
/etc/linbit-tunl/tunl-principals |
Allowed certificate principals (tunl-customer) |
/var/lib/linbit-tunl/tunl.db |
SQLite database (sessions + uploads) |
/var/lib/linbit-tunl/share/ |
Broker socket directory (or /run/linbit-tunl/) |
/var/log/linbit-tunl/ |
Per-session audit logs and cast files |
/usr/local/lib/linbit-tunl/ |
Installed relay scripts |
/etc/ssh/sshd_config.d/tunl.conf |
sshd restrictions for tunl/support users |
/etc/pam.d/linbit-tunl |
PAM service for KI auth fallback |
Ports
| 443 |
Relay public ingress; nginx muxes by ClientHello: SSH bytes pass
through to the relay sshd (customer + support SSH), TLS goes to the
landing page and the public API |
| 80 |
Relay HTTP (redirects to 443; also used for ACME) |
| 8080 |
relay-api (localhost only on the relay host; reached via SSH forward
or via the public-API allowlist on nginx) |
| 30000-39999 |
Reverse tunnel ports (relay localhost, on-demand) |
| 1080 |
Local SOCKS5 proxy (support laptop, via tunl ssh or
join menu) |
Environment overrides --
linbit-tunl.py
TUNL_RELAY |
tunl.linbit.com |
Relay hostname |
TUNL_RELAY_PORT |
22 |
Relay SSH port (set to 443 against the production
relay, where nginx muxes 443 by ClientHello and forwards SSH bytes
through) |
TUNL_USER |
tunl |
Relay SSH username |
TUNL_API |
https://tunl.linbit.com |
Relay API URL (cert signing, host key) |
TUNL_AUTHKEYS_FILE |
~/.ssh/authorized_keys |
Where to install support keys (tunnel mode) |
TUNL_TARGET_HOST |
localhost |
Tunnel forward destination host |
TUNL_TARGET_PORT |
22 |
Tunnel forward destination port |
TUNL_RELAY_CA_PUBKEY |
(embedded) |
Relay host CA public key (for testing) |
TUNL_COMPRESS |
1 |
SSH compression (set 0 to disable) |
TUNL_CUSTOMER_COMPANY |
`` |
Organisation name; words > 2 chars become @mention
targets in chat |
TUNL_PREFIX |
C-b |
tmux prefix for the customer-side outer tmux (e.g. C-a,
M-Space) -- useful when the shell pane runs nested
tmux |
Environment overrides --
tunl.py
TUNL_RELAY |
tunl.linbit.com |
Relay hostname |
TUNL_RELAY_PORT |
22 |
Relay SSH port (set to 443 against the production
relay, where nginx muxes 443 by ClientHello and forwards SSH bytes
through) |
TUNL_SUPPORT_USER |
support |
Support SSH username on relay |
TUNL_API_PORT |
8080 |
relay-api port on relay localhost |
TUNL_SOCKS_PORT |
1080 |
Local SOCKS5 port for tunl ssh / join menu |
TUNL_SSH_LOG |
/tmp/tunl-ssh.log |
SSH debug log |
TUNL_DISPLAY_NAME |
$USER |
Display name shown to customer |
TUNL_SUPPORT_COMPANY |
linbit |
Company name; words become @mention targets in
chat |
TUNL_RELAY_HOST_CA_PUBKEY |
(embedded) |
Relay host CA public key (for testing) |
TUNL_PREFIX |
C-b |
tmux prefix for the join-session tmux (e.g. C-a,
M-Space) -- useful when running nested under your own
tmux |
Environment overrides --
relay-api
TUNL_DB |
/var/lib/linbit-tunl/tunl.db |
SQLite database path |
TUNL_SUPPORT_KEYS |
/etc/linbit-tunl/support-keys.pub |
Support keys file |
TUNL_HOST_KEY_FILE |
/etc/ssh/ssh_host_ed25519_key.pub |
Relay SSH host public key |
TUNL_API_BIND |
127.0.0.1 |
Listen address |
TUNL_API_PORT |
8080 |
Listen port |
TUNL_RELAY_HOSTNAME |
tunl.linbit.com |
Relay name in /api/v1/info |
TUNL_REQUIRE_PENDING |
1 (on) |
Require support to pre-register session ID |
TUNL_REQUIRE_TOKEN |
(off) |
Require 6-digit one-time token |
TUNL_TOKEN_TTL_MINUTES |
60 |
Token lifetime in minutes |
Environment overrides --
relay-share.py
TUNL_SHARE_DIR |
/run/linbit-tunl |
Broker socket directory |
TUNL_API |
http://127.0.0.1:8080 |
relay-api base URL |
TUNL_LOG |
/var/log/linbit-tunl |
Audit log directory |
SSH_ORIGINAL_COMMAND |
(sshd) |
Customer context string |
SSH_CONNECTION |
(sshd) |
Client IP info |