summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVanessasaurus <814322+vsoch@users.noreply.github.com>2021-05-29 00:32:57 -0600
committerGitHub <noreply@github.com>2021-05-28 23:32:57 -0700
commit6f534acbefa1ff1d0b389f72221a8dbdd87609b0 (patch)
tree7ebb7f195507e8088d9fd334395653ffca753bde
parentf6febd2ef5058a265cbc389c2d05aa43e9678d81 (diff)
downloadspack-6f534acbefa1ff1d0b389f72221a8dbdd87609b0.tar.gz
spack-6f534acbefa1ff1d0b389f72221a8dbdd87609b0.tar.bz2
spack-6f534acbefa1ff1d0b389f72221a8dbdd87609b0.tar.xz
spack-6f534acbefa1ff1d0b389f72221a8dbdd87609b0.zip
adding support for export of private gpg key (#22557)
This PR allows users to `--export`, `--export-secret`, or both to export GPG keys from Spack. The docs are updated that include a warning that this usually does not need to be done. This addresses an issue brought up in slack, and also represented in #14721. Signed-off-by: vsoch <vsoch@users.noreply.github.com> Co-authored-by: vsoch <vsoch@users.noreply.github.com>
-rw-r--r--lib/spack/docs/getting_started.rst76
-rw-r--r--lib/spack/spack/binary_distribution.py4
-rw-r--r--lib/spack/spack/cmd/gpg.py23
-rw-r--r--lib/spack/spack/test/cmd/gpg.py14
-rw-r--r--lib/spack/spack/util/gpg.py10
-rwxr-xr-xshare/spack/spack-completion.bash4
6 files changed, 117 insertions, 14 deletions
diff --git a/lib/spack/docs/getting_started.rst b/lib/spack/docs/getting_started.rst
index 805149af90..fa77d74cd6 100644
--- a/lib/spack/docs/getting_started.rst
+++ b/lib/spack/docs/getting_started.rst
@@ -1119,6 +1119,33 @@ Secret keys may also be later exported using the
<https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged>`_
provides a good overview of sources of randomness.
+Here is an example of creating a key. Note that we provide a name for the key first
+(which we can use to reference the key later) and an email address:
+
+.. code-block:: console
+
+ $ spack gpg create dinosaur dinosaur@thedinosaurthings.com
+
+
+If you want to export the key as you create it:
+
+
+.. code-block:: console
+
+ $ spack gpg create --export key.pub dinosaur dinosaur@thedinosaurthings.com
+
+Or the private key:
+
+
+.. code-block:: console
+
+ $ spack gpg create --export-secret key.priv dinosaur dinosaur@thedinosaurthings.com
+
+
+You can include both ``--export`` and ``--export-secret``, each with
+an output file of choice, to export both.
+
+
^^^^^^^^^^^^
Listing keys
^^^^^^^^^^^^
@@ -1127,7 +1154,22 @@ In order to list the keys available in the keyring, the
``spack gpg list`` command will list trusted keys with the ``--trusted`` flag
and keys available for signing using ``--signing``. If you would like to
remove keys from your keyring, ``spack gpg untrust <keyid>``. Key IDs can be
-email addresses, names, or (best) fingerprints.
+email addresses, names, or (best) fingerprints. Here is an example of listing
+the key that we just created:
+
+.. code-block:: console
+
+ gpgconf: socketdir is '/run/user/1000/gnupg'
+ /home/spackuser/spack/opt/spack/gpg/pubring.kbx
+ ----------------------------------------------------------
+ pub rsa4096 2021-03-25 [SC]
+ 60D2685DAB647AD4DB54125961E09BB6F2A0ADCB
+ uid [ultimate] dinosaur (GPG created for Spack) <dinosaur@thedinosaurthings.com>
+
+
+Note that the name "dinosaur" can be seen under the uid, which is the unique
+id. We might need this reference if we want to export or otherwise reference the key.
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Signing and Verifying Packages
@@ -1142,6 +1184,38 @@ may also be used to create a signed file which contains the contents, but it
is not recommended. Signed packages may be verified by using
``spack gpg verify <file>``.
+
+^^^^^^^^^^^^^^
+Exporting Keys
+^^^^^^^^^^^^^^
+
+You likely might want to export a public key, and that looks like this. Let's
+use the previous example and ask spack to export the key with uid "dinosaur."
+We will provide an output location (typically a `*.pub` file) and the name of
+the key.
+
+.. code-block:: console
+
+ $ spack gpg export dinosaur.pub dinosaur
+
+You can then look at the created file, `dinosaur.pub`, to see the exported key.
+If you want to include the private key, then just add `--secret`:
+
+.. code-block:: console
+
+ $ spack gpg export --secret dinosaur.priv dinosaur
+
+This will write the private key to the file `dinosaur.priv`.
+
+.. warning::
+
+ You should be very careful about exporting private keys. You likely would
+ only want to do this in the context of moving your spack installation to
+ a different server, and wanting to preserve keys for a buildcache. If you
+ are unsure about exporting, you can ask your local system administrator
+ or for help on an issue or the Spack slack.
+
+
.. _cray-support:
-------------
diff --git a/lib/spack/spack/binary_distribution.py b/lib/spack/spack/binary_distribution.py
index ba4921b677..46ca4fe88a 100644
--- a/lib/spack/spack/binary_distribution.py
+++ b/lib/spack/spack/binary_distribution.py
@@ -1558,7 +1558,9 @@ def push_keys(*mirrors, **kwargs):
filename = fingerprint + '.pub'
export_target = os.path.join(prefix, filename)
- spack.util.gpg.export_keys(export_target, fingerprint)
+
+ # Export public keys (private is set to False)
+ spack.util.gpg.export_keys(export_target, [fingerprint])
# If mirror is local, the above export writes directly to the
# mirror (export_target points directly to the mirror).
diff --git a/lib/spack/spack/cmd/gpg.py b/lib/spack/spack/cmd/gpg.py
index e34c2c4c84..3a65f1abc3 100644
--- a/lib/spack/spack/cmd/gpg.py
+++ b/lib/spack/spack/cmd/gpg.py
@@ -60,6 +60,9 @@ def setup_parser(subparser):
default='0', help='when the key should expire')
create.add_argument('--export', metavar='DEST', type=str,
help='export the public key to a file')
+ create.add_argument('--export-secret', metavar="DEST", type=str,
+ dest="secret",
+ help='export the private key to a file.')
create.set_defaults(func=gpg_create)
list = subparsers.add_parser('list', help=gpg_list.__doc__)
@@ -79,7 +82,9 @@ def setup_parser(subparser):
help='where to export keys')
export.add_argument('keys', nargs='*',
help='the keys to export; '
- 'all secret keys if unspecified')
+ 'all public keys if unspecified')
+ export.add_argument('--secret', action='store_true',
+ help='export secret keys')
export.set_defaults(func=gpg_export)
publish = subparsers.add_parser('publish', help=gpg_publish.__doc__)
@@ -112,22 +117,28 @@ def setup_parser(subparser):
def gpg_create(args):
"""create a new key"""
- if args.export:
+ if args.export or args.secret:
old_sec_keys = spack.util.gpg.signing_keys()
+
+ # Create the new key
spack.util.gpg.create(name=args.name, email=args.email,
comment=args.comment, expires=args.expires)
- if args.export:
+ if args.export or args.secret:
new_sec_keys = set(spack.util.gpg.signing_keys())
new_keys = new_sec_keys.difference(old_sec_keys)
- spack.util.gpg.export_keys(args.export, *new_keys)
+
+ if args.export:
+ spack.util.gpg.export_keys(args.export, new_keys)
+ if args.secret:
+ spack.util.gpg.export_keys(args.secret, new_keys, secret=True)
def gpg_export(args):
- """export a secret key"""
+ """export a gpg key, optionally including secret key."""
keys = args.keys
if not keys:
keys = spack.util.gpg.signing_keys()
- spack.util.gpg.export_keys(args.location, *keys)
+ spack.util.gpg.export_keys(args.location, keys, args.secret)
def gpg_list(args):
diff --git a/lib/spack/spack/test/cmd/gpg.py b/lib/spack/spack/test/cmd/gpg.py
index 099d53e039..eb7eab2734 100644
--- a/lib/spack/spack/test/cmd/gpg.py
+++ b/lib/spack/spack/test/cmd/gpg.py
@@ -117,6 +117,20 @@ def test_gpg(tmpdir, mock_gnupghome):
export_path = tmpdir.join('export.testing.key')
gpg('export', str(export_path))
+ # Test exporting the private key
+ private_export_path = tmpdir.join('export-secret.testing.key')
+ gpg('export', '--secret', str(private_export_path))
+
+ # Ensure we exported the right content!
+ with open(str(private_export_path), 'r') as fd:
+ content = fd.read()
+ assert "BEGIN PGP PRIVATE KEY BLOCK" in content
+
+ # and for the public key
+ with open(str(export_path), 'r') as fd:
+ content = fd.read()
+ assert "BEGIN PGP PUBLIC KEY BLOCK" in content
+
# Create a second key for use in the tests.
gpg('create',
'--comment', 'Spack testing key',
diff --git a/lib/spack/spack/util/gpg.py b/lib/spack/spack/util/gpg.py
index aa376bc041..03b33af234 100644
--- a/lib/spack/spack/util/gpg.py
+++ b/lib/spack/spack/util/gpg.py
@@ -336,10 +336,12 @@ class Gpg(object):
*args, output=str)
return parse_public_keys_output(output)
- def export_keys(self, location, *keys):
- self('--batch', '--yes',
- '--armor', '--export',
- '--output', location, *keys)
+ def export_keys(self, location, keys, secret=False):
+ if secret:
+ self("--export-secret-keys", "--armor", "--output", location, *keys)
+ else:
+ self('--batch', '--yes', '--armor', '--export', '--output',
+ location, *keys)
def trust(self, keyfile):
self('--import', keyfile)
diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash
index 163a2ecf5f..6bf88fee60 100755
--- a/share/spack/spack-completion.bash
+++ b/share/spack/spack-completion.bash
@@ -1010,7 +1010,7 @@ _spack_gpg_sign() {
_spack_gpg_create() {
if $list_options
then
- SPACK_COMPREPLY="-h --help --comment --expires --export"
+ SPACK_COMPREPLY="-h --help --comment --expires --export --export-secret"
else
SPACK_COMPREPLY=""
fi
@@ -1027,7 +1027,7 @@ _spack_gpg_init() {
_spack_gpg_export() {
if $list_options
then
- SPACK_COMPREPLY="-h --help"
+ SPACK_COMPREPLY="-h --help --secret"
else
_keys
fi