diff --git a/lower-constraints.txt b/lower-constraints.txt index 6cb1b44fc..db92fef36 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -50,7 +50,7 @@ msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -openstacksdk==0.11.2 +openstacksdk==0.17.0 os-client-config==1.28.0 os-service-types==1.2.0 os-testr==1.0.0 diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 3d9c199d1..1e67692a5 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -16,9 +16,11 @@ """Image V2 Action Implementations""" import argparse +from base64 import b64encode import logging from glanceclient.common import utils as gc_utils +from openstack.image import image_signer from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions @@ -183,6 +185,22 @@ class CreateImage(command.ShowOne): help=_("Force image creation if volume is in use " "(only meaningful with --volume)"), ) + parser.add_argument( + '--sign-key-path', + metavar="", + default=[], + help=_("Sign the image using the specified private key. " + "Only use in combination with --sign-cert-id") + ) + parser.add_argument( + '--sign-cert-id', + metavar="", + default=[], + help=_("The specified certificate UUID is a reference to " + "the certificate in the key manager that corresponds " + "to the public key and is used for signature validation. " + "Only use in combination with --sign-key-path") + ) protected_group = parser.add_mutually_exclusive_group() protected_group.add_argument( "--protected", @@ -335,6 +353,46 @@ class CreateImage(command.ShowOne): parsed_args.project_domain, ).id + # sign an image using a given local private key file + if parsed_args.sign_key_path or parsed_args.sign_cert_id: + if not parsed_args.file: + msg = (_("signing an image requires the --file option, " + "passing files via stdin when signing is not " + "supported.")) + raise exceptions.CommandError(msg) + if (len(parsed_args.sign_key_path) < 1 or + len(parsed_args.sign_cert_id) < 1): + msg = (_("'sign-key-path' and 'sign-cert-id' must both be " + "specified when attempting to sign an image.")) + raise exceptions.CommandError(msg) + else: + sign_key_path = parsed_args.sign_key_path + sign_cert_id = parsed_args.sign_cert_id + signer = image_signer.ImageSigner() + try: + pw = utils.get_password( + self.app.stdin, + prompt=("Please enter private key password, leave " + "empty if none: "), + confirm=False) + if not pw or len(pw) < 1: + pw = None + signer.load_private_key( + sign_key_path, + password=pw) + except Exception: + msg = (_("Error during sign operation: private key could " + "not be loaded.")) + raise exceptions.CommandError(msg) + + signature = signer.generate_signature(fp) + signature_b64 = b64encode(signature) + kwargs['img_signature'] = signature_b64 + kwargs['img_signature_certificate_uuid'] = sign_cert_id + kwargs['img_signature_hash_method'] = signer.hash_method + if signer.padding_method: + kwargs['img_signature_key_type'] = signer.padding_method + # If a volume is specified. if parsed_args.volume: volume_client = self.app.client_manager.volume diff --git a/releasenotes/notes/osc-included-image-signing-a7021a4dbdcf6336.yaml b/releasenotes/notes/osc-included-image-signing-a7021a4dbdcf6336.yaml new file mode 100644 index 000000000..7c7a2d616 --- /dev/null +++ b/releasenotes/notes/osc-included-image-signing-a7021a4dbdcf6336.yaml @@ -0,0 +1,11 @@ +--- +features: + - | + Add options ``--sign-key-path`` and ``--sign-cert-id`` to the ``image create`` + command. Tthe image must be present on disk, therefore the ``file`` option + is required: + + ``image create --file --sign-key-path --sign-cert-id ``. + + A prompt for a password ensures, that the private key can be protected too. + [Bug `2002128 `_] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2ee03a5cd..205d4e64e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ six>=1.10.0 # MIT Babel!=2.4.0,>=2.3.4 # BSD cliff!=2.9.0,>=2.8.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 -openstacksdk>=0.11.2 # Apache-2.0 +openstacksdk>=0.17.0 # Apache-2.0 osc-lib>=1.10.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0