+++
title = "ssh-tresor: Encrypting Secrets with Nothing but Your SSH Agent"
date = 2026-01-21
[taxonomies]
tags = [ "ssh", "encryption", "rust", "security" ]
+++

<!-- more -->

*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).*
