From d0b84c5b5915b4aec8e35a1784cace8ded15bd18 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Thu, 7 Jul 2016 21:44:23 +0000 Subject: [PATCH] Document signing key management Add key management process sections with configuration details, a transcript of key generation and an example of key signing. Change-Id: Ibf4588437a9ed62c111df1728722358f51b92016 --- doc/source/signing.rst | 440 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 436 insertions(+), 4 deletions(-) diff --git a/doc/source/signing.rst b/doc/source/signing.rst index a0209ddf25..e9d210e769 100644 --- a/doc/source/signing.rst +++ b/doc/source/signing.rst @@ -80,8 +80,440 @@ completely, the revocation certificate generated at key creation time should be used as a last resort. -Management -========== +Key Management Process +====================== -As process is solidified, this section will be updated with specific -commands and examples. +Configuration +------------- + +This is the content of the ``/root/signing.gnupg/gpg.conf`` file on +our management bastion host:: + + # A basic gpg.conf using secure keyserver transport and some more + # verbose display options. This configuration assumes you have + # installed both the gnupg and gnupg-curl packages. Set your umask + # to 077, create a /root/signing.gnupg directory and place this + # configuration file in it. + # + # Retrieve and validate the HKPS key for the SKS keyservers this way: + # + # wget -P ~/signing.gnupg/ \ + # https://sks-keyservers.net/sks-keyservers.netCA.pem{,.asc} + # gpg --homedir signing.gnupg --recv-key \ + # 0x94CBAFDD30345109561835AA0B7F8B60E3EDFAE3 + # gpg --homedir signing.gnupg --verify \ + # ~/signing.gnupg/sks-keyservers.netCA.pem{.asc,} + + # Receive, send and search for keys in the SKS keyservers pool using + # HKPS (OpenPGP HTTP Keyserver Protocol via TLS/SSL). + keyserver hkps://hkps.pool.sks-keyservers.net + + # Set the path to the public certificate for the + # sks-keyservers.net CA used to verify connections to servers in + # the pool above. + keyserver-options ca-cert-file=/root/signing.gnupg/sks-keyservers.netCA.pem + + # Ignore keyserver URLs specified in retrieved/refreshed keys + # so they don't direct you to update from non-HKPS sources. + keyserver-options no-honor-keyserver-url + + # Display key IDs in a more accurate 16-digit hexidecimal format + # and add 0x at the beginning for clarity. + keyid-format 0xlong + + # Display the calculated validity of user IDs when listing keys or + # showing signatures. + list-options show-uid-validity + verify-options show-uid-validity + + +Generation +---------- + +Key generation should happen reasonably far in advance of expiration +of the old key (at least a month), so as to provide ample time for a +majority of our root sysadmins to attest to the key and provide +warning to the rest of the community of the upcoming transition. Of +course, if this is being done to replace a revoked key, this +timeline should be accelerated as much as possible to provide +continuity of service so use your best judgement on a balance of +sufficient attestation and warning (same-day turnaround is +preferred). + +Make sure we start with a restrictive umask so that files and +directories we write from this point forward are only accessible by +the root user: + +.. code-block:: shell-session + + root@puppetmaster:~# umask 077 + +Now create a master key for the coming development cycle, taking +mostly the GnuPG recommended default values. Set a validity period +sufficient to last through the release process at the conclusion of +the cycle. Use a sufficiently long, randomly-generated passphrase +string (it's fine to reuse the one stored in our passwords list for +earlier keys unless we know it to have been compromised): + +.. code-block:: shell-session + + root@puppetmaster:~# gpg --homedir signing.gnupg --gen-key + gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Please select what kind of key you want: + (1) RSA and RSA (default) + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) + Your selection? + RSA keys may be between 1024 and 4096 bits long. + What keysize do you want? (2048) + Requested keysize is 2048 bits + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) 7m + Key expires at Thu 02 Feb 2017 08:41:39 PM UTC + Is this correct? (y/N) y + + You need a user ID to identify your key; the software constructs the user ID + from the Real Name, Comment and Email Address in this form: + "Heinrich Heine (Der Dichter) " + + Real name: OpenStack Infra + Email address: infra-root@openstack.org + Comment: Some Cycle + You selected this USER-ID: + "OpenStack Infra (Some Cycle) " + + Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o + You need a Passphrase to protect your secret key. + + Enter passphrase: ******************************** + Repeat passphrase: ******************************** + + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + .+++++ + ......+++++ + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + .+++++ + +++++ + gpg: key 0x120D3C23C6D5584D marked as ultimately trusted + public and secret key created and signed. + + gpg: checking the trustdb + gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model + gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u + gpg: next trustdb check due at 2017-02-02 + pub 2048R/0x120D3C23C6D5584D 2016-07-07 [expires: 2017-02-02] + Key fingerprint = 7222 E5A0 5730 B767 0F93 035A 120D 3C23 C6D5 584D + uid [ultimate] OpenStack Infra (Some Cycle) + sub 2048R/0x1F215B56867C5D9A 2016-07-07 [expires: 2017-02-02] + +Create a revocation certificate for the master key, for use in the +case extreme case that this master key itself becomes inaccessible, +for example because the decryption passphrase is lost (under any +other circumstances, a revocation certificate with a more detailed +description can be generated using the master key on an as-needed +basis): + +.. code-block:: shell-session + + root@puppetmaster:~# gpg --homedir signing.gnupg --output \ + > signing.gnupg/revoke.asc --gen-revoke 0x120D3C23C6D5584D + sec 2048R/0x120D3C23C6D5584D 2016-07-07 OpenStack Infra (Some Cycle) + + Create a revocation certificate for this key? (y/N) y + Please select the reason for the revocation: + 0 = No reason specified + 1 = Key has been compromised + 2 = Key is superseded + 3 = Key is no longer used + Q = Cancel + (Probably you want to select 1 here) + Your decision? 1 + Enter an optional description; end it with an empty line: + > This revocation is to be used in the event the key cannot be recovered. + > + Reason for revocation: Key has been compromised + This revocation is to be used in the event the key cannot be recovered. + Is this okay? (y/N) y + + You need a passphrase to unlock the secret key for + user: "OpenStack Infra (Some Cycle) " + 2048-bit RSA key, ID 0x120D3C23C6D5584D, created 2016-07-07 + + Enter passphrase: ******************************** + + ASCII armored output forced. + Revocation certificate created. + + Please move it to a medium which you can hide away; if Mallory gets + access to this certificate he can use it to make your key unusable. + It is smart to print this certificate and store it away, just in case + your media become unreadable. But have some caution: The print system of + your machine might store the data and make it available to others! + +Use the interactive key editor to add a subkey constrained to +signing purposes only. It does not need an expiration since it will +be valid only for as long as its associated master key is valid: + +.. code-block:: shell-session + + root@puppetmaster:~# gpg --homedir signing.gnupg --edit-key 0x120D3C23C6D5584D + gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + pub 2048R/0x120D3C23C6D5584D created: 2016-07-07 expires: 2017-02-02 usage: SC + trust: ultimate validity: ultimate + sub 2048R/0x1F215B56867C5D9A created: 2016-07-07 expires: 2017-02-02 usage: E + [ultimate] (1). OpenStack Infra (Some Cycle) + + gpg> addkey + Key is protected. + + You need a passphrase to unlock the secret key for + user: "OpenStack Infra (Some Cycle) " + 2048-bit RSA key, ID 0x120D3C23C6D5584D, created 2016-07-07 + + Enter passphrase: ******************************** + + Please select what kind of key you want: + (3) DSA (sign only) + (4) RSA (sign only) + (5) Elgamal (encrypt only) + (6) RSA (encrypt only) + Your selection? 4 + RSA keys may be between 1024 and 4096 bits long. + What keysize do you want? (2048) + Requested keysize is 2048 bits + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) + Key does not expire at all + Is this correct? (y/N) y + Really create? (y/N) y + We need to generate a lot of random bytes. It is a good idea to perform + some other action (type on the keyboard, move the mouse, utilize the + disks) during the prime generation; this gives the random number + generator a better chance to gain enough entropy. + +++++ + ........+++++ + + pub 2048R/0x120D3C23C6D5584D created: 2016-07-07 expires: 2017-02-02 usage: SC + trust: ultimate validity: ultimate + sub 2048R/0x1F215B56867C5D9A created: 2016-07-07 expires: 2017-02-02 usage: E + sub 2048R/0xC0224DB5F541FB68 created: 2016-07-07 expires: never usage: S + [ultimate] (1). OpenStack Infra (Some Cycle) + + gpg> save + +Now send the master key to the keyserver network. The subkeys are +all submitted along with it, so do not need to be specified +separately: + +.. code-block:: shell-session + + root@puppetmaster:~# gpg --homedir signing.gnupg --send-keys 0x120D3C23C6D5584D + sending key 0x120D3C23C6D5584D to hkps server hkps.pool.sks-keyservers.net + +The rest of this process shouldn't happen until we're ready for the +signing system to transition to our new key. In a typical, +non-emergency rotation this should not happen until release +activities for the previous cycle have concluded so that we don't +inadvertently sign their artifacts with the new key. + +Create a new GnuPG keychain by exporting a copy of just the signing +subkey to a file and then importing that (and only that) in a new +GnuPG directory: + +.. code-block:: shell-session + + root@puppetmaster:~# mkdir temporary.gnupg + root@puppetmaster:~# gpg --homedir signing.gnupg --output \ + > temporary.gnupg/secret-subkeys --export-secret-subkeys 0xC0224DB5F541FB68\! + root@puppetmaster:~# gpg --homedir temporary.gnupg --import \ + > temporary.gnupg/secret-subkeys + gpg: keyring `temporary.gnupg/secring.gpg' created + gpg: keyring `temporary.gnupg/pubring.gpg' created + gpg: key C6D5584D: secret key imported + gpg: temporary.gnupg/trustdb.gpg: trustdb created + gpg: key C6D5584D: public key "OpenStack Infra (Some Cycle) " imported + gpg: Total number processed: 1 + gpg: imported: 1 (RSA: 1) + gpg: secret keys read: 1 + gpg: secret keys imported: 1 + +So that our CI jobs will be able to make use of this subkey without +interactively supplying a passphrase, the old passphrase (exported +from the master key) must be reset to an empty string in the new +temporary copy. This is again done using an interactive key editor +session: + +.. code-block:: shell-session + + root@puppetmaster:~# gpg --homedir temporary.gnupg --edit-key 0xC0224DB5F541FB68 + gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Secret key is available. + + pub 2048R/C6D5584D created: 2016-07-07 expires: 2017-02-02 usage: SC + trust: unknown validity: unknown + sub 2048R/F541FB68 created: 2016-07-07 expires: never usage: S + [ unknown] (1). OpenStack Infra (Some Cycle) + + gpg> passwd + Secret parts of primary key are not available. + + You need a passphrase to unlock the secret key for + user: "OpenStack Infra (Some Cycle) " + 2048-bit RSA key, ID F541FB68, created 2016-07-07 + + Enter passphrase: ******************************** + + Enter the new passphrase for this secret key. + + Enter passphrase: + Repeat passphrase: + + You don't want a passphrase - this is probably a *bad* idea! + + Do you really want to do this? (y/N) y + + gpg> save + +This leaves us with a temporary keyring containing only an +unencrypted copy of the signing subkey. Push this into private hiera +so that it will be installed onto the signing system by our +configuration management: + +.. code-block:: shell-session + + root@puppetmaster:~# /opt/system-config/production/tools/hieraedit.py --yaml \ + > /opt/system-config/hieradata/production/group/signing.yaml -f \ + > temporary.gnupg/pubring.gpg pubring + root@puppetmaster:~# /opt/system-config/production/tools/hieraedit.py --yaml \ + > /opt/system-config/hieradata/production/group/signing.yaml -f \ + > temporary.gnupg/secring.gpg secring + +Finally, do your best to securely remove the temporary copy of the +unencrypted signing subkey and any associated files: + +.. code-block:: shell-session + + root@puppetmaster:~# shred temporary.gnupg/* + root@puppetmaster:~# rm -rf temporary.gnupg + + +Attestation +----------- + +We need a majority (if not all) of our current root sysadmins to +verify and attest to the authenticity of our artifact signing key, +because it represents a system maintained by our team rather than +representing some particular individual and so anyone else attesting +to this key can really only do so transitively through us. This +should be done soon after a new key is minted (preferably the same +week) so that others in the community who wish to extend the web of +trust around the key based on our attestations (for example, release +managers or team leads) have an opportunity to do so before it's put +into production. + +Start by logging into the management bastion and examining the +fingerprint of the key as it exists on disk: + +.. code-block:: shell-session + + me@puppetmaster:~$ sudo gpg --homedir /root/signing.gnupg --fingerprint \ + > --list-keys "OpenStack Infra (Some Cycle)" + pub 2048R/0x120D3C23C6D5584D 2016-07-07 [expires: 2017-02-02] + Key fingerprint = 120D 3C23 C6D5 584D 6FC2 4646 64DB B05A CC5E 7C28 + uid [ultimate] OpenStack Infra (Some Cycle) + sub 2048R/0x1F215B56867C5D9A 2016-07-07 [expires: 2017-02-02] + sub 2048R/0xC0224DB5F541FB68 2016-07-07 + +Now on your own system where your OpenPGP key resides, retrieve the +key, compare the fingerprint from above, and if they match, sign it +and push the signature back to the keyserver network: + +.. code-block:: shell-session + + me@home:~$ gpg2 --recv-keys 0x120D3C23C6D5584D + gpg: requesting key 0x120D3C23C6D5584D from hkps server hkps.pool.sks-keyservers.net + gpg: key 0x120D3C23C6D5584D: public key "OpenStack Infra (Some Cycle) " imported + gpg: 3 marginal(s) needed, 1 complete(s) needed, classic trust model + gpg: depth: 0 valid: 3 signed: 31 trust: 0-, 0q, 0n, 0m, 0f, 3u + gpg: depth: 1 valid: 31 signed: 46 trust: 30-, 0q, 0n, 0m, 1f, 0u + gpg: next trustdb check due at 2016-11-30 + gpg: Total number processed: 1 + gpg: imported: 1 (RSA: 1) + me@home:~$ gpg2 --fingerprint 0x120D3C23C6D5584D + pub 2048R/0x120D3C23C6D5584D 2016-07-07 [expires: 2017-02-02] + Key fingerprint = 120D 3C23 C6D5 584D 6FC2 4646 64DB B05A CC5E 7C28 + uid [ full ] OpenStack Infra (Some Cycle) + sub 2048R/0x1F215B56867C5D9A 2016-07-07 [expires: 2017-02-02] + sub 2048R/0xC0224DB5F541FB68 2016-07-07 + me@home:~$ gpg2 --sign-key 0x120D3C23C6D5584D + + pub 2048R/0x120D3C23C6D5584D created: 2016-07-07 expires: 2017-02-02 usage: SC + trust: unknown validity: full + sub 2048R/0x1F215B56867C5D9A created: 2016-07-07 expires: 2017-02-02 usage: E + sub 2048R/0xC0224DB5F541FB68 created: 2016-07-07 expires: never usage: S + [ full ] (1). OpenStack Infra (Some Cycle) + + + pub 2048R/0x120D3C23C6D5584D created: 2016-07-07 expires: 2017-02-02 usage: SC + trust: unknown validity: full + Primary key fingerprint: 120D 3C23 C6D5 584D 6FC2 4646 64DB B05A CC5E 7C28 + + OpenStack Infra (Some Cycle) + + This key is due to expire on 2017-02-02. + Are you sure that you want to sign this key with your + key "My Name " (0xAB54A98CEB1F0AD2) + + Really sign? (y/N) y + + +-----------------------------------------------------------------------+ + | Please enter the passphrase to unlock the secret key for the OpenPGP | + | certificate: | + | "My Name " | + | 2048-bit RSA key, ID 0xAB54A98CEB1F0AD2, | + | created 2008-09-10. | + | | + | | + | Passphrase **********************____________________________________ | + | | + | | + +-----------------------------------------------------------------------+ + + me@home:~$ gpg2 --send-keys 0x120D3C23C6D5584D + gpg: sending key 0x120D3C23C6D5584D to hkps server hkps.pool.sks-keyservers.net + +Also, please retrieve a copy of the +``/root/signing.gnupg/revoke.asc`` fallback revocation certificate +from the management bastion and keep it stashed somewhere secure, +for emergency use in the (hopefully very unlikely) event that our +OpenPGP master private key is completely lost to us (for example, if +we lose the file containing its decryption passphrase and all +backups thereof).