Configure SSH certificate authentication with CA signing for secure server access

Advanced 45 min May 03, 2026 521 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up SSH certificate-based authentication using a Certificate Authority to eliminate individual key management. Create signed user certificates that provide secure, scalable access control for multiple servers and users.

Prerequisites

  • Root or sudo access on SSH servers
  • Basic understanding of SSH key authentication
  • Network connectivity between CA server and clients
  • OpenSSH version 5.4 or later

What this solves

SSH certificate authentication eliminates the need to distribute and manage individual public keys across multiple servers. Instead of copying SSH keys to each server, you create a Certificate Authority (CA) that signs user certificates, and servers trust any certificate signed by your CA. This provides centralized access control, automatic key rotation, and enhanced security logging for enterprise environments.

Step-by-step configuration

Generate SSH Certificate Authority

Create a dedicated CA key pair that will sign user certificates. Store the CA private key securely as it controls access to all servers.

sudo mkdir -p /etc/ssh/ca
sudo chmod 700 /etc/ssh/ca
cd /etc/ssh/ca
sudo ssh-keygen -t ed25519 -f ssh_ca -C "SSH-CA-$(hostname)-$(date +%Y%m%d)"
sudo chmod 600 ssh_ca
sudo chmod 644 ssh_ca.pub

Configure SSH server for certificate authentication

Add the CA public key to SSH server configuration so it trusts certificates signed by your CA.

# Enable certificate authentication
TrustedUserCAKeys /etc/ssh/ca/ssh_ca.pub

# Optional: Require certificates (disable key authentication)
# PubkeyAuthentication no
# AuthenticationMethods publickey

# Enable certificate logging
LogLevel INFO

# Certificate validation settings
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

Restart SSH service

Apply the SSH configuration changes and verify the service starts correctly.

sudo systemctl reload sshd
sudo systemctl status sshd
sudo systemctl reload sshd
sudo systemctl status sshd

Create user SSH key pair

Generate a standard SSH key pair for the user. This key will be signed by your CA to create a certificate.

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_cert -C "username@example.com"
chmod 600 ~/.ssh/id_ed25519_cert
chmod 644 ~/.ssh/id_ed25519_cert.pub

Sign user certificate with CA

Use the CA private key to sign the user's public key, creating a certificate with specific validity period and permissions.

# Copy user public key to CA server
sudo cp ~/.ssh/id_ed25519_cert.pub /etc/ssh/ca/user_key.pub

# Sign the certificate (valid for 30 days)
sudo ssh-keygen -s /etc/ssh/ca/ssh_ca \
  -I "username@example.com" \
  -n "username" \
  -V "+30d" \
  /etc/ssh/ca/user_key.pub

# Copy certificate back to user
sudo cp /etc/ssh/ca/user_key-cert.pub ~/.ssh/id_ed25519_cert-cert.pub
sudo chown $(whoami):$(whoami) ~/.ssh/id_ed25519_cert-cert.pub
chmod 644 ~/.ssh/id_ed25519_cert-cert.pub

Configure SSH client for certificate authentication

Set up the SSH client to use the certificate for authentication.

Host *
    CertificateFile ~/.ssh/id_ed25519_cert-cert.pub
    IdentityFile ~/.ssh/id_ed25519_cert
    IdentitiesOnly yes
    
Host production-server
    HostName 203.0.113.10
    User username
    Port 22

Create certificate management script

Automate certificate signing with a script that includes validation and logging.

#!/bin/bash
set -euo pipefail

# SSH Certificate Signing Script
# Usage: sign-ssh-cert.sh username public_key_file [validity_days]

if [ $# -lt 2 ]; then
    echo "Usage: $0 username public_key_file [validity_days]"
    echo "Example: $0 john /tmp/john.pub 30"
    exit 1
fi

USERNAME="$1"
PUBKEY_FILE="$2"
VALIDITY_DAYS="${3:-30}"
CA_KEY="/etc/ssh/ca/ssh_ca"
LOG_FILE="/var/log/ssh-cert-signing.log"

# Validation
if [ ! -f "$PUBKEY_FILE" ]; then
    echo "Error: Public key file $PUBKEY_FILE not found"
    exit 1
fi

if [ ! -f "$CA_KEY" ]; then
    echo "Error: CA private key not found at $CA_KEY"
    exit 1
fi

# Validate public key format
if ! ssh-keygen -l -f "$PUBKEY_FILE" > /dev/null 2>&1; then
    echo "Error: Invalid public key format"
    exit 1
fi

# Generate certificate
CERT_FILE="${PUBKEY_FILE}-cert.pub"
echo "$(date): Signing certificate for $USERNAME, validity: $VALIDITY_DAYS days" >> "$LOG_FILE"

ssh-keygen -s "$CA_KEY" \
  -I "$USERNAME@$(hostname)-$(date +%Y%m%d_%H%M%S)" \
  -n "$USERNAME" \
  -V "+${VALIDITY_DAYS}d" \
  "$PUBKEY_FILE"

echo "Certificate created: $CERT_FILE"
echo "Valid for $VALIDITY_DAYS days"

# Show certificate details
echo "\nCertificate details:"
ssh-keygen -L -f "$CERT_FILE"

Make script executable and secure

Set correct permissions on the certificate signing script.

sudo chmod 750 /usr/local/bin/sign-ssh-cert.sh
sudo chown root:root /usr/local/bin/sign-ssh-cert.sh

# Create log file with proper permissions
sudo touch /var/log/ssh-cert-signing.log
sudo chmod 640 /var/log/ssh-cert-signing.log
sudo chown root:adm /var/log/ssh-cert-signing.log

Create certificate renewal script

Set up automated certificate renewal before expiration.

#!/bin/bash
set -euo pipefail

# SSH Certificate Renewal Script
# Checks certificate expiration and renews if needed

CERT_FILE="$HOME/.ssh/id_ed25519_cert-cert.pub"
KEY_FILE="$HOME/.ssh/id_ed25519_cert.pub"
CA_SERVER="ca-server.example.com"
USERNAME="$(whoami)"
RENEW_THRESHOLD_DAYS=7

if [ ! -f "$CERT_FILE" ]; then
    echo "Certificate not found at $CERT_FILE"
    exit 1
fi

# Check certificate expiration
EXPIRY_DATE=$(ssh-keygen -L -f "$CERT_FILE" | grep "Valid:" | awk '{print $4}')
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
CURRENT_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))

echo "Certificate expires in $DAYS_LEFT days ($EXPIRY_DATE)"

if [ "$DAYS_LEFT" -le "$RENEW_THRESHOLD_DAYS" ]; then
    echo "Certificate expires within $RENEW_THRESHOLD_DAYS days. Renewing..."
    
    # Copy public key to CA server for signing
    scp "$KEY_FILE" "$CA_SERVER:/tmp/${USERNAME}_renewal.pub"
    
    # Request certificate renewal
    ssh "$CA_SERVER" "sudo /usr/local/bin/sign-ssh-cert.sh $USERNAME /tmp/${USERNAME}_renewal.pub 30"
    
    # Download new certificate
    scp "$CA_SERVER:/tmp/${USERNAME}_renewal.pub-cert.pub" "$CERT_FILE"
    
    # Clean up temporary files
    ssh "$CA_SERVER" "sudo rm -f /tmp/${USERNAME}_renewal.pub /tmp/${USERNAME}_renewal.pub-cert.pub"
    
    echo "Certificate renewed successfully"
else
    echo "Certificate is still valid"
fi

Set up automated certificate renewal

Create a cron job to automatically check and renew certificates.

chmod +x /usr/local/bin/renew-ssh-cert.sh

# Add to user's crontab (runs daily at 9 AM)
echo "0 9 * * * /usr/local/bin/renew-ssh-cert.sh >> ~/.ssh/cert-renewal.log 2>&1" | crontab -

# Verify crontab
crontab -l

Configure host certificate (optional)

Sign host certificates to prevent SSH host key verification warnings.

# Generate host certificate CA (separate from user CA for security)
sudo ssh-keygen -t ed25519 -f /etc/ssh/ca/ssh_host_ca -C "SSH-Host-CA-$(hostname)"

# Sign the server's host key
sudo ssh-keygen -s /etc/ssh/ca/ssh_host_ca \
  -I "$(hostname)-host-cert" \
  -h \
  -n "$(hostname),203.0.113.10,localhost" \
  -V "+365d" \
  /etc/ssh/ssh_host_ed25519_key.pub

# Configure SSH to use host certificate
echo "HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl reload sshd

Verify your setup

Test certificate authentication and verify certificate details.

# Check certificate details
ssh-keygen -L -f ~/.ssh/id_ed25519_cert-cert.pub

# Test SSH connection with certificate
ssh -v username@203.0.113.10

# Check SSH logs for certificate authentication
sudo grep "Accepted publickey" /var/log/auth.log | grep -i cert

# Verify CA configuration on server
sudo sshd -T | grep -i trustedusercakeys

# Test certificate signing script
sudo /usr/local/bin/sign-ssh-cert.sh testuser ~/.ssh/id_ed25519_cert.pub 7
Security tip: Always store CA private keys securely and restrict access. Consider using hardware security modules (HSMs) or dedicated CA servers for production environments.

Common issues

Symptom Cause Fix
Permission denied with certificate Certificate expired or invalid principals Check certificate details with ssh-keygen -L -f cert.pub and verify principals match SSH username
SSH ignores certificate TrustedUserCAKeys not configured Verify /etc/ssh/sshd_config contains correct CA public key path
Certificate validation failed Clock skew between systems Synchronize time with NTP: sudo chrony sources -v
SSH falls back to key authentication Certificate file not found or wrong name Ensure certificate file ends with -cert.pub and is in SSH config
CA private key permission denied Incorrect file ownership or permissions Set ownership: sudo chown root:root ca_key and permissions: sudo chmod 600 ca_key

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: CA key rotation, certificate lifecycle management, compliance auditing, and incident response when certificates expire. Our managed platform covers monitoring, backups and 24/7 response by default.

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 01:27 · reachable in a message, no ticket form