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>
This commit is contained in:
parent
f6febd2ef5
commit
6f534acbef
@ -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:
|
||||
|
||||
-------------
|
||||
|
@ -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).
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
|
@ -336,10 +336,12 @@ def public_keys(self, *args):
|
||||
*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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user