Skip to content

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_keys file 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.

permitopen="127.0.0.1:5432",permitopen="127.0.0.1:8080" ssh-ed25519 AAAA... alice@tw

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 serve required
  • 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.