# `sshd-openpgp-auth` This tool provides server-side functionality to create and manage OpenPGP certificates that serve as trust anchors for SSH host keys. More specifically, `sshd-openpgp-auth` transparently manages the lifecycle of an OpenPGP certificate and the validity of SSH host keys as its authentication subkeys. Its featureful commandline interface allows for import of host keys from SSH public key files as well as [known_hosts file format] formatted strings, extension of expiry period of the primary key, revocation of any of the subkeys and deployment to a [Web Key Directory (WKD)]. As complementary client-side tool `ssh-openpgp-auth` is used to validate the trust anchors and OpenPGP subkeys created with `sshd-openpgp-auth`. ## Installation To install this tool use cargo: ```shell cargo install sshd-openpgp-auth ``` ## Dependencies This tool is able to add subkeys from existing SSH host keys on a system. For this only access to the SSH public keys is required. When importing the public SSH host keys of remote machines (in [known_hosts file format]), the output of [`ssh-keyscan`] (usually part of an [OpenSSH] installation) can be used directly. ## Using `sshd-openpgp-auth` This tool does not require a configuration file and relies on defaults which can be overridden on the command-line and using dedicated environment variables. By default SSH public keys are searched for in `/etc/ssh/` and OpenPGP certificates are stored in `/var/lib/sshd-openpgp-auth/`. As such, the tool should be run as a separate system user when using the default OpenPGP certificate location! This repository offers a [sysusers.d] and [tmpfiles.d] integration in the [contrib](/contrib/) dir for this scenario. However, it is entirely possible to manage the trust anchors of *other hosts* and rely on one's own user for this with the help of the `--openpgp-dir` and `--fingerprint` options to define the directory for storing OpenPGP certificates and identifying specific OpenPGP certificates with the help of an OpenPGP fingerprint. The following subsections provide a broad overview of the various commands `sshd-openpgp-auth` offers. For a more detailed overview refer to the command's `--help` output. ### Initializing a trust anchor To start offering OpenPGP based authentication for the SSH setup of a host, one needs to create an OpenPGP certificate, which serves as trust anchor. For demonstration purposes let us assume, that we are working with a custom temporary storage location for our OpenPGP certificates, are not using the system's SSH host keys (generating the host keys requires [`ssh-keygen`], usually part of [OpenSSH]) and want to work on a host named `example.com`. ```sh export SSH_DIR="$(mktemp -d)" export SOA_SSH_DIR="$SSH_DIR/etc/ssh/" export LOCAL_SSH_DIR="$SOA_SSH_DIR" export SOA_OPENPGP_DIR="$(mktemp -d)" export LOCAL_OPENPGP_DIR="$SOA_OPENPGP_DIR" mkdir -vp "$SOA_SSH_DIR" ssh-keygen -A -f "$SSH_DIR" sleep 2 sshd-openpgp-auth init "example.com" sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` ### Adding SSH host keys The SSH host keys of a system can be added to an OpenPGP certificate in two ways: Locally and remotely. #### Local host keys To add the local SSH host keys the `add` subcommand is used. ```sh sshd-openpgp-auth add sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk unset SOA_SSH_DIR ``` #### Remote host keys Let us assume we are [codeberg.org] and want to use `sshd-openpgp-auth` to add the service's SSH public keys to a trust anchor on a different host. This example requires [`ssh-keyscan`]. ```sh export PREV_SOA_OPENPGP_DIR="$SOA_OPENPGP_DIR" export SOA_OPENPGP_DIR="$(mktemp -d)" sshd-openpgp-auth init "codeberg.org" sshd-openpgp-auth list set +o pipefail ssh-keyscan -t ecdsa,ed25519,rsa codeberg.org | sshd-openpgp-auth add --known-hosts set -o pipefail sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk export REMOTE_OPENPGP_DIR="$SOA_OPENPGP_DIR" export SOA_OPENPGP_DIR="$PREV_SOA_OPENPGP_DIR" ``` ### Adding thirdparty certifications The client-side tool `ssh-openpgp-auth` can validate the authenticity of a host's OpenPGP certificate based on PGPKI (aka the [Web of Trust]). In this scenario the user of the client tool usually trusts a central [certificate authority (CA)]: Another OpenPGP certificate holder that certifies the host's OpenPGP certificate. Let us assume that the host `example.com` will be certified by an OpenPGP certificate with the User ID `admin@central-ca.com`. ```sh export CA_TMPDIR="$(mktemp -d)" # create a temporary CA key sq key generate --userid 'SSH CA ' --output "$CA_TMPDIR/ca_key.tsk" sq inspect --certifications "$CA_TMPDIR/ca_key.tsk" # certify the host's key using the CA key sq pki certify --output "$CA_TMPDIR/example_certification.pgp" "$CA_TMPDIR/ca_key.tsk" "$SOA_OPENPGP_DIR/"*.tsk "" sq inspect --certifications "$CA_TMPDIR/example_certification.pgp" ``` It is now possible to directly import the created certification by merging it with the host's [Transferable Secret Key] ([TSK]). ```sh sshd-openpgp-auth merge "$CA_TMPDIR/example_certification.pgp" sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` Managing thirdparty certifications such as the one added above in the host's [TSK] has the upside, that certifications are directly available to clients upon exporting the host key to [WKD]. ### Exporting to Web Key Directory One of the upsides of `sshd-openpgp-auth` is, that it can be used in the context of the already existing OpenPGP based ecosystem for certificate retrieval: [WKD]. Using the `export` subcommand, certificates can be exported to a local directory, which can directly be served by a web server. ```sh export SOA_WKD_OUTPUT_DIR="$(mktemp -d)" sshd-openpgp-auth export "example.com" export SOA_WKD_OUTPUT_DIR="$(mktemp -d)" sshd-openpgp-auth export "example.com" --wkd-type "direct" ``` ### Extend the expiry period When using this tool all added subkeys depend on the expiration time of the OpenPGP primary key. Using the `extend` subcommand the primary key can be extended. Let us assume, the administrator wants to extend the OpenPGP certificate's expiry period to a duration longer than the default. ```sh sshd-openpgp-auth extend --threshold 365 --expiry 730 sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` ### Revoking authentication subkeys Sometimes it is necessary to revoke one or all of the authentication subkeys. Reasons for this may be, that the administrator wants to create new SSH host keys with stronger cryptographic algorithms, deprecate old ones or (worst case) has to replace compromised ones. Let us have a look at the different scenarios in the following subsections. #### The key has been superseded This is the default revocation action and used in scenarios where new OpenPGP authentication subkeys replace old ones. Let us again imagine we are [codeberg.org] and want to replace our current SSH host keys with new ones. We will add newly created SSH host keys and then revoke the old ones while appending a message that points at the new subkeys. ```sh export SOA_OPENPGP_DIR="$REMOTE_OPENPGP_DIR" export SOA_SSH_DIR="$LOCAL_SSH_DIR" sshd-openpgp-auth list sshd-openpgp-auth add sshd-openpgp-auth list export REMOTE_SUBKEYS=( $(sshd-openpgp-auth list | grep '✅' | cut -f 2 -d ' ') ) sshd-openpgp-auth revoke --subkey-fingerprint "${REMOTE_SUBKEYS[0]}" --message "Superseded by ${REMOTE_SUBKEYS[1]}" sshd-openpgp-auth revoke --subkey-fingerprint "${REMOTE_SUBKEYS[2]}" --message "Superseded by ${REMOTE_SUBKEYS[3]}" sshd-openpgp-auth revoke --subkey-fingerprint "${REMOTE_SUBKEYS[4]}" --message "Superseded by ${REMOTE_SUBKEYS[5]}" sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` #### The key is retired For this example let us assume that we want to retire only a single SSH host key (e.g. because we do not support the algorithm in question anymore). ```sh export SOA_OPENPGP_DIR="$LOCAL_OPENPGP_DIR" sshd-openpgp-auth list export REMOTE_SUBKEYS=( $(sshd-openpgp-auth list | grep '✅' | cut -f 2 -d ' ') ) sshd-openpgp-auth revoke --subkey-fingerprint "${REMOTE_SUBKEYS[0]}" --reason retired --message "We stopped supporting RSA" sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` #### The key is compromised Let us also have a look at our worst-case scenario: Our host has been compromised and an attacker stole our SSH host keys to be able to impersonate our host. ```sh sshd-openpgp-auth revoke --all --reason compromised --message "This host has been compromised today!" sshd-openpgp-auth list sq inspect --certifications "$SOA_OPENPGP_DIR/"*.tsk ``` ### Adding Keyoxide DNS proof To increase trust in the host certificate it's also possible to insert [Keyoxide](https://keyoxide.org/) proof of authenticity: ```sh sshd-openpgp-auth proof dns add ``` The above will add a notation to the certificate, which represents a claim about the ownership of the domain the certificate is used for. Clients can retrieve the certificate via WKD or OpenPGP keyservers and attempt to validate this claim. To allow clients to validate the identity claim added with the notation, specific proof must be added to the DNS zone file of the host as a `TXT` record in the following format: ``` openpgp4fpr:FINGERPRINT ``` Where `FINGERPRINT` is a lower-cased hexadecimal representation of the key's fingerprint. An example of several authorized keys: ```bash $ dig +short TXT metacode.biz "openpgp4fpr:0e3bb828432962f4e33c9a74d1f809bb3f02ede9" "openpgp4fpr:198c722a4bac336e9daaae44579d01b3abe1540e" "openpgp4fpr:653909a2f0e37c106f5faf546c8857e0d8e8f074" ``` For [OpenPGP CA](https://openpgp-ca.org/) installations the fingerprint of the OpenPGP CA certificate should also be present in the `TXT` records of the DNS zone. For more technical details see [Keyoxide DNS proof](https://docs.keyoxide.org/service-providers/dns/) page. ## Funding This project is funded through [NGI Assure](https://nlnet.nl/assure), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/OpenPGP-OpenSSH). [NLnet foundation logo](https://nlnet.nl) [NGI Assure Logo](https://nlnet.nl/assure) ## License This project is licensed under either of: - [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0), - [MIT license](https://opensource.org/licenses/MIT). at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [known_hosts file format]: https://man.archlinux.org/man/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT [Web Key Directory (WKD)]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service#name-web-key-directory [WKD]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service#name-web-key-directory [`ssh-keyscan`]: https://man.archlinux.org/man/ssh-keyscan.1 [OpenSSH]: https://www.openssh.com/portable.html [sysusers.d]: https://man.archlinux.org/man/sysusers.d.5 [tmpfiles.d]: https://man.archlinux.org/man/tmpfiles.d.5 [`ssh-keygen`]: https://man.archlinux.org/man/ssh-keygen.1 [codeberg.org]: https://codeberg.org [Web of Trust]: https://en.wikipedia.org/wiki/Web_of_trust [certificate authority (CA)]: https://en.wikipedia.org/wiki/Certificate_authority [Transferable Secret Key]: https://openpgp.dev/book/glossary.html#term-Transferable-Secret-Key [TSK]: https://openpgp.dev/book/glossary.html#term-Transferable-Secret-Key