# How to Enable Auth-Free SSH with Tailcat: A Complete Guide

> Learn how to enable auth free SSH with Tailcat. Securely access your devices using Tailcat's encrypted tunnel for client identity instead of passwords or keys. Get the complete guide.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: how-to-guide
- Published: 2026-09-06

---

**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:

1. **WireGuard encryption** – All traffic is encrypted and authenticated at the tunnel layer
2. **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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/ssh.go))

The core SSH logic resides in [`cmd/tailcat/ssh.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/ssh.go). Lines 206-209 configure the *none* authentication method when the service name matches `no-auth-ssh`:

```go
// 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`](https://github.com/tailscale/tailcat/blob/main/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:

```bash
$ 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:

```bash
$ 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 `--`:

```bash

# 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

```bash
$ 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

```bash
$ 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`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) warning print |
| Unauthorized access | Use `--allow` with specific node keys | Tunnel layer ACL in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) |
| Scope of access | Use forced commands (`-- command`) | [`ssh.go`](https://github.com/tailscale/tailcat/blob/main/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

```bash

# 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

```bash

# 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

```bash

# 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-ssh` to start the server; the printed `tc...` 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`](https://github.com/tailscale/tailcat/blob/main/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`](https://github.com/tailscale/tailcat/blob/main/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:

```bash
$ tailcat serve no-auth-ssh -- /usr/bin/rsync --server

```

This pattern, shown in [`cmd/tailcat/ssh.go`](https://github.com/tailscale/tailcat/blob/main/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.