Warning: This post describes a non-production setup of Vault. As such, it is not hardened with appropriate security measures, and it is not recommended to use this setup in production. You should use this for learning purposes on SSH CAs and Vault.
SSH certificates are an effective way to secure SSH server access. They can restrict users and the commands they can run, making them especially valuable for managing access to multiple servers. By using SSH certificates, server fingerprint validation becomes unnecessary since the certificates are signed by a Certificate Authority (CA) with the CA’s public key installed on the server. Vault is an excellent tool for managing SSH certificates, offering functionalities like issuing and revoking certificates, managing SSH keys, and providing audit logs.
Install Vault
To quickly set up a development Vault server, use the official Docker image with the following command:
docker run --cap-add=IPC_LOCK -e 'VAULT_LOCAL_CONFIG={"storage": {"file": {"path": "/vault/file"}}, "listener": [{"tcp": { "address": "0.0.0.0:8200", "tls_disable": true}}], "default_lease_ttl": "168h", "max_lease_ttl": "720h", "ui": true}' -p 8200:8200 hashicorp/vault server
Configure SSH Certificate Authority
With Vault installed and running, configure it to issue SSH certificates using the SSH secrets engine.
First, generate an SSH key pair (private and public keys) to act as the SSH Certificate Authority (CA) for Vault using the ssh-keygen command:
ssh-keygen -t rsa -b 4096 -f ssh_ca_key -C "Vault SSH CA"
You will now have two files: the private key ssh_ca_key
and the public key ssh_ca_key.pub
. Vault will use the private key to sign the SSH certificates, while clients will use the public key to verify the SSH certificates.
Enable and configure the SSH secrets engine to use the generated public key as the CA:
- Log in to Vault with
vault login <initial_root_token>
. - Enable the SSH secrets engine with
vault secrets enable ssh
. - Configure the SSH secrets engine to use the generated keys:
vault write ssh/config/ca \
private_key=@ssh_ca_key \
public_key=@ssh_ca_key.pub
Create a role called ops-team
to issue SSH certificates. This role allows any user with access to request an SSH certificate. The example below grants broad permissions, including any option for allowed_users
and port forwarding. Be sure to restrict these permissions based on your use case.
vault write ssh/roles/ops-team \
key_type=ca \
ttl=2h \
max_ttl=24h \
allow_user_certificates=true \
allowed_users="*" \
default_extensions='permit-pty,permit-port-forwarding,permit-agent-forwarding'
Configure the remote server to accept the SSH certificates issued by Vault:
- Copy the CA public key to the remote server:
scp ssh_ca_key.pub <username>@<target_server_ip>:/tmp/ssh_ca_key.pub
- Add the public key to the OpenSSH configuration and restart the OpenSSH daemon:
echo "TrustedUserCAKeys /etc/ssh/user_ca.pub" | sudo tee -a /etc/ssh/sshd_config
sudo cp /tmp/ssh_ca_key.pub /etc/ssh/user_ca.pub
sudo systemctl restart sshd
Requesting SSH Certificates
To request an SSH certificate from Vault and use it to SSH into the remote server, follow these steps:
- Use the
ops-team
role to request the certificate and pass your local SSH keyid_rsa.pub
. Also, specify the username to use when connecting to the remote server:
vault write -field=signed_key ssh/sign/ops-team \
public_key=@$HOME/.ssh/id_rsa.pub \
valid_principals="<username>" > signed_id_rsa-cert.pub
- Use the
signed_id_rsa-cert.pub
file to SSH into the remote server:
ssh -i signed_id_rsa-cert.pub -i $HOME/.ssh/id_rsa <username>@<target_server_ip>
Requesting a signed certificate manually each time can be tedious. To simplify this process, create a script called vault-ssh.sh
and make it executable with chmod +x vault-ssh.sh
.
#!/bin/bash
# Configuration
VAULT_ADDR="http://<vault_server_ip>:8200"
VAULT_ROLE="ops-team"
USERNAME="<username>"
PUBLIC_KEY_PATH="$HOME/.ssh/id_rsa.pub"
CERT_PATH="$HOME/.ssh/id_rsa-cert.pub"
CONFIG_FILE="path/to/vault-creds.conf"
# Read the Vault token from the configuration file
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
else
echo "Error: Vault configuration file not found"
exit 1
fi
# Check if the VAULT_TOKEN variable is set
if [ -z "$VAULT_TOKEN" ]; then
echo "Error: VAULT_TOKEN is not set in the configuration file"
exit 1
fi
# Generate a new SSH certificate
vault write -field=signed_key ssh/sign/$VAULT_ROLE \
public_key=@$PUBLIC_KEY_PATH \
valid_principals="$USERNAME" > $CERT_PATH
This script requires a vault-creds.conf
file containing the Vault token:
VAULT_TOKEN=<vault_token>
To integrate the certificate generation process with your SSH config, use the ProxyCommand configuration option, which allows you to run a custom command (like the script) as a “proxy” for the actual SSH connection.
Add the following to your SSH config:
Host *
IdentityFile ~/.ssh/id_rsa
CertificateFile ~/.ssh/id_rsa-cert.pub
ProxyCommand bash -c 'path/to/valt-ssh.sh && nc %h %p'
Keep in mind that this approach generates a new SSH certificate for every connection. Depending on the frequency of your connections and the TTL of your certificates, you might want to modify the vault-ssh.sh
script to check the current certificate’s validity and generate a new one only if necessary.
Conclusion
This post covered configuring Vault to issue SSH certificates, setting up a remote server to accept these certificates, and streamlining the process of requesting SSH certificates. Use the knowledge from this post to enhance your environment’s security. Remember that this post describes a non-production setup of Vault and should be used for learning purposes only.
Credits: The above post was written from knowledge and experience of using Vault. The instructions for docker configuration of vault are from the official Vault docker documentation.