Skip to main content
Navigation

credential-types

SSH Keys

SSH keys in Vault

SSH keys are used for server access, git operations over SSH, and automated deployments. Vault stores the private key as an ssh-key credential and extracts structural metadata (algorithm, fingerprint, key size) automatically during storage.

Storing an SSH key

await vault.credentials.create({
  name: 'deploy-key-prod',
  type: 'ssh-key',
  value: `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAA...
-----END OPENSSH PRIVATE KEY-----`,
  metadata: {
    host: 'prod.example.com',
    username: 'deploy',
    environment: 'production',
    passphrase: 'optional-passphrase-here',
  },
  policy: {
    defaultLeaseTTL: '30m',
    maxLeaseTTL: '2h',
    maxLeases: 1, // Only one agent should deploy at a time
  },
});

The private key goes in the value field. If the key has a passphrase, store it in metadata. Both are encrypted with the same encryption envelope.

Validation

Vault validates SSH keys on storage:

  • The value must begin with a recognized PEM header (-----BEGIN OPENSSH PRIVATE KEY-----, -----BEGIN RSA PRIVATE KEY-----, or -----BEGIN EC PRIVATE KEY-----).
  • The key must parse successfully. Corrupted or truncated keys are rejected.
  • The algorithm is extracted and recorded: RSA, Ed25519, or ECDSA.

If validation fails, the credential is not stored and Vault returns a descriptive error indicating what went wrong.

Extracted metadata

When you store an SSH key, Vault automatically extracts and adds these fields to the credential’s metadata:

FieldDescriptionExample
algorithmKey algorithmed25519
fingerprintSHA-256 fingerprint of the public keySHA256:abc123...
keySizeKey size in bits (RSA only)4096
hasPassphraseWhether the key is passphrase-protectedtrue

These extracted fields are merged with any metadata you provide. Your metadata takes precedence if there are key conflicts.

Leasing SSH keys for deployment

A typical deployment workflow with Vault:

// Agent requests the deploy key with a short lease
const lease = await vault.leases.create({
  credentialName: 'deploy-key-prod',
  ttl: '15m',
  identity: {
    type: 'agent',
    name: 'claude-code',
    purpose: 'Deploying commit abc123 to production',
  },
});

// Write the key to a temporary file for ssh-agent
const tmpKeyPath = '/tmp/deploy-key';
await fs.writeFile(tmpKeyPath, lease.value, { mode: 0o600 });

// Use the key
await exec(`ssh -i ${tmpKeyPath} deploy@prod.example.com ./deploy.sh`);

// Clean up immediately; don't wait for lease expiry
await fs.unlink(tmpKeyPath);

Setting maxLeases: 1 on the credential policy ensures that only one agent can hold the deploy key at a time, preventing concurrent deployment conflicts.

Public key storage

Vault stores private keys. If you want to keep the corresponding public key for reference, add it as metadata:

metadata: {
  publicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... deploy@prod",
}

The public key is not secret, but storing it alongside the private key makes it easy to find the matching pair when setting up authorized_keys on a server.

Key rotation

SSH key rotation in Vault follows the same pattern as other credential types:

  1. Generate a new key pair.
  2. Add the new public key to the target server’s authorized_keys.
  3. Rotate the credential in Vault with the new private key.
  4. Existing leases continue using the old key until they expire.
  5. Remove the old public key from the server after all leases have expired.
await vault.credentials.rotate({
  name: 'deploy-key-prod',
  newValue: newPrivateKey,
  metadata: {
    publicKey: newPublicKey,
  },
});