Access Control¶
Tunnel Whisperer enforces access control at two independent layers: the transport layer (Xray UUID) and the session layer (SSH public key with port restrictions). Revoking access at either layer is sufficient to block a user.
Transport-Layer Auth¶
Each user is assigned a unique Xray UUID during the tw create user wizard. This UUID is registered in the relay's Xray configuration file (/usr/local/etc/xray/config.json).
- The relay accepts only connections authenticated with a registered UUID
- Each UUID maps to a single user, enabling individual revocation at the transport layer
- Removing a UUID from the relay config immediately prevents that user from establishing a tunnel
{
"inbounds": [{
"settings": {
"clients": [
{ "id": "server-uuid-here" },
{ "id": "alice-uuid-here" },
{ "id": "bob-uuid-here" }
]
}
}]
}
SSH Public Key Auth¶
All SSH authentication uses Ed25519 key pairs. Password authentication is disabled entirely.
- Each user receives an individual key pair generated by
tw create user - The server maintains an
authorized_keysfile with one entry per user - The embedded SSH server (Go
x/crypto/ssh) validates the client's public key against this file on every connection attempt
No password fallback
The SSH server does not support password authentication under any circumstances. If a client loses their private key, a new key pair must be generated and the old public key removed from authorized_keys.
Per-User Port Restrictions¶
The permitopen directive in authorized_keys limits each user to forwarding traffic only to specific 127.0.0.1:<port> targets. Users cannot access the server's wider network or forward to arbitrary ports.
In this example, user alice can forward to:
127.0.0.1:5432(e.g., PostgreSQL)127.0.0.1:8080(e.g., a web application)
Any attempt to forward to a port not listed in permitopen is rejected by the SSH server.
Localhost only
The remote host in port forwarding is locked to 127.0.0.1. Users cannot specify external hosts — all forwarded traffic targets services running on the server machine itself.
Dynamic Authorization¶
The embedded SSH server re-reads authorized_keys on every authentication attempt. This means:
- Adding a new user takes effect immediately — no restart of
tw serverequired - Revoking a user's key takes effect on the next connection attempt
- There is no cached state that could allow a revoked key to authenticate
This design ensures that access control changes are applied in real time, without service interruption.
User Revocation¶
To fully revoke a user's access, two actions are required:
Step 1 — Remove the SSH public key
Remove the user's entry from the server's authorized_keys file. This blocks SSH authentication on the next connection attempt.
Step 2 — Remove the Xray UUID from the relay
Remove the user's UUID from the relay's Xray configuration (/usr/local/etc/xray/config.json) and restart Xray on the relay. This blocks the user at the transport layer.
Both steps are recommended
Removing either the SSH key or the UUID is sufficient to block access. However, performing both steps ensures defense-in-depth — the user is blocked at two independent layers.
Revocation takes effect on the next connection attempt. Existing active sessions are not terminated immediately; they will fail on reconnection.
Compliance Properties¶
Tunnel Whisperer's access control model supports the following compliance-relevant properties:
Zero plaintext data leaves the local network¶
All application data is encrypted by the SSH layer before it reaches the Xray tunnel. The TLS layer provides an additional envelope. No plaintext data is ever transmitted over the public internet.
No credentials stored on relay¶
The relay stores only Xray UUIDs (transport identifiers). It does not store SSH private keys, user passwords, or application data. Compromise of the relay does not expose user credentials or data.
Principle of least privilege¶
Each user can only forward traffic to the specific 127.0.0.1:<port> targets explicitly listed in their permitopen directives. There is no broad network access — every user is scoped to the minimum required services.
Audit trail via configurable logging¶
Tunnel Whisperer uses Go's slog structured logging framework. Log levels are configurable at runtime, and log output captures:
- SSH authentication attempts (success and failure)
- Port forwarding requests (allowed and denied)
- Tunnel establishment and disconnection events
- Xray connection lifecycle events
Log level configuration
The log level can be changed via the dashboard settings. Changing the log level triggers a server restart notification to ensure the new level takes effect.