Authentication
tunnel-me answers three different access questions, and each has its own knob:
| Question | Answered by | Where it lives |
|---|---|---|
| Who may open the admin panel? | The server's --auth setting |
Server command line |
| Who may open a tunnel as a visitor? | The tunnel's sign-in setting | Panel → Agents → your tunnel → Authentication |
| Which machines may join a tunnel as agents? | Tunnel tokens | Panel → Agents → your tunnel → Tokens |
1. Who may open the admin panel
The panel (and the API behind it) is protected by the --auth flag on the server. All methods below protect the panel only — they never affect who can visit your tunnels.
static — the default, and it means off
tunnel-me serve --auth static
static performs no credential check. It exists for setups where something in front of the server does the authentication. Out of the box, anyone who can reach the server can open the panel.
Before exposing the port to the internet, switch to one of the methods below.
basic — username and password
tunnel-me serve \
--auth basic \
--auth.basic.username me \
--auth.basic.password s3cret
The browser shows its native login prompt. Defaults are admin/admin — change them. The browser resends the credentials on every request; there is no session to expire and no lockout on wrong tries.
file — many users from an htpasswd file
tunnel-me serve --auth file --auth.file.filename /etc/tunnel-me/panel.htpasswd
The file uses the standard htpasswd format — plain name:password-hash lines, inherited from the Apache web server and still the lingua franca of self-hosting (htpasswd -B /etc/tunnel-me/panel.htpasswd somebody adds a user). It is read once at startup — adding or removing users means restarting the server. If the file is missing, the server refuses to start.
oidc — sign in through your identity provider
tunnel-me serve \
--auth oidc \
--auth.oidc.server-url https://tunnels.example.com \
--auth.oidc.issuer-url https://idp.example.com/realms/main \
--auth.oidc.client-id tunnel-me-panel \
--auth.oidc.client-secret <secret>
- Register an application at your provider (Keycloak, Authelia, Google, …) with the redirect URI
https://tunnels.example.com/oauth2/callback— exactly your--auth.oidc.server-urlplus/oauth2/callback. --auth.oidc.server-urlmust be the panel's public address, or the provider will reject the redirect.- Anyone the provider signs in gets full admin access. Restrict the application's allowed users at the provider side.
- If TLS terminates in a proxy in front of tunnel-me, add
--auth.oidc.trust-proxy. - Signing in sets a session cookie valid for 7 days. Panel sessions are kept in memory: restarting the server signs everyone out of the panel.
https://tunnels.example.com/oauth2/logoutends the session.
webauth — let the proxy in front decide
tunnel-me serve --auth webauth --auth.web.header X-Panel-User
tunnel-me reads the named header and treats it as the signed-in user; a request without it gets rejected. This is for setups where the reverse proxy authenticates users (SSO plugins, forward auth) and injects the username.
Only safe when the server sits directly behind a proxy that authenticates and overwrites that header — otherwise a client could set the header itself and walk in. If your tunnels forward usernames to your apps, pick a header name that the proxy always overwrites (see the
X-Usernote below).
2. Who may visit a tunnel
Each tunnel picks its own sign-in rule in the panel: Public, OIDC, or Internal. A tunnel that belongs to you can safely serve the public internet only if you decide so — there is no global default.
Public
No sign-in. Anyone with the address is in. Good for things meant to be open (a public website, a share link).
OIDC — sign in with an identity provider
OIDC is the machinery behind every "Sign in with Google" button: you register tunnel-me at your provider once, and from then on visitors sign in with accounts they already have.
In tunnel-me, add the provider on the OIDC page: label, issuer URL, client ID, client secret, scopes (default openid, profile, groups, email). It needs a redirect URI pointing back at your server — the page gives you two ready-to-copy values:
*.tunnels.example.com/oauth2/callback— one redirect rule for all tunnels (requires a wildcard-capable provider setup);myapp.tunnels.example.com/oauth2/callback— for one specific tunnel.
Then, on the tunnel's Authentication tab, pick OIDC, choose the provider, and optionally restrict it to specific groups. With no groups selected, any authenticated user of that provider gets in.
When a visitor opens the tunnel, they are redirected to the provider's login page and returned to the exact URL they wanted.
Internal — built-in users and groups
No external services needed: you manage accounts yourself, right in the panel.
- Users (the Users page): a login, a password, a display name. Every account has an Active toggle — deactivated accounts cannot sign in anywhere.
- Groups (the Groups page): plain buckets of users.
- On the tunnel's Authentication tab, pick Internal and add the users and/or groups allowed to sign in. A group grants access to all of its members, present and future — handy for a "family" or "friends" group.
- No users and no groups selected means nobody can sign in. The panel warns you about it.
Wrong passwords do not go unpunished: after several failed tries the visitor's IP is blocked temporarily. The Security page shows currently blocked addresses, when each block expires, and an Unblock button for each.
The X-User header — telling your app who is visiting
On the tunnel's Authentication tab there is a Forward authenticated user switch (on by default). While it is on:
- a signed-in visitor reaches your app with their username in the
X-Userheader — your app can greet them or make decisions without implementing any login itself; - a
X-Userheader sent by the visitor is removed first, so nobody can fake an identity.
Turn it off if your app uses that header for its own purposes.
Visitor sessions
Signing in to a tunnel sets a cookie scoped to that tunnel's own address (myapp.tunnels.example.com) — every tunnel has its own, so being signed in to one tunnel never unlocks another. Sessions last 7 days.
Restart behavior differs by method:
- Internal (username/password) sessions live in memory — after a server restart visitors sign in again.
- OIDC visitor sessions are stored encrypted in the database — they survive restarts.
3. Which machines may connect as agents
Tunnels are joined by machines presenting a token. Tokens are created per tunnel (Agents → your tunnel → Tokens) and belong to that tunnel only.
- The full token is displayed exactly once at creation — copy it immediately. Later you only see a short hint to tell tokens apart, so give each a label saying what it is for (a machine, a deployment, an environment).
- Deleting a token immediately disconnects every machine signed in with it.
- A single token works for any number of machines and replicas — they all connect with it and the server spreads visitor requests across them (this is what makes auto-scaled agent fleets, e.g. Kubernetes sidecars, a non-event). Extra tokens are for finer control, not a requirement.