From d779a54e63195d4515a5505d7abc4314deb4aeb2 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 21 Jan 2026 13:36:19 +0100 Subject: [PATCH] add: guide on ssh-tresor for encrypting secrets with SSH agent Signed-off-by: Harald Hoyer --- content/2026-01-21-ssh-tresor.md | 166 +++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 content/2026-01-21-ssh-tresor.md diff --git a/content/2026-01-21-ssh-tresor.md b/content/2026-01-21-ssh-tresor.md new file mode 100644 index 0000000..f89c033 --- /dev/null +++ b/content/2026-01-21-ssh-tresor.md @@ -0,0 +1,166 @@ +# ssh-tresor: Encrypting Secrets with Nothing but Your SSH Agent + +*DISCLAIMER: This article and most of the code was written with claude code. Use at your own risk.* + +## The Problem + +If you work on remote servers, you've probably faced this dilemma: you need to store secrets—API keys, database +passwords, access tokens—but your options are limited. + +On a remote machine accessed via SSH, you typically have: + +- A shell +- Your forwarded SSH agent (`ssh -A`) +- Nothing else + +No password manager. No hardware token plugged in. No GPG key (and even if you had one, GPG agent forwarding is +notoriously painful). Just you and your SSH keys. + +### The Usual "Solutions" + +**Plain text files with restrictive permissions**: The classic `chmod 600 secrets.txt`. Quick, easy, and utterly +insecure. Anyone with root access, any leaked backup, any misconfigured service—your secrets are exposed. + +**Environment variables**: Slightly better, but they show up in `/proc`, get logged by accident, and persist in shell +history. + +**GPG encryption**: Powerful, but requires setting up GPG, managing a separate keyring, and dealing with GPG agent +forwarding if you want to use it remotely. The friction is real. + +**SOPS, age, or similar tools**: Good options, but they require you to have encryption keys on the remote machine—which +defeats the purpose if you're trying to avoid storing key material there. + +**Password managers with CLI tools**: Great locally, but integrating them over SSH typically means storing yet another +credential on the remote machine. + +The common thread? They all either store key material on the remote machine or require additional infrastructure beyond +what's already there. + +## The Realization + +I kept staring at my SSH agent. It was *right there*—holding my private keys, forwarded to the remote machine, ready to +sign anything I asked. SSH agents are designed to be secure: the private key never leaves the agent, even when +forwarded. + +What if I could use the SSH agent itself as my decryption mechanism? + +## Introducing ssh-tresor + +ssh-tresor encrypts secrets using keys held in your SSH agent. The private key never leaves the agent. There's no key +material stored on disk. If your SSH agent is forwarded, you can decrypt your secrets. If it's not, you can't. It's that +simple. + +### How It Works + +The core insight is that SSH agents can sign arbitrary data. ssh-tresor exploits this: + +1. **Encryption**: Generate a random challenge. Ask the SSH agent to sign it. Use the signature (via HKDF-SHA256) as key + material for AES-256-GCM encryption. + +2. **Decryption**: Present the same challenge to the agent. If the same key signs it, you get the same signature, derive + the same key, and decrypt successfully. + +The actual data encryption uses a master key with AES-256-GCM. Each SSH key gets its own "slot" that encrypts this +master key—similar to how LUKS handles multiple passphrases for disk encryption. + +### Key Features + +**No key material on disk**: Your private keys stay in the SSH agent. The encrypted file contains only challenges and +ciphertexts—nothing that reveals your key. + +**Works seamlessly over SSH**: If you have agent forwarding enabled (`ssh -A`), you can decrypt secrets on any remote +machine. No additional setup required. + +**Multi-key support (LUKS-style slots)**: Encrypt a secret for multiple SSH keys. Have a backup YubiKey? Add it. Use +different keys on different machines? Add them all. Rotate keys by adding a new one and removing the old. Each slot +independently encrypts the master key. + +**Armored output**: Optional base64 format with headers for easy embedding in config files or version control. + +**Simple CLI**: Follows Unix conventions—reads from stdin, writes to stdout, works in pipelines. + +## Practical Usage + +```bash +# List keys in your agent +ssh-tresor list-keys + +# Encrypt a secret (uses first available key) +echo -n "my-api-key-12345" | ssh-tresor encrypt -a > api-key.tresor + +# Decrypt it +ssh-tresor decrypt api-key.tresor + +# Encrypt for multiple keys (e.g., laptop key + YubiKey) +echo -n "my-api-key" | ssh-tresor encrypt -k SHA256:abc... -k SHA256:def... -o api-key.tresor + +# Add another key to an existing tresor +ssh-tresor add-key -k SHA256:newkey -i secret.tresor + +# Remove a key +ssh-tresor remove-key -k SHA256:oldkey -i secret.tresor +``` + +### Integration with Tools + +Many applications support fetching passwords from a command: + +```toml +# Email client config +server_password_command = "ssh-tresor decrypt ~/.config/mail/imap.tresor" +``` + +```json +// Claude Code API key +{ + "apiKeyHelper": "ssh-tresor decrypt ~/.config/claude/api-key.tresor" +} +``` + +The pattern works anywhere you can shell out for a password. + +## Security Properties + +- **AES-256-GCM** for authenticated encryption +- **HKDF-SHA256** for key derivation from signatures +- **Per-encryption random challenges**—replaying a captured tresor against a different agent instance won't help an + attacker +- **Security keys (FIDO2) supported**—they're tried last during decryption since they require user presence (touch) +- **Zeroization**—master keys and derived keys are zeroed from memory after use + +## When ssh-tresor Makes Sense + +- You regularly work on remote machines via SSH with agent forwarding +- You want to store secrets in dotfiles or git repos without them being readable if leaked +- You have multiple SSH keys (laptop, desktop, backup YubiKey) and want any of them to decrypt your secrets +- You want secrets protected by your SSH key without managing GPG or other encryption tools +- You use FIDO2/security keys and want hardware-bound encryption + +## When It Doesn't + +- You need to decrypt secrets when no SSH agent is available +- You need encryption that doesn't depend on any external service (the agent) +- You're working in an environment where SSH agent forwarding is prohibited + +## Installation + +```bash +# Via cargo +cargo install ssh-tresor + +# Via nix +nix run github:haraldh/ssh-tresor -- --help +``` + +## Conclusion + +ssh-tresor was born from a simple observation: on remote machines, the SSH agent is often the only secure key storage +available. By leveraging it for symmetric encryption, we get a secret management solution that requires no additional +infrastructure, no key material on disk, and works seamlessly wherever your SSH agent is forwarded. + +The SSH agent was always there. Now it can guard more than just your logins. + +--- + +*ssh-tresor is open source under MIT/Apache-2.0 license. Find it +at [github.com/haraldh/ssh-tresor](https://github.com/haraldh/ssh-tresor).*