OpenSSH is incorporated into many commercial products, but very few of those companies assist OpenSSH with funding.
Table of contents:
- Certificate Authority
- Daemon Configuration
- Client Configuration
OpenSSH daemon, in its default configuration, is more than sufficient for one user to remote login into one machine, with sophisticated security.
A typical fresh install has SSH host keys, and the OpenSSH daemon started on first boot.
The machine's public host key is extracted on first use:
laptop$ ssh 2001:0db8::1 The authenticity of host '2001:0db8::1 (2001:0db8::1)' can't be established.
As another option:
laptop$ ssh-keyscan -t ed25519 2001:0db8::1
At this moment, the public host key to be added in
~/.ssh/known_hosts (by the SSH protocol) is validated, completing the trust on first use (TOFU) security model.
Are you sure you want to continue connecting (yes/no)? yes
The identified public host key's fingerprint will be added to the DNS zone of the name associated with the host's IP address:
mercury.example.com IN AAAA 2001:0db8::1
Print the Secure Shell (Key) Fingerprint (SSHFP) Resource Record (RR) for the public host key:
laptop$ ssh-keygen -r 2001:0db8::1 -f ssh_host_ed25519_key.pub laptop$ ssh-keyscan -D -t ed25519 2001:0db8::1
SSHFP RR are added to the DNS zone for the chosen name, and signed:
mercury.example.com IN SSHFP 4 1 ec3fdef6b0bsd2dc3f8163a1e08069c92ff96d49 mercury.example.com IN SSHFP 4 2 f510394a7e4490800bsd15d066ecd0ae131ec6b03c7ca269a9013a62730ab2ca
After the DNS zone propagates, SSH fingerprints can be verified by a DNSSEC aware resolver:
laptop$ ssh -o "VerifyHostKeyDNS ask" firstname.lastname@example.org
In addition, the configuration option "VisualHostKey yes" generates "random art" on every login, for comparison:
laptop$ ssh -o "VisualHostKey yes" email@example.com +-[ED25519-CERT]--+ | + ....+o. | |.o *. o +. | |=.B. o oo. | |+X.= o..o | |BoO . S. . | |XX o | |B+B | |+o | |oE | +----[SHA256]-----+
The operating system installation script may include the system administrator's public key, for OpenSSH "publickey" authentication (on first boot):
Next authentication method: publickey
Otherwise, she must login using the interractive "password" authentication method, to add her public key on the host:
laptop$ cat ~/.ssh/id_ed25519.pub | ssh firstname.lastname@example.org "cat >> .ssh/authorized_keys"
i.e. Remote access to local user "puffy" on host "mercury.example.com" is now under control.
As more systems and users are added to the mix, it becomes cumbersome to manage and keep remote access secure solely with the TOFU model.
To scale, OpenSSH implements a mechanism for using a trusted third party in the form of Certificate Authority (CA).
While the concept is similar to X.509 (commonly used in TLS), OpenSSH certificates are a different format. They contain much less information, which doesn't mean they are less useful. In fact, OpenSSH certificates allow fine-grained control to local users and hosts with security principal.
OpenSSH provides both identification and authentication of hosts and users, which are distinct entities, as seen earlier with TOFU. Certificates expand this security model.
Hosts and users still need to go through TOFU, but only once, which greatly improves security and usability of OpenSSH keys, and a fortiori the TOFU model itself.
Moreoever, instead of configuring access on each host, principal permissions are set inside the certificate, and signed by a central authority.
In computer security, and in OpenSSL, principals can be hosts, users, or groups of various types.
Each principal has a distinct security identifier (SID):
- Host identities can be part of individual hostname principals, or groups.
- User identities can be part of individual user principals, or groups.
Expanding on the previous scenario, let's introduce a CA and combine these characteristics by adding a second host.
To make it tangible, we'll focus on the email service of a simple network, and our two hosts in this example will become the primary and backup MX.
OpenSSH Certificate Authorities may be created on encrypted flash memory card/drive, and operated on single purpose secure machines, with no network access.
/etc |-ssh/ | |-ca/ | | |-.ssh/ | | | |-ssh_ca_ed25519 | | | |-ssh_ca_ed25519.pub | | |-ssh_ca.krl | | |-host/ | | | |-hermes.example.com/ | | | | |-.ssh/ | | | | | |-ssh_host_ed25519_key.pub | | | | | |-ssh_host_ed25519_key-cert.pub | | | |-mercury.example.com/ | | | | |-.ssh/ | | | | | |-ssh_host_ed25519_key.pub | | | | | |-ssh_host_ed25519_key-cert.pub | | |-user/ | | | |-puffy/ | | | | |-.ssh/ | | | | | |-id_ed25519.pub | | | | | |-id_ed25519-cert.pub | | |-auth/ | | | |-all | | | |-backup | | | |-database | | | |-dns | | | |-email | | | |-router | | | |-nas | | | |-web
Since two hosts are part of the same service, they'll be part of the same "email" group principal. In this way, authentication is based on service type, instead on each host in the service group.
n.b. The files under
/etc/ssh/ca/auth represent other services on the network, and are informational cues.
Let's create each part of this sample CA, starting with its layout:
airtight# mkdir -pm 755 /etc/ssh/ca/.ssh
Generate a new password protected CA key:
airtight# ssh-keygen -t ed25519 -C email@example.com -f /etc/ssh/ca/.ssh/ssh_ca_ed25519
Transfer the public CA key from CA to sysadmin's laptop, and further to the host:
laptop$ scp /etc/ssh/ca/.ssh/ssh_ca_ed25519.pub \ firstname.lastname@example.org:/etc/ssh/ca/.ssh/
The CA is now ready to issue signed certificates with the following information:
- public key
- (-I) identity
- (-n) principal(s)
- (-V) validity interval
- (-z) serial number
- (-O) configuration options
OpenSSH host certificates authenticate server hosts to users.
Add a new host to CA:
airtight# mkdir -pm 755 /etc/ssh/ca/host/mercury.example.com/.ssh
Add the identified public host key:
airtight# cp ssh_host_ed25519_key.pub /etc/ssh/ca/host/mercury.example.com/.ssh/
Generate a (-h) host certificate, using the
hostname as identity and principal:
airtight# ssh-keygen -s /etc/ssh/ca/.ssh/ssh_ca_ed25519 \ -h \ -I mercury.example.com \ -n mercury.example.com \ -V always:forever \ -z 1 \ /etc/ssh/ca/host/mercury.example.com/.ssh/ssh_host_ed25519_key.pub
airtight# ssh-keygen -L \ -f /etc/ssh/ca/host/mercury.example.com/.ssh/ssh_host_ed25519_key-cert.pub Type: email@example.com host certificate Public key: ED25519-CERT SHA256:OBsd... Signing CA: ED25519 SHA256:Zszg... Key ID: "mercury.example.com" Serial: 1 Valid: forever Principals: mercury.example.com Critical Options: (none) Extensions: (none)
Transfer the host certificate from CA to sysadmin's laptop, and further to the host:
laptop$ scp /etc/ssh/ca/host/mercury.example.com/.ssh/ssh_host_ed25519_key-cert.pub \ firstname.lastname@example.org:/etc/ssh/
The second host, "hermes.example.com" is added to the CA and processed as above.
OpenSSH user certificates authenticate users to servers.
On her end, the user generates a new password protected key:
laptop$ ssh-keygen -t ed25519 -C email@example.com > Enter file in which to save the key (/home/puffy/.ssh/id_ed25519):
The user sends her public key to her system administrator, to generate a user certificate, using "email" group principal:
airtight# ssh-keygen -s /etc/ssh/ca/.ssh/ssh_ca_ed25519 \ -I puffy \ -n email \ -O no-x11-forwarding \ -V +31d \ -z 2 \ /etc/ssh/ca/user/puffy/.ssh/id_ed25519.pub > Signed user key /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub: id "puffy" serial 2 for puffy valid from 2018-10-02T13:41:00 to 2018-10-31T13:42:37
airtight# ssh-keygen -L \ -f /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub Type: firstname.lastname@example.org user certificate Public key: ED25519-CERT SHA256:0B5d... Signing CA: ED25519 SHA256:Zszg... Key ID: "puffy" Serial: 2 Valid: from 2018-10-02T13:41:00 to 2018-10-31T13:42:37 Principals: email Critical Options: (none) Extensions: permit-agent-forwarding permit-port-forwarding permit-pty permit-user-rc
Transfer the user certificate from CA to sysadmin's laptop (and further to the user):
laptop$ scp /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub \ email@example.com:/home/puffy/.ssh/
Key Revocation List
Key revocation lists (KRL) avoid the process of recreating the CA on each change.
Create a new KRL
airtight# ssh-keygen -k \ -f /etc/ssh/ca/ssh_ca.krl \ -s /etc/ssh/ca/.ssh/ssh_ca_ed25519.pub \ -z 2 \ /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub
Update an existing KRL
airtight# ssh-keygen -k \ -f /etc/ssh/ca/ssh_ca.krl \ -u \ -s /etc/ssh/ca/.ssh/ssh_ca_ed25519.pub \ -z 2 \ /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub
airtight# ssh-keygen -Q \ -f /etc/ssh/ca/ssh_ca.krl \ /etc/ssh/ca/user/puffy/.ssh/id_ed25519-cert.pub
On each update, transfer the KRL to a distribution location for all hosts, or:
laptop$ scp /etc/ssh/ca/ssh_ca.krl firstname.lastname@example.org:/etc/ssh/ca/ laptop$ scp /etc/ssh/ca/ssh_ca.krl email@example.com:/etc/ssh/ca/
By now, hosts and users are in possession of their certificates, and OpenSSH needs to be aware of this.
/etc |-ssh/ | |-ca/ | | |-ssh_ca.krl | |-principals/ | | |-puffy | |-ssh_host_ed25519_key | |-ssh_host_ed25519_key-cert.pub | |-ssh_host_ed25519_key.pub
On hosts, we associate principals with local users:
mercury# mkdir -m 755 /etc/ssh/principals mercury# echo -e 'email' > /etc/ssh/principals/puffy
i.e. The "email" group principal has access to local user "puffy", on hosts "mercury.example.com" and "hermes.example.com".
Relevant daemon configuration snippet:
mercury# cat /etc/ssh/sshd_config ... #HostKey /etc/ssh/ssh_host_ed25519_key # https://man.openbsd.org/sshd_config.5#HostKeyAlgorithms HostKeyAlgorithms firstname.lastname@example.org,ssh-ed25519 HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub # Certificate Authority TrustedUserCAKeys /etc/ssh/ca/.ssh/ssh_ca_ed25519.pub RevokedKeys /etc/ssh/ca/ssh_ca.krl # http://man.openbsd.org/sshd_config.5#CASignatureAlgorithms #CASignatureAlgorithms ssh-ed25519 PermitRootLogin no # default: prohibit-password #PubkeyAuthentication yes # http://man.openbsd.org/sshd_config.5#PubkeyAcceptedKeyTypes PubkeyAcceptedKeyTypes email@example.com,ssh-ed25519 AuthorizedKeysFile none # default: .ssh/authorized_keys # http://man.openbsd.org/sshd_config.5#AuthorizedPrincipalsFile AuthorizedPrincipalsFile /etc/ssh/principals/%u # default: none PasswordAuthentication no # default: yes ...
~ |-.ssh/ | |-config | |-known_hosts | |-id_ed25519 | |-id_ed25519-cert.pub | |-id_ed25519.pub
Relevant client configuration snippet for CA public key:
laptop$ cat ~/.ssh/known_hosts @cert-authority *.example.com ssh-ed25519 AAAA... firstname.lastname@example.org
Hash "known_hosts" file:
laptop$ ssh-keygen -H \ -f ~/.ssh/known_hosts
Relevant client configuration snippet for SSHFP:
laptop$ cat ~/.ssh/config Host * VerifyHostKeyDNS ask VisualHostKey yes
OpenSSH configuration is flexible, and it is well worth upgrading to certificates, even for a single user.
It is possible to further centralize with OpenSSH gateway, using
gateway$ cat /etc/ssh/sshd_config ... #http://man.openbsd.org/rdomain #http://man.openbsd.org/sshd_config#ListenAddress ListenAddress :: rdomain 1 #http://man.openbsd.org/sshd_config#RDomain #http://man.openbsd.org/sshd_config#Match RDomain %D ...
Contributions welcome, please open a Pull Request