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>`_
|
<https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged>`_
|
||||||
provides a good overview of sources of randomness.
|
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
|
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
|
``spack gpg list`` command will list trusted keys with the ``--trusted`` flag
|
||||||
and keys available for signing using ``--signing``. If you would like to
|
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
|
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
|
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
|
is not recommended. Signed packages may be verified by using
|
||||||
``spack gpg verify <file>``.
|
``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:
|
.. _cray-support:
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
|
@ -1558,7 +1558,9 @@ def push_keys(*mirrors, **kwargs):
|
|||||||
filename = fingerprint + '.pub'
|
filename = fingerprint + '.pub'
|
||||||
|
|
||||||
export_target = os.path.join(prefix, filename)
|
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
|
# If mirror is local, the above export writes directly to the
|
||||||
# mirror (export_target points directly to the mirror).
|
# mirror (export_target points directly to the mirror).
|
||||||
|
@ -60,6 +60,9 @@ def setup_parser(subparser):
|
|||||||
default='0', help='when the key should expire')
|
default='0', help='when the key should expire')
|
||||||
create.add_argument('--export', metavar='DEST', type=str,
|
create.add_argument('--export', metavar='DEST', type=str,
|
||||||
help='export the public key to a file')
|
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)
|
create.set_defaults(func=gpg_create)
|
||||||
|
|
||||||
list = subparsers.add_parser('list', help=gpg_list.__doc__)
|
list = subparsers.add_parser('list', help=gpg_list.__doc__)
|
||||||
@ -79,7 +82,9 @@ def setup_parser(subparser):
|
|||||||
help='where to export keys')
|
help='where to export keys')
|
||||||
export.add_argument('keys', nargs='*',
|
export.add_argument('keys', nargs='*',
|
||||||
help='the keys to export; '
|
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)
|
export.set_defaults(func=gpg_export)
|
||||||
|
|
||||||
publish = subparsers.add_parser('publish', help=gpg_publish.__doc__)
|
publish = subparsers.add_parser('publish', help=gpg_publish.__doc__)
|
||||||
@ -112,22 +117,28 @@ def setup_parser(subparser):
|
|||||||
|
|
||||||
def gpg_create(args):
|
def gpg_create(args):
|
||||||
"""create a new key"""
|
"""create a new key"""
|
||||||
if args.export:
|
if args.export or args.secret:
|
||||||
old_sec_keys = spack.util.gpg.signing_keys()
|
old_sec_keys = spack.util.gpg.signing_keys()
|
||||||
|
|
||||||
|
# Create the new key
|
||||||
spack.util.gpg.create(name=args.name, email=args.email,
|
spack.util.gpg.create(name=args.name, email=args.email,
|
||||||
comment=args.comment, expires=args.expires)
|
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_sec_keys = set(spack.util.gpg.signing_keys())
|
||||||
new_keys = new_sec_keys.difference(old_sec_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):
|
def gpg_export(args):
|
||||||
"""export a secret key"""
|
"""export a gpg key, optionally including secret key."""
|
||||||
keys = args.keys
|
keys = args.keys
|
||||||
if not keys:
|
if not keys:
|
||||||
keys = spack.util.gpg.signing_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):
|
def gpg_list(args):
|
||||||
|
@ -117,6 +117,20 @@ def test_gpg(tmpdir, mock_gnupghome):
|
|||||||
export_path = tmpdir.join('export.testing.key')
|
export_path = tmpdir.join('export.testing.key')
|
||||||
gpg('export', str(export_path))
|
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.
|
# Create a second key for use in the tests.
|
||||||
gpg('create',
|
gpg('create',
|
||||||
'--comment', 'Spack testing key',
|
'--comment', 'Spack testing key',
|
||||||
|
@ -336,10 +336,12 @@ def public_keys(self, *args):
|
|||||||
*args, output=str)
|
*args, output=str)
|
||||||
return parse_public_keys_output(output)
|
return parse_public_keys_output(output)
|
||||||
|
|
||||||
def export_keys(self, location, *keys):
|
def export_keys(self, location, keys, secret=False):
|
||||||
self('--batch', '--yes',
|
if secret:
|
||||||
'--armor', '--export',
|
self("--export-secret-keys", "--armor", "--output", location, *keys)
|
||||||
'--output', location, *keys)
|
else:
|
||||||
|
self('--batch', '--yes', '--armor', '--export', '--output',
|
||||||
|
location, *keys)
|
||||||
|
|
||||||
def trust(self, keyfile):
|
def trust(self, keyfile):
|
||||||
self('--import', keyfile)
|
self('--import', keyfile)
|
||||||
|
@ -1010,7 +1010,7 @@ _spack_gpg_sign() {
|
|||||||
_spack_gpg_create() {
|
_spack_gpg_create() {
|
||||||
if $list_options
|
if $list_options
|
||||||
then
|
then
|
||||||
SPACK_COMPREPLY="-h --help --comment --expires --export"
|
SPACK_COMPREPLY="-h --help --comment --expires --export --export-secret"
|
||||||
else
|
else
|
||||||
SPACK_COMPREPLY=""
|
SPACK_COMPREPLY=""
|
||||||
fi
|
fi
|
||||||
@ -1027,7 +1027,7 @@ _spack_gpg_init() {
|
|||||||
_spack_gpg_export() {
|
_spack_gpg_export() {
|
||||||
if $list_options
|
if $list_options
|
||||||
then
|
then
|
||||||
SPACK_COMPREPLY="-h --help"
|
SPACK_COMPREPLY="-h --help --secret"
|
||||||
else
|
else
|
||||||
_keys
|
_keys
|
||||||
fi
|
fi
|
||||||
|
Loading…
Reference in New Issue
Block a user