Configure SSH certificate authentication with CA signing for secure server access

Advanced 45 min May 03, 2026 87 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

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.