# How to Configure SSH Authorized Keys for Tailcat: Complete Guide

> Configure SSH authorized keys for Tailcat using the --ssh-authorized-keys flag. Learn to authenticate clients with literal keys, file paths, and GitHub users for secure access.

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

---

**Tailcat's SSH service authenticates clients using public keys specified via the `--ssh-authorized-keys` flag, which accepts comma-separated sources including literal keys, file paths, and GitHub users.**

This guide explains how to configure SSH authorized keys for the `tailscale/tailcat` repository. Whether you're deploying a single instance or managing multiple users, understanding the authorized key configuration ensures secure, key-based SSH access to your Tailcat server.

## What the `--ssh-authorized-keys` Flag Does

The `--ssh-authorized-keys` flag is defined in [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) at lines 104-106. It enables public-key authentication for Tailcat's built-in SSH service by accepting a comma-separated list of **authorized-key sources**.

The flag is **required** when launching the `ssh` service. The source code enforces this constraint at lines 66-73 of the same file—Tailcat will refuse to start the SSH service without it. Additionally, lines 69-71 prevent using `--ssh-authorized-keys` with the `no-auth-ssh` service, as these are mutually exclusive authentication modes.

## Supported Authorized Key Sources

Tailcat's parsing logic in [`cmd/tailcat/ssh_authorized_keys.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/ssh_authorized_keys.go) (lines 26-33) recognizes three source types:

| Source Type | Format | Description |
|-------------|--------|-------------|
| **Literal key** | `ssh-ed25519 AAAAC3Nza... user@host` | Inline OpenSSH public key string |
| **File path** | `/path/to/authorized_keys` | Path to a file containing one or more keys |
| **GitHub user** | `username@github` | Resolves to `https://github.com/username.keys` |

The `loadSSHAuthorizedKeys` function (lines 33-71) processes each comma-separated entry and dispatches to the appropriate loader based on the detected source type.

## How Tailcat Loads and Validates Keys

### Step 1: Source Detection and Fetching

For each entry in the comma-separated list, `loadSSHAuthorizedKeysFrom` performs the following:

1. **GitHub users** — Detected via the `@github` suffix, validated against `githubUsernameRx`, then fetched via HTTPS using `fetchGitHubSSHKeys` (lines 49-53)
2. **File paths** — Read via `readSSHAuthorizedKeysFile` (lines 85-99) with a hard **1 MiB limit** (`maxSSHAuthorizedKeysSize`)
3. **Literal keys** — Passed directly to validation

### Step 2: Validation

After all sources are resolved to key strings, the entire collection undergoes final validation through `tailcat.ValidateSSHAuthorizedKeys` (lines 63-70). This ensures only well-formed OpenSSH public keys are accepted by the SSH subsystem.

### Step 3: Runtime Binding

The validated keys are passed to the platform-specific SSH implementation (`tailcat_ssh_*.go` files), where they populate an `ssh.ServerConfig` that authorizes clients presenting matching private keys.

## Practical Configuration Examples

### Use a Local Authorized Keys File

```bash
tailcat serve --ssh-authorized-keys=$HOME/.ssh/authorized_keys ssh

```

This command starts Tailcat with SSH access limited to keys already trusted by your local system.

### Mix Multiple Source Types

Create a file with a literal key:

```bash
cat > mykey.pub <<EOF
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO... user@example.com
EOF

```

Launch Tailcat with combined sources:

```bash
tailcat serve \
  --ssh-authorized-keys=alice@github,$HOME/.ssh/authorized_keys,mykey.pub \
  ssh

```

This configuration accepts connections from:
- Any key in the GitHub user **alice's** public key list
- All keys in your local `~/.ssh/authorized_keys` file
- The specific key stored in `mykey.pub`

### GitHub-Based Team Access

```bash
tailcat serve \
  --ssh-authorized-keys=alice@github,bob@github,carol@github \
  ssh

```

Ideal for teams already using GitHub—no manual key distribution required.

## Key Implementation Files

Understanding these source files helps troubleshoot configuration issues:

- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)** — Flag definition and runtime constraint enforcement (lines 66-73, 104-106)
- **[`cmd/tailcat/ssh_authorized_keys.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/ssh_authorized_keys.go)** — Core parsing, fetching, reading, and validation logic
- **`tailcat/ssh_*.go`** — Platform-specific SSH server implementation consuming the loaded keys

## Common Configuration Pitfalls

| Issue | Cause | Solution |
|-------|-------|----------|
| "ssh service requires --ssh-authorized-keys" | Missing required flag | Add authorized key sources to your command |
| GitHub keys not loading | Invalid username format | Ensure format is exactly `username@github` |
| File too large error | Exceeds 1 MiB limit | Split keys across multiple files or use GitHub references |
| Flag conflicts with no-auth-ssh | Mutually exclusive modes | Choose either `--ssh-authorized-keys` or `no-auth-ssh`, not both |

## Summary

- The `--ssh-authorized-keys` flag is **mandatory** for Tailcat's SSH service and accepts comma-separated sources
- Three source types are supported: **literal keys**, **file paths**, and **GitHub users** (`user@github`)
- GitHub users are resolved to `https://github.com/user.keys` at runtime
- File sources are limited to **1 MiB** (`maxSSHAuthorizedKeysSize`)
- All keys undergo **double validation**: once per source, then collectively via `tailcat.ValidateSSHAuthorizedKeys`
- The loaded keys populate the `ssh.ServerConfig` used by the platform-specific SSH subsystem

## Frequently Asked Questions

### Can I use multiple authorized key files with Tailcat?

Yes. Pass multiple paths as comma-separated values to `--ssh-authorized-keys`. For example: `--ssh-authorized-keys=/etc/ssh/admin_keys,/etc/ssh/dev_keys`. Each file is read separately and subject to the 1 MiB size limit.

### How does Tailcat handle GitHub users who have multiple SSH keys?

Tailcat fetches all public keys returned by `https://github.com/username.keys` via the `fetchGitHubSSHKeys` function. Any of the returned keys can authenticate. This matches GitHub's own SSH authentication behavior.

### What happens if one source in my comma-separated list fails?

The `loadSSHAuthorizedKeysFrom` implementation processes each source independently. If a GitHub fetch fails or a file is unreadable, that specific source fails but the overall command may still succeed with remaining valid sources. Check server logs for per-source error details.

### Is there a way to disable SSH authentication entirely?

Yes. Use the `no-auth-ssh` service instead of `ssh`. However, **you cannot combine `no-auth-ssh` with `--ssh-authorized-keys`**—the source code explicitly blocks this at lines 69-71 of [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) to prevent configuration confusion.