|
16 | 16 | """Image V2 Action Implementations""" |
17 | 17 |
|
18 | 18 | import argparse |
| 19 | +from base64 import b64encode |
19 | 20 | import logging |
20 | 21 |
|
21 | 22 | from glanceclient.common import utils as gc_utils |
| 23 | +from openstack.image import image_signer |
22 | 24 | from osc_lib.cli import parseractions |
23 | 25 | from osc_lib.command import command |
24 | 26 | from osc_lib import exceptions |
@@ -183,6 +185,22 @@ def get_parser(self, prog_name): |
183 | 185 | help=_("Force image creation if volume is in use " |
184 | 186 | "(only meaningful with --volume)"), |
185 | 187 | ) |
| 188 | + parser.add_argument( |
| 189 | + '--sign-key-path', |
| 190 | + metavar="<sign-key-path>", |
| 191 | + default=[], |
| 192 | + help=_("Sign the image using the specified private key. " |
| 193 | + "Only use in combination with --sign-cert-id") |
| 194 | + ) |
| 195 | + parser.add_argument( |
| 196 | + '--sign-cert-id', |
| 197 | + metavar="<sign-cert-id>", |
| 198 | + default=[], |
| 199 | + help=_("The specified certificate UUID is a reference to " |
| 200 | + "the certificate in the key manager that corresponds " |
| 201 | + "to the public key and is used for signature validation. " |
| 202 | + "Only use in combination with --sign-key-path") |
| 203 | + ) |
186 | 204 | protected_group = parser.add_mutually_exclusive_group() |
187 | 205 | protected_group.add_argument( |
188 | 206 | "--protected", |
@@ -335,6 +353,46 @@ def take_action(self, parsed_args): |
335 | 353 | parsed_args.project_domain, |
336 | 354 | ).id |
337 | 355 |
|
| 356 | + # sign an image using a given local private key file |
| 357 | + if parsed_args.sign_key_path or parsed_args.sign_cert_id: |
| 358 | + if not parsed_args.file: |
| 359 | + msg = (_("signing an image requires the --file option, " |
| 360 | + "passing files via stdin when signing is not " |
| 361 | + "supported.")) |
| 362 | + raise exceptions.CommandError(msg) |
| 363 | + if (len(parsed_args.sign_key_path) < 1 or |
| 364 | + len(parsed_args.sign_cert_id) < 1): |
| 365 | + msg = (_("'sign-key-path' and 'sign-cert-id' must both be " |
| 366 | + "specified when attempting to sign an image.")) |
| 367 | + raise exceptions.CommandError(msg) |
| 368 | + else: |
| 369 | + sign_key_path = parsed_args.sign_key_path |
| 370 | + sign_cert_id = parsed_args.sign_cert_id |
| 371 | + signer = image_signer.ImageSigner() |
| 372 | + try: |
| 373 | + pw = utils.get_password( |
| 374 | + self.app.stdin, |
| 375 | + prompt=("Please enter private key password, leave " |
| 376 | + "empty if none: "), |
| 377 | + confirm=False) |
| 378 | + if not pw or len(pw) < 1: |
| 379 | + pw = None |
| 380 | + signer.load_private_key( |
| 381 | + sign_key_path, |
| 382 | + password=pw) |
| 383 | + except Exception: |
| 384 | + msg = (_("Error during sign operation: private key could " |
| 385 | + "not be loaded.")) |
| 386 | + raise exceptions.CommandError(msg) |
| 387 | + |
| 388 | + signature = signer.generate_signature(fp) |
| 389 | + signature_b64 = b64encode(signature) |
| 390 | + kwargs['img_signature'] = signature_b64 |
| 391 | + kwargs['img_signature_certificate_uuid'] = sign_cert_id |
| 392 | + kwargs['img_signature_hash_method'] = signer.hash_method |
| 393 | + if signer.padding_method: |
| 394 | + kwargs['img_signature_key_type'] = signer.padding_method |
| 395 | + |
338 | 396 | # If a volume is specified. |
339 | 397 | if parsed_args.volume: |
340 | 398 | volume_client = self.app.client_manager.volume |
|
0 commit comments