• August 29, 2025
  • Written by: naxslabs
  • No Comments

Securely controlling SSH access to your servers is critical. HashiCorp Vault can act as a central SSH Certificate Authority (CA), allowing you to issue signed user keys, enforce TTLs, and avoid distributing static SSH keys.


Prerequisites / Requirements

  • HashiCorp Vault
  • Linux Virtual Machine

Step 1: Enable the SSH Secrets Engine

Enable the SSH engine at a chosen mount point:

vault secrets enable -path=ssh ssh
  • The ssh mount point manages Vault’s SSH CA and key signing.
  • Vault will generate its own internal SSH CA key for signing user keys.

Step 2: Generate the Vault SSH CA Key

vault write ssh/config/ca generate_signing_key=true
  • Vault now holds an internal SSH CA key.
  • The private key stays in Vault, ensuring secure signing.

Step 3: Extract the SSH CA Public Key

vault read -field=public_key ssh/config/ca > vault-ca.pub
  • This single vault-ca.pub file is distributed to your VMs.
  • It allows your servers to verify certificates signed by Vault.

The last command outputs a public key that should go in the /etc/ssh directory of your Linux virtual machine. However, it wouldn’t be very convenient to load it manually on every device. I handle this through a snippet on the Proxmox hypervisor.

You can create a YAML config file and place it in the /var/lib/vz/snippets directory of your Proxmox host. If you already have a cloud image template, you can attach a snippet, and the public key will be loaded into each virtual machine at boot time.

Example Snippet

# user-data.yaml

users:
  - name: user
    passwd: mkpasswd --method=SHA-256 password
    lock_passwd: false
    ssh_authorized_keys:
      - Paste SSH Public Key
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash

  - name: ansible
    ssh_authorized_keys:
      - Paste SSH Public Key
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash

package_update: true
package_upgrade: true

write_files:
  - path: /etc/ssh/vault-ca.pub
    content: |
      Paste Public Key here
    permissions: '0644'

bootcmd:
  - echo "TrustedUserCAKeys /etc/ssh/vault-ca.pub" >> /etc/ssh/sshd_config

runcmd:
  - systemctl restart ssh

Assuming your cloud image template has an ID of 1000:

qm set 1000 --cicustom "user=local:snippets/user-data.yaml"

Note: This method doesn’t mesh well with Cloud-Init in the GUI. Either use snippets to configure your cloud images, or use the GUI and leave additional configurations to Ansible and other configuration tools.

Step 4: Create a Role, Generate a Key Pair, and Sign the Key

Define an SSH role in Vault. This role controls which users can request signed certificates and sets the TTL (time-to-live) for the certificates:

Example:

vault write ssh/roles/dev-role \
key_type=ca \
allow_user_certificates=true \
allowed_users=”ansible,darnell” \
default_user=”ansible” \
ttl=”1h” \
max_ttl=”24h”

Generate an SSH Key Pair and Sign the Key

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_demo

Sign the key for the ansible user:

vault write -field=signed_key ssh/sign/dev-role \
public_key=@/home/darnell/.ssh/id_ed25519_demo.pub \
username=ansible \
> /home/darnell/.ssh/id_ed25519_demo-cert.pub

Set the permissions:
chmod 600 ~/.ssh/id_ed25519_demo
chmod 644 ~/.ssh/id_ed25519_demo-cert.pub

Test SSH access using the signed certificate(s):

ssh -i ~/.ssh/id_ed25519_demo -i ~/.ssh/id_ed25519_demo-cert.pub ansible@IP/Hostname

Leave a Reply

Your email address will not be published. Required fields are marked *