Files
ansiblings/packages/keyman
Benjamin Diedrichsen 764f890900 [keyman] keep the passphrase off argv, and recover a missing .pub
Generate prompted for the passphrase itself and passed it as `-N <value>`,
so it sat in this process's argv — readable by any user on the box through
`ps` for the length of the spawn — and in keyman's memory before that.
Verified that omitting `-N` makes ssh-keygen prompt *and* confirm, so the
prompt and the flag are both gone and the spawn inherits stdio. keyman no
longer learns the passphrase, which is strictly better than handling it more
carefully, and it deletes code.

The other half is the missing `.pub`. The selection list is built from
private keys, so an orphan is offered like any other, and copyFileSync
discovered the absent sibling only *after* age had written the encrypted
key: a vault entry with no public key, and an exception that took the rest
of the batch with it. It is now derived with `ssh-keygen -y -f`, before the
vault directory is created. Verified against real binaries that the derived
key matches the original byte for byte, that an encrypted key prompts (on
stderr — hence stdout piped, stdin and stderr inherited), and that a refused
derivation degrades to storing the private key alone rather than failing.

encrypt's loop now isolates per key and reports which ones did not make it,
except for ToolNotFoundError: age missing is not a per-key problem and nine
more identical errors help nobody.

storeInVault is the shared write path both callers had a copy of. It also
undoes its own mess: age has to write into a directory that already exists,
so a failure could leave an empty directory or a truncated .age — which list
counts as a vault entry and decrypt offers. The .age is removed because we
named it, the directory only while empty, since one holding an earlier key
is not ours to delete.
2026-07-30 15:01:22 +02:00
..
2026-07-27 13:09:00 +02:00

Keyman - SSH Key Management with Age Encryption

Keyman is a simple command line tool built around the age encryption tool. It allows you to manage SSH keys in public GitHub repositories securely by encrypting the private keys.

Features

  • 🔐 Encrypt SSH private keys with age encryption
  • 📁 Organized vault structure: vault/keys/ for encrypted keys, vault/tmp/ for decrypted keys
  • ⚙️ Configurable via .keymanrc.json with sensible defaults
  • 🔍 Interactive CLI for encrypting, decrypting, and listing keys
  • 🔄 Support for key rotation

Quick Start

1. Generate Age Encryption Key

# Create vault structure
mkdir -p vault/keys vault/tmp

# Generate age encryption key (keep this secret!)
age-keygen -o vault/age.key

# Add to .gitignore
echo "vault/age.key" >> .gitignore
echo "vault/tmp/" >> .gitignore

2. Generate SSH Keys

# Generate SSH key pair
ssh-keygen -t ed25519 -f vault/tmp/id_deploy -N "" -C "deploy@myapp.dev"

3. Run Keyman

# Run keyman interactively
VAULT_ROOT=./vault keyman

# Or if you have .keymanrc.json configured, just run:
keyman

Configuration

Keyman uses sensible defaults but can be customized via .keymanrc.json:

{
  "vaultRoot": "./vault",
  "keysDir": "keys",
  "tmpDir": "tmp",
  "ageKeyFile": "age.key"
}

Configuration Priority

  1. VAULT_ROOT environment variable (highest priority)
  2. .keymanrc.json file (searched from current directory upward)
  3. Default values (lowest priority)

Default Values

  • vaultRoot: "vault"
  • keysDir: "keys"
  • tmpDir: "tmp"
  • ageKeyFile: "age.key"

Vault Structure

project/
├── vault/
│   ├── age.key         # Master encryption key (NEVER commit!)
│   ├── keys/           # Encrypted keys (safe to commit)
│   │   └── deploy/     # Each key has its own folder
│   │       ├── id_deploy.pub        # Public key
│   │       └── id_deploy.age        # Encrypted private key
│   └── tmp/            # Decrypted keys (NEVER commit!)
│       ├── id_deploy       # Decrypted private key
│       └── id_deploy.pub   # Public key
└── .keymanrc.json      # Configuration (optional)

Operations

Keyman provides an interactive menu-driven interface with the following operations:

  • 📋 List keys - Compact view showing all keys with checkbox indicators for their locations
  • 🔒 Encrypt keys - Encrypt SSH keys from vault/tmp/ and store in vault/keys/
  • 🔓 Decrypt keys - Decrypt keys from vault/keys/ to vault/tmp/ or ~/.ssh/
  • Quit - Exit the program

After completing any operation, keyman automatically returns to the main menu, allowing you to perform multiple operations in a single session without restarting the tool.

List Keys Output

The list command shows a compact, unified view of all SSH keys with their locations:

🔑 SSH Keys:

  Key Name                      [Vault] [Tmp] [.ssh]
  ──────────────────────────────────────────────────────────
  ✅ id_deploy (.pub)              [✓]   [ ]  [✓]
  🔓 id_github (.pub)              [✓]   [✓]  [ ]
  🔒 id_backup (.pub)              [✓]   [ ]  [ ]
  ⚠️  id_local (.pub)               [ ]   [ ]  [✓]

  Legend:
  ✅ = Managed (encrypted in vault + active in .ssh)
  🔓 = Decrypted (in vault + decrypted to tmp)
  🔒 = Encrypted only (in vault, not decrypted)
  ⚠️  = Unmanaged (in .ssh or tmp, not encrypted in vault)

Features:

  • Public keys are indicated with (.pub) suffix instead of separate entries
  • Status emoji shows management state at a glance
  • Checkboxes [✓] show presence in three locations:
    • [Vault] - Encrypted in vault/keys/
    • [Tmp] - Decrypted in vault/tmp/
    • [.ssh] - Active in ~/.ssh/
  • Alphabetically sorted for easy scanning
  • New 🔓 status for keys decrypted to tmp but not yet in .ssh

Example Usage

# Using environment variable
VAULT_ROOT=../../vault keyman

# Using default configuration
keyman

# Keyman will show:
# 📁 Vault Root: /path/to/vault
# 🔑 Keys Directory: /path/to/vault/keys
# 📂 Temp Directory: /path/to/vault/tmp
# 🔐 Age Key: /path/to/vault/age.key

Best Practices

  1. Never commit vault/age.key or vault/tmp/ to version control
  2. Always backup your age.key securely (password manager, encrypted USB)
  3. Commit vault/keys/ - encrypted keys are safe to share
  4. Use environment variables for CI/CD: VAULT_ROOT=/path/to/vault keyman
  5. Keep .keymanrc.json in your project root for team consistency