How to Enable Auth-Free SSH with Tailcat: A Complete Guide
Use tailcat serve no-auth-ssh to start an SSH server that relies on the encrypted Tailcat tunnel for client identity instead of passwords or keys.
Tailcat, an experimental WireGuard-based networking tool from Tailscale, can run an SSH service that does not require client authentication. The encrypted Tailcat tunnel itself identifies the client, so the server trusts any peer that presents the correct Tailcat address. This "auth-free" mode is useful for quick ad-hoc shells or for environments where the tunnel address is exchanged over a private channel.
How Auth-Free SSH Works in Tailcat
Tailcat's auth-free SSH implementation leverages tunnel-provided identity rather than traditional SSH credentials. When you enable this feature, the server skips public-key checks entirely and relies on two security properties:
- WireGuard encryption – All traffic is encrypted and authenticated at the tunnel layer
- Address-as-secret – The Tailcat address embeds the server's WireGuard public key and acts as a bearer token
According to the tailscale/tailcat source code, the implementation spans three main components.
CLI Flag Parsing (cmd/tailcat/tailcat.go)
The main entry point parses the --serve flag. When the service name is no-auth-ssh, Tailcat selects the auth-free SSH server and prints a security warning. In cmd/tailcat/tailcat.go lines 94-106, the flag handling routes to the appropriate service implementation. The validation logic at lines 1246-1513 ensures no-auth-ssh cannot be combined with the regular ssh service and emits: "WARNING: no-auth-ssh gives a shell to anyone with this address; keep it secret (never in a DNS TXT record) or restrict clients with --allow".
SSH Server Implementation (cmd/tailcat/ssh.go)
The core SSH logic resides in cmd/tailcat/ssh.go. Lines 206-209 configure the none authentication method when the service name matches no-auth-ssh:
// From ssh.go - the none auth method bypasses public-key checks
authMethods: []ssh.AuthMethod{
// No public-key authentication required
},
This explicitly removes all SSH-level authentication while still offering a shell or forced command.
End-to-End Tests (cmd/tailcat/ssh_e2e_test.go)
The test suite verifies that a no-auth-ssh server starts correctly, that the warning appears on stderr, and that the server accepts connections without any client keys present.
Starting an Auth-Free SSH Server
Basic Server Setup
Run the following on your target machine:
$ tailcat serve no-auth-ssh
# 🐈 Server listening with new address: tc1a2b3c4d5e...
# ⚠️ WARNING: no-auth-ssh gives a shell to anyone with this address; keep it secret (never in a DNS TXT record) or restrict clients with --allow
The server generates a new Tailcat address starting with tc.... Treat this address as a secret — anyone who knows it can obtain a shell.
Connecting from a Client
From any machine with Tailcat installed:
$ tailcat ssh tc1a2b3c4d5e...
You are dropped into an interactive shell immediately. No password prompt. No key exchange. The tunnel authentication happens transparently.
Running Forced Commands
You can restrict what clients execute by supplying a command after --:
# Server: only allow git upload-pack
$ tailcat serve no-auth-ssh -- git-upload-pack /srv/repo.git
# Client: clone via the restricted SSH service
$ tailcat git clone tc1a2b3c4d5e...:repo.git
The command runs in the context of the shell on the server. Because authentication is disabled, any client that knows the address can run the forced command.
Restricting Access with --allow
Even in auth-free mode, you can limit which client node keys may connect. This adds a layer of control while keeping the SSH service "auth-free" from the perspective of SSH-level credentials.
Step 1: Generate a Client Key
$ tailcat genkey --client --key=myclient
This creates a persistent client identity. Extract the node key for the server configuration.
Step 2: Start Server with Allowed Keys
$ tailcat serve \
--allow=nodekey:$(tailcat genkey --client --key=myclient | grep nodekey | awk '{print $2}') \
no-auth-ssh
Only clients possessing the matching node key can use the address. Others are rejected at the tunnel layer before reaching the SSH service.
Security Considerations for Auth-Free SSH
| Risk | Mitigation | Implementation Location |
|---|---|---|
| Address leakage | Treat as bearer token; never publish to DNS | tailcat.go warning print |
| Unauthorized access | Use --allow with specific node keys |
Tunnel layer ACL in tailcat.go |
| Scope of access | Use forced commands (-- command) |
ssh.go command execution |
| Audit trail | Tailcat logs tunnel connections | WireGuard handshake logging |
The source code explicitly warns against placing the address in DNS TXT records or other public channels. The tunnel address acts as a capability — possession grants access.
Complete Configuration Examples
Example 1: Quick Ephemeral Shell
# Server (any Linux/macOS machine)
$ tailcat serve no-auth-ssh
# 🐈 Server listening with new address: tcAbCdEfGh...
# Client (your laptop)
$ tailcat ssh tcAbCdEfGh...
$ hostname # runs on server
server-hostname
Example 2: Git Server with No SSH Keys
# Server: expose a git repository
$ tailcat serve no-auth-ssh -- git-upload-pack /srv/myproject.git
# Client: clone without SSH key setup
$ tailcat git clone tcXyZ12...:myproject.git
Example 3: Restricted Development Environment
# Client: create persistent identity
$ tailcat genkey --client --key=developer1
# Server: allow only specific developers
$ tailcat serve \
--allow=nodekey:abc123... \
--allow=nodekey:def456... \
no-auth-ssh
# Only developer1 and developer2 can connect
Summary
- Auth-free SSH with Tailcat eliminates SSH key management by relying on WireGuard tunnel identity
- Use
tailcat serve no-auth-sshto start the server; the printedtc...address is your only credential - Never expose the Tailcat address publicly — treat it as a secret bearer token
- Add
--allow=nodekey:<key>to restrict which clients can connect at the tunnel layer - Forced commands after
--limit execution scope even without authentication
Frequently Asked Questions
What is the difference between ssh and no-auth-ssh in Tailcat?
The standard ssh service requires client public-key authentication against authorized_keys, while no-auth-ssh permits only the none authentication method. In cmd/tailcat/ssh.go, the service name determines which authMethods slice is initialized. The no-auth-ssh variant is designed for ephemeral access where key distribution is impractical.
Is auth-free SSH with Tailcat secure?
Security depends on address confidentiality. The Tailcat address embeds the server's WireGuard public key and acts as a capability token. The tunnel itself provides encryption and anti-replay protection. According to the source code in cmd/tailcat/tailcat.go, the implementation warns operators that "the address is the sole credential" and recommends using --allow for additional restrictions.
Can I combine auth-free SSH with forced commands?
Yes. Append -- followed by your command when starting the server:
$ tailcat serve no-auth-ssh -- /usr/bin/rsync --server
This pattern, shown in cmd/tailcat/ssh.go, executes the specified command instead of an interactive shell. Clients still connect without authentication, but can only run the forced command.
How do I revoke access to an auth-free SSH server?
Since there are no SSH keys to remove, you have two options: rotate the server address by restarting tailcat serve no-auth-ssh (generates a new tc... address), or restart with stricter --allow rules to exclude specific client node keys. The source code does not implement dynamic revocation; access control is established at server startup.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →