How to Configure SSH Authorized Keys for Tailcat: Complete Guide
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 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 (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:
- GitHub users — Detected via the
@githubsuffix, validated againstgithubUsernameRx, then fetched via HTTPS usingfetchGitHubSSHKeys(lines 49-53) - File paths — Read via
readSSHAuthorizedKeysFile(lines 85-99) with a hard 1 MiB limit (maxSSHAuthorizedKeysSize) - 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
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:
cat > mykey.pub <<EOF
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO... user@example.com
EOF
Launch Tailcat with combined sources:
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_keysfile - The specific key stored in
mykey.pub
GitHub-Based Team Access
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— Flag definition and runtime constraint enforcement (lines 66-73, 104-106)cmd/tailcat/ssh_authorized_keys.go— Core parsing, fetching, reading, and validation logictailcat/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-keysflag 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.keysat 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.ServerConfigused 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 to prevent configuration confusion.
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 →