wiki:OpenPGPandSSH

Version 29 (modified by jrollins, 10 years ago) (diff)

--

Using OpenPGP certificates with SSH connections

This page tries to document a proposal for using OpenPGP certificates with SSH connections. The proposal is going under the working title of MonkeySphere.

Goals

  • Use all free software
  • Use OpenPGP certificates for authentication in both directions (client->server and server->client)
  • Authorization should use User IDs, not keys
  • Key revocation/transition should be straightforward and effective without explicit notification of individual hosts or users
  • End users and server administrators should be able to choose who they trust to properly identify/introduce other entities during authentication
  • Any implementation should be cleanly interoperable with a non-OpenPGP-capable SSH implementation
  • Minimize the amount of patching of upstream sources. Ideally, people should be able to use this framework with the existing tools

Overview

OpenSSH provides a functional way for management of explicit RSA keys (without certification of any type). The basic idea of this project is to create a framework that uses GPG's keyring manipulation capabilities and public keyservers to generate files that OpenSSH will accept and handle without complaint.

Both entities in an OpenSSH connection (client and server) thus have the responsibility to explicitly designate who they trust to "introduce" others. They can explicitly indicate this trust relationship with traditional GPG keyring trust indicators. No modification is made to the SSH protocol on the wire, which continues to use raw RSA public keys.

Simplifying Assumptions

These assumptions might not be necessary, but we'll humor them for the sake of a clean implementation at the moment.

  • Only use RSA keys, since RSA is known to work with both OpenSSH and GPG.
  • This framework will use a specialized keyring, so that explicit trust relationships mapped here don't necessarily overflow into other OpenPGP-covered domains.
  • A redundant set of public keyservers is available for both client and server to access (both query and upload) at will.

Key Translations

How do we use the same key for GPG and OpenSSH?

Private Keys

At the least, we need to have a way to convert the private key from one format to another. While i have no problem using an OpenSSH-generated RSA private key as the private part of an X.509 key/cert pair (and vice versa), i'm having difficulty figuring out how to translate the PEM-encoded RSA keys into OpenPGP-encoded RSA keys.

This conversion should be do-able by reading the OpenPGP spec for Secret Key packet formats and the RSA encryption standard's Private Key Syntax. It's also possible that GnuTLS (which is capable of dealing with both OpenPGP and X.509/PEM) might be able to do the conversion.

Public Keys

We'll also probably need to be able to translate public keys, since the keyservers will offer OpenPGP-formatted public keys, but OpenSSH's ssh-keygen can convert from the IETF's SECSH Public Key Format, which is different. AFAICT, ssh-keygen isn't capable of converting from an OpenPGP public key. When i try it, i get:

0 dkg@ape:~/.keys$ ssh-keygen -i -f gpg.pubkey 
uudecode failed.
1 dkg@ape:~/.keys$ 

Validating the User

The server's job when a connection is created is to authenticate an incoming request, and to verify that the authenticated entity is authorized to connect.

The first thing a server operator will need to do is to establish the host's own keyring, indicating which OpenPGP entities the host should trust to "introduce" people by certifying the binding between User ID and public key.

Technique

When an RSA public key is offered to the server by a user proposing to make a connection, it is looked up in the user's authorized_keys file. The framework's job is therefore to populate that file with the public keys that should be acceptable and no more. Let's assume that each user has a ~/.ssh/authorized_userids file, with the same syntax as the traditional authorized_keys file, but with an OpenPGP User ID in place of the public key.

Further, let's assume that sshd on the host has been configured to look for authorized_keys files somewhere not under the user's immediate control (say, in /var/lib/wherever/authorized_keys/username). The framework will transform the ~/.ssh/authorized_userids into the system-maintained authorized_keys files.

For each line in ~/.ssh/authorized_userids, the framework would:

  • query the public keyservers for the exact User ID mentioned.
    For each key found with a bound User ID:
    • if a legitimate trust path is present for that User ID:
      • create an entry in the user's authorized_keys file, using the OpenSSH form of the associated public key

What if two User IDs map to the same public key? What if those User IDs have different options? (see AUTHORIZED_KEYS options in man sshd) Which options take precedence in the resultant authorized_keys file?

Timing

The simplest implementation of the framework would a regular poll, querying public keyservers for values listed in ~/.ssh/authorized_userids for each user. This could cause considerable lag, though: what if your key was just added to the keyserver and now you want to log in? Is there a way to get sshd to do the lookup just-in-time? Could we trigger lookups at specific times (such as when authorized_userids changes)?

Authenticating the Server

When initiating a connection, a user needs to verify that she is actually connecting to the specific host she expects it to be.

OpenPGP Host User ID format

To get an OpenPGP certificate to identify an SSH host, there needs to be a binding between a User ID and a public key where the User ID unambiguously specifies the SSH host. The OpenPGP Spec indicates that the User ID packets can be arbitrary UTF-8 strings, though by convention they are RFC 822 addresses, but that this is not a requirement.

I'm unaware of any registry of definitions of other forms of User ID. In the absence of other definitions, i'd like to propose that the relevant User ID should look like this:

ssh://monkey.example.net

Specifically, it should be formatted in accordance with the draft for ssh URIs, but with no username, and the path-abempty part entirely empty.

With gpg, this may require the use of --allow-freeform-uid

Fetching a host key

A user should be able to query a public keyserver for a hostkey matching the pattern mentioned. Could we do such a check immediately prior to each connection?

Key revocation

How does key revocation work? How can we be sure that a revoked key is no longer accepted within a reasonable period of time after the revocation takes place?

Key transitions

Say a user or a host needs to change keys. How can a new key be adopted smoothly without explicit notification of all the relevant hosts?

Open Questions

Protecting public keyservers from DoS attacks

If anyone can upload keys to a public keyserver, how do we keep them from being drowned in irrelevance? How do we avoid DoS attacks of various sorts?

Good Logging

Would it be possible to get OpenSSH or the framework to log the relevant User ID on each successful connection?

Minimizing Windows of Vulnerability

Depending on the timings/delays of fetching keys, signatures, and revocations from keyservers, there is a window during which an invalid key will be treated as valid. How do we minimize these windows without inserting significant delay in the common use case?

Chained Trust specific to account owner

It seems like a GPG keyring should provide enough information to describe basic server-wide trust rules. But what if you wanted a trust rules like:

  • for account foo, trust UserID certifications from key XYZ as well as the usual system-wide introducers, or
  • for account foo, allow chained introductions through any key with already-certified User ID Fubar <foo@example.org>

How would this be implemented?

Minimizing Bad Incentives

Some users will always want to share access to their account, even if it is discouraged by the system administrators. Ultimately, since users can share authentication tokens (or keyboards!) with each other, there is nothing that can be done to prevent this absolutely. How do we make this framework flexible enough to allow users who want to share access to do so without doing things that violate the web of trust?

Are there any other bad incentives that this framework might create that we should try to mitigate?

Resources

Other Work

References

Tools