#openpgp #public-key #ssh-key #ssh #primary-key #command-line-tool #pgp

bin+lib sshd-openpgp-auth

Command-line tool for creating and managing OpenPGP based trust anchors for SSH host keys

4 releases (2 breaking)

0.3.0 Feb 29, 2024
0.2.1 Feb 10, 2024
0.2.0 Feb 10, 2024
0.1.0 Jan 19, 2024

#456 in Cryptography

Apache-2.0 OR MIT and LGPL-2.0-or-later

66KB
1K SLoC

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:

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 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.

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.

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.

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.

export CA_TMPDIR="$(mktemp -d)"

# create a temporary CA key
sq key generate --userid 'SSH CA <admin@central-ca.com>' --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 "<ssh-openpgp-auth@example.com>"
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).

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.

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.

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.

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).

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.

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 proof of authenticity:

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:

$ dig +short TXT metacode.biz
"openpgp4fpr:0e3bb828432962f4e33c9a74d1f809bb3f02ede9"
"openpgp4fpr:198c722a4bac336e9daaae44579d01b3abe1540e"
"openpgp4fpr:653909a2f0e37c106f5faf546c8857e0d8e8f074"

For OpenPGP CA 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 page.

Funding

This project is funded through NGI Assure, a fund established by NLnet with financial support from the European Commission's Next Generation Internet program. Learn more at the NLnet project page.

NLnet foundation logo NGI Assure Logo

License

This project is licensed under either of:

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.

Dependencies

~17–27MB
~385K SLoC