Skip to content

Instantly share code, notes, and snippets.

@flanger001
Last active August 10, 2025 18:08
Show Gist options
  • Save flanger001/e9a084709eb84f4d8def45e0a9a74af8 to your computer and use it in GitHub Desktop.
Save flanger001/e9a084709eb84f4d8def45e0a9a74af8 to your computer and use it in GitHub Desktop.
SSH Certificates on local network

OpenSSH Certificates

I have some machines on my local network that I want to be able to log into from any other machine on my network. This can be done by generating SSH key pairs for each user/client, then putting each of the public keys in each other machine's authorized_keys file. It sounds simple, but it is a nightmare scenario. Every machine needs to know about every public key for every other client on the network, and adding or removing a machine means manually managing all of those public keys. For a small number (<= 2) of machines it is manageable and possibly even preferable, but I would argue that 3 or more machines requires a different strategy for the sanity of the network admin, and this is where SSH Certificates come in.

The general idea with SSH Certificates is that you establish 2 certificate authorities: a User CA and a Host CA. The User CA is for authenticating users to hosts, and the Host CA is for authenticating hosts to users.

For the User CA, you distribute the User CA public key to every host you need to recognize it, then configure each host to trust it. You get user public keys and sign them with the User CA private key, then distribute those keys back to the clients. Each host that trusts your User CA will trust public keys signed by the CA.

For the Host CA, you get host public keys and sign them with the Host CA private key, then distribute those keys back to the hosts. Then you configure each host to offer its certificate to users logging in.

This guide is the result of my notes from learning about and implementing both User and Host CAs on my local network. The majority, but not all, of these commands will need to be run as the root user, but I will not be distinguishing these in this guide due to the impossibility of generalizing such things for arbitrary networks. In other words, I have no idea and cannot have any idea what your user account situation is on your local network, so if you are following this guide and run into permissions errors, it is on you to make sure you are executing commands with the correct privileges.

User Certificate Authority (CA)

Establish User CA

Establishing a User CA is as simple as creating a key pair. For my example, I created an /etc/ssh/ca_keys directory, so this guide will do the same:

# Create User CA key pair
ssh-keygen -t ed25519 -f /etc/ssh/ca_keys/user_ca_key -C "User CA"

Distribute and trust User CA public key

For each host that needs to recognize the User CA, distribute the User CA's public key. Recommend placing it in /etc/ssh/ on the host.

# Possible script to distribute User CA public key
scp /etc/ssh/ca_keys/user_ca_key.pub remote_host:/etc/ssh/user_ca_key.pub

Configure the host to trust the User CA public key:

# nano /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/user_ca_key.pub

Restart the sshd service after this:

sudo service sshd restart

# Or with systemctl
sudo systemctl restart sshd

# Or with launchctl
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist 
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist 

Sign user public keys

For each user that needs to be able to remotely access a machine, acquire and sign their public key. These can be generated by the User CA and distributed to the machines that require them, or they can be generated on the client machines and sent to the User CA for signing.

# Possible script to acquire user keys
scp remote_host:~/.ssh/id_ed25519.pub /etc/ssh/user_keys/clientname-id_ed25519.pub
ssh-keygen -I "Client name" \                # -I is a comment or identifier logged at login
-s /etc/ssh/ca_keys/user_ca_key \            # -s indicates we are using the private key at /etc/ssh/ca_keys/user_ca_key to sign a public key
-n username1,username2 \                     # -n is a comma-separated list of user names that are allowed to login with this cert
-z 1 \                                       # -z is a serial number used for logging
-V +52w \                                    # -V is a date string indicating how long the certificate is valid for
/etc/ssh/user_keys/clientname-id_ed25519.pub # This is the user public key we are signing

Fully expanded out:

ssh-keygen -I "Client name" -s /etc/ssh/ca_keys/user_ca_key -n username1,username2 -z 1 -V +52w /etc/ssh/user_keys/clientname-id_ed25519.pub

This will output a certificate at /etc/ssh/user_keys/clientname-id_ed25519-cert.pub

Distribute user certificate back to client

# Possible script to distribute user certificates
scp /etc/ssh/user_keys/clientname-id_ed25519-cert.pub remote_host:~/.ssh/id_ed25519-cert.pub

At this point, the client should be able to remotely log into the host with no password.

Host Certificate Authority (CA)

Establish Host CA

Once again, establishing a Host CA is as simple as creating a key pair. We will reuse the ca_keys directory from the User CA section:

# Create Host CA key pair 
ssh-keygen -t ed25519 -f /etc/ssh/ca_keys/host_ca_key -C "Host CA"

Sign host public keys

For each host that needs to allow remote access, acquire its host public key. This will usually be in /etc/ssh/, example: /etc/ssh/ssh_host_ed25519_key.pub. These keys are generated automatically when openssh-server is installed.

# Possible script to acquire host keys
scp remote_host:/etc/ssh/ssh_host_ed25519_key.pub /etc/ssh/host_keys
# Signing host public key with Host CA private key creates host certificate: 
ssh-keygen -I "Host name" \                 # -I is a comment or identifier logged at login
-s /etc/ssh/ca_keys/host_ca_key \           # -s indicates we are using the private key at /etc/ssh/ca_keys/host_ca_key to sign a public key
-h \                                        # -h indicates this is creating a host certificate
-n hostname \                               # -n is a comma-separated list of host names that are authenticated with the CA
-z 1 \                                      # -z is a serial number used for logging
-V +52w \                                   # -V is a date string indicating how long the certificate is valid for
/etc/ssh/host_keys/ssh_host_ed25519_key.pub # This is the host public key we are signing

Fully expanded out:

ssh-keygen -I "Host name" -s /etc/ssh/ca_keys/host_ca_key -h -n hostname -z 1 -V +52w /etc/ssh/host_keys/ssh_host_ed25519_key.pub

This will output a certificate at /etc/ssh/host_keys/ssh_host_ed25519_key-cert.pub.

Distribute host certificate back to host

# Possible script to return host keys
scp /etc/ssh/host_keys/ssh_host_ed25519_key-cert.pub remote_host:/etc/ssh/

Configure host to present the certificate

# nano /etc/ssh/sshd_config
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

You may also need to run chown root:root /etc/ssh/ssh_host_ed25519_key-cert.pub in order to make sshd recognize the certificate.

Restart the sshd service after this:

sudo service sshd restart

# Or with systemctl
sudo systemctl restart sshd

# Or with launchctl
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist 
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist 

At this point, your hosts should present their certificates to clients logging in. You can see this by running ssh -v remote_host. You will see a line in the logging output that reads something like debug1: Server host certificate: [email protected] ....

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment