feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The command group for keys."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.core import resources
class Keys(base.Group):
"""Create and manage keys.
A key represents a logical key that can be used for cryptographic
operations.
"""
category = base.IDENTITY_AND_SECURITY_CATEGORY
@classmethod
def Args(cls, parser):
parser.display_info.AddUriFunc(
cloudkms_base.MakeGetUriFunc(flags.CRYPTO_KEY_COLLECTION))

View File

@@ -0,0 +1,35 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding for a kms key.
description: |
Adds a policy binding to the IAM policy of a kms key. A binding consists of at least one
member, a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/editor' for the user 'test-user@gmail.com'
on the key frodo with the keyring fellowship and location global, run:
$ {command} frodo --keyring='fellowship' --location='global' --member='user:test-user@gmail.com' --role='roles/editor'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/cloudkms.signer' and the user 'test-user@gmail.com' on a the key frodo with the keyring
fellowship and location global, run:
$ {command} frodo --keyring='fellowship' --location='global' --member='user:test-user@gmail.com' --role='roles/cloudkms.signer' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: cloudkms.projects.locations.keyRings.cryptoKeys
arguments:
resource:
help_text: The key to add the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.kms.resources:key
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for managing Cloud KMS crypto key configurations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Config(base.Group):
"""Manage Cloud KMS crypto key configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Cloud KMS crypto key.
description: |
*{command}* exports the configuration for a Cloud KMS crypto key.
Crypto key configurations can be exported in
Kubernetes Resource Model (krm) or Terraform HCL formats. The
default format is `krm`.
Specifying `--all` allows you to export the configurations for all
crypto keys within the project.
Specifying `--path` allows you to export the configuration(s) to
a local directory.
examples: |
To export the configuration for a crypto key, run:
$ {command} my-crypto-key
To export the configuration for a crypto key to a file, run:
$ {command} my-crypto-key --path=/path/to/dir/
To export the configuration for a crypto key in Terraform
HCL format, run:
$ {command} my-crypto-key --resource-format=terraform
To export the configurations for all crypto keys within a
project, run:
$ {command} --all
arguments:
resource:
help_text: Crypto key to export the configuration for.
spec: !REF googlecloudsdk.command_lib.kms.resources:key

View File

@@ -0,0 +1,244 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Create a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import maps
from googlecloudsdk.command_lib.kms import resource_args
from googlecloudsdk.command_lib.util.args import labels_util
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Create(base.CreateCommand):
r"""Create a new key.
Creates a new key within the given keyring.
The flag `--purpose` is always required when creating a key.
The flag `--default-algorithm` is required when creating a symmetric signing
key, an asymmetric key, or an external key. Algorithm and purpose should be
compatible.
The optional flags `--rotation-period` and `--next-rotation-time` define a
rotation schedule for the key. A schedule can also be defined
by the `--create-rotation-schedule` command.
The flag `--next-rotation-time` must be in ISO 8601 or RFC3339 format,
and `rotation-period` must be in the form INTEGER[UNIT], where units
can be one of seconds (s), minutes (m), hours (h) or days (d).
The optional flag `--protection-level` specifies the physical environment
where crypto operations with the key happen. The default is ``software''; use
``hsm'' to create a hardware-backed key, ``external'' to create an
externally backed key, or ``external-vpc'' to create an external key over vpc.
The optional flag `--labels` defines a user specified key/value pair for the
given key.
The flag `--skip-initial-version-creation` creates a CryptoKey with no
versions. If you import into the CryptoKey, or create a new version in that
CryptoKey, there will be no primary version until one is set using the
`--set-primary-version` command. You must include
`--skip-initial-version-creation` when creating a CryptoKey with protection
level ``external'' or ``external-vpc''.
The optional flag `--import-only` restricts the key to imported key versions
only. To do so, the flag `--skip-initial-version-creation` must also be set.
The optional flag `--destroy-scheduled-duration` defines the destroy schedule
for the key, and must be in the form INTEGER[UNIT], where units can be one of
seconds (s), minutes (m), hours (h) or days (d).
The flag `--crypto-key-backend` defines the resource name for the
backend where the key resides. Required for ``external-vpc'' keys.
The optional flag `--allowed-access-reasons` defines the Key Access
Justifications Policy for the key, and is specified as a comma separated list
of zero or more justification codes defined in
https://cloud.google.com/assured-workloads/key-access-justifications/docs/justification-codes.
The key must be enrolled in Key Access Justifications to use this flag.
## EXAMPLES
The following command creates a key named ``frodo'' with protection level
``software'' within the keyring ``fellowship'' and location ``us-east1'':
$ {command} frodo \
--location=us-east1 \
--keyring=fellowship \
--purpose=encryption
The following command creates a key named ``strider'' with protection level
``software'' within the keyring ``rangers'' and location ``global'' with a
specified rotation schedule:
$ {command} strider \
--location=global --keyring=rangers \
--purpose=encryption \
--rotation-period=30d \
--next-rotation-time=2017-10-12T12:34:56.1234Z
The following command creates a raw encryption key named ``foo'' with
protection level ``software'' within the keyring ``fellowship'' and location
``us-east1'' with two specified labels:
$ {command} foo \
--location=us-east1 \
--keyring=fellowship \
--purpose=raw-encryption \
--default-algorithm=aes-128-cbc
--labels=env=prod,team=kms
The following command creates an asymmetric key named ``samwise'' with
protection level ``software'' and default algorithm ``ec-sign-p256-sha256''
within the keyring ``fellowship'' and location ``us-east1'':
$ {command} samwise \
--location=us-east1 \
--keyring=fellowship \
--purpose=asymmetric-signing \
--default-algorithm=ec-sign-p256-sha256
The following command creates a key named ``gimli'' with protection level
``hsm'' and default algorithm ``google-symmetric-encryption'' within the
keyring ``fellowship'' and location ``us-east1'':
$ {command} gimli \
--location=us-east1 \
--keyring=fellowship \
--purpose=encryption \
--protection-level=hsm
The following command creates a key named ``legolas'' with protection level
``external'' and default algorithm ``external-symmetric-encryption'' within
the keyring ``fellowship'' and location ``us-central1'':
$ {command} legolas \
--location=us-central1 \
--keyring=fellowship \
--purpose=encryption \
--default-algorithm=external-symmetric-encryption \
--protection-level=external
--skip-initial-version-creation
The following command creates a key named ``bilbo'' with protection level
``external-vpc'' and default algorithm ``external-symmetric-encryption'' and
an EkmConnection of ``eagles'' within the keyring ``fellowship'' and location
``us-central1'':
$ {command} bilbo \
--location=us-central1 \
--keyring=fellowship \
--purpose=encryption \
--default-algorithm=external-symmetric-encryption \
--protection-level=external-vpc
--skip-initial-version-creation
--crypto-key-backend="projects/$(gcloud config get project)/
locations/us-central1/ekmConnections/eagles"
The following command creates a key named ``arwen'' with protection level
``software'' within the keyring ``fellowship'' and location ``us-east1'' with
a Key Access Justifications policy that allows access reasons
``customer-initiated-access'' and ``google-initiated-system-operation'':
$ {command} arwen \
--location=us-east1 \
--keyring=fellowship \
--purpose=encryption \
--allowed-access-reasons=customer-initiated-access,google-initiated-system-operation
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
flags.AddRotationPeriodFlag(parser)
flags.AddNextRotationTimeFlag(parser)
flags.AddSkipInitialVersionCreationFlag(parser)
labels_util.AddCreateLabelsFlags(parser)
parser.add_argument(
'--purpose',
choices=sorted(maps.PURPOSE_MAP.keys()),
required=True,
help='The "purpose" of the key.')
parser.display_info.AddCacheUpdater(flags.KeyRingCompleter)
flags.AddProtectionLevelFlag(parser)
flags.AddDefaultAlgorithmFlag(parser)
flags.AddImportOnlyFlag(parser)
flags.AddDestroyScheduledDurationFlag(parser)
flags.AddCryptoKeyBackendFlag(parser)
flags.AddAllowedAccessReasonsFlag(parser)
def _CreateRequest(self, args):
messages = cloudkms_base.GetMessagesModule()
purpose = maps.PURPOSE_MAP[args.purpose]
valid_algorithms = maps.VALID_ALGORITHMS_MAP[purpose]
# Check default algorithm has been specified for non-symmetric-encryption
# keys. For backward compatibility, the algorithm is
# google-symmetric-encryption by default if the purpose is encryption.
if not args.default_algorithm:
if args.purpose != 'encryption':
raise kms_exceptions.ArgumentError(
'--default-algorithm needs to be specified when creating a key with'
' --purpose={}. The valid algorithms are: {}'.format(
args.purpose, ', '.join(valid_algorithms)))
args.default_algorithm = 'google-symmetric-encryption'
# Check default algorithm and purpose are compatible.
if args.default_algorithm not in valid_algorithms:
raise kms_exceptions.ArgumentError(
'Default algorithm and purpose are incompatible. Here are the valid '
'algorithms for --purpose={}: {}'.format(args.purpose,
', '.join(valid_algorithms)))
crypto_key_ref = args.CONCEPTS.key.Parse()
parent_ref = crypto_key_ref.Parent()
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCreateRequest(
parent=parent_ref.RelativeName(),
cryptoKeyId=crypto_key_ref.Name(),
cryptoKey=messages.CryptoKey(
purpose=purpose,
versionTemplate=messages.CryptoKeyVersionTemplate(
protectionLevel=maps.PROTECTION_LEVEL_MAPPER.GetEnumForChoice(
args.protection_level),
algorithm=maps.ALGORITHM_MAPPER.GetEnumForChoice(
args.default_algorithm)),
labels=labels_util.ParseCreateArgs(args,
messages.CryptoKey.LabelsValue),
importOnly=args.import_only,
cryptoKeyBackend=args.crypto_key_backend),
skipInitialVersionCreation=args.skip_initial_version_creation)
flags.SetNextRotationTime(args, req.cryptoKey)
flags.SetRotationPeriod(args, req.cryptoKey)
flags.SetDestroyScheduledDuration(args, req.cryptoKey)
flags.SetKeyAccessJustificationsPolicy(args, req.cryptoKey)
return req
def Run(self, args):
client = cloudkms_base.GetClientInstance()
return client.projects_locations_keyRings_cryptoKeys.Create(
self._CreateRequest(args))

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Describe a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import resource_args
class Describe(base.DescribeCommand):
"""Get metadata for a given key.
Returns metadata for the given key.
## EXAMPLES
The following command returns metadata for the key `frodo` within
the keyring `fellowship` in the location `us-east1`:
$ {command} frodo --keyring=fellowship --location=us-east1
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = args.CONCEPTS.key.Parse()
if not crypto_key_ref.Name():
raise exceptions.InvalidArgumentException('key',
'key id must be non-empty.')
resp = client.projects_locations_keyRings_cryptoKeys.Get(
messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysGetRequest(
name=crypto_key_ref.RelativeName()))
# Suppress the attestation in the response, if there is one. Users can use
# keys versions describe --attestation-file to obtain it, instead.
if resp.primary and resp.primary.attestation:
resp.primary.attestation = None
return resp

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fetch the IAM policy for a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import iam
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import resource_args
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for a key.
Gets the IAM policy for the given key.
Returns an empty policy if the resource does not have a policy
set.
## EXAMPLES
The following command gets the IAM policy for the key `frodo` within
the keyring `fellowship` and location `global`:
$ {command} frodo --keyring=fellowship --location=global
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
return iam.GetCryptoKeyIamPolicy(flags.ParseCryptoKeyName(args))

View File

@@ -0,0 +1,27 @@
- release_tracks: [ALPHA]
help_text:
brief: Get the IAM policy for a key.
description: |
*{command}* displays the IAM policy associated with a key.
If formatted as JSON, the output can be edited and used as
a policy file for *set-iam-policy*. The output includes an "etag"
field identifying the version emitted and allowing detection of
concurrent policy updates;
see $ {parent_command} set-iam-policy for additional details.
examples: |
To print the IAM policy for a given cluster, run:
$ {command} --keyring=my-key-ring --location=my-location my-key
request:
collection: cloudkms.projects.locations.keyRings.cryptoKeys
arguments:
resource:
help_text: The key for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.kms.resources:key
iam:
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""List the keys within a keyring."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import resource_args
class List(base.ListCommand):
"""List the keys within a keyring.
Lists all keys within the given keyring.
## EXAMPLES
The following command lists all keys within the
keyring `fellowship` and location `global`:
$ {command} --keyring=fellowship --location=global
"""
@staticmethod
def Args(parser):
# The format of a CryptoKeyVersion name is:
# 'projects/*/locations/*/keyRings/*/cryptoKeys/*/cryptoKeyVersions/*'
# The CryptoKeyVersionId is captured by segment(9).
resource_args.AddKmsKeyringResourceArgForKMS(parser, True, '--keyring')
parser.display_info.AddFormat("""
table(
name,
purpose,
version_template.algorithm,
version_template.protection_level,
labels.list(),
primary.name.segment(9):label=PRIMARY_ID,
primary.state:label=PRIMARY_STATE)
""")
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
key_ring_ref = args.CONCEPTS.keyring.Parse()
request = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysListRequest(
parent=key_ring_ref.RelativeName())
return list_pager.YieldFromList(
client.projects_locations_keyRings_cryptoKeys,
request,
field='cryptoKeys',
limit=args.limit,
batch_size_attribute='pageSize')

View File

@@ -0,0 +1,42 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding for a kms key.
description: |
Removes a policy binding from the IAM policy of a kms key. A binding consists of at least one
member, a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of 'roles/editor' for the user
'test-user@gmail.com' on the key frodo with the keyring fellowship and location global, run:
$ {command} frodo --keyring='fellowship' --location='global' --member='user:test-user@gmail.com' --role='roles/editor'
To remove an IAM policy binding with a condition of
expression='request.time < timestamp("2019-01-01T00:00:00Z")', title='expires_end_of_2018',
and description='Expires at midnight on 2018-12-31' for the role of 'roles/cloudkms.signer'
for the user 'test-user@gmail.com' on a the key frodo with the keyring fellowship and location
global, run:
$ {command} frodo --keyring='fellowship' --location='global' --member='user:test-user@gmail.com' --role='roles/cloudkms.signer' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
To remove all IAM policy bindings regardless of the condition for the role of
'roles/cloudkms.signer' and for the user 'test-user@gmail.com' on a the key frodo with the
keyring fellowship and location global, run:
$ {command} frodo --keyring='fellowship' --location='global' --member='user:test-user@gmail.com' --role='roles/cloudkms.signer' --all
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: cloudkms.projects.locations.keyRings.cryptoKeys
arguments:
resource:
help_text: The key to remove the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.kms.resources:key
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Remove a rotation schedule."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import resource_args
class RemoveRotationSchedule(base.UpdateCommand):
r"""Remove the rotation schedule for a key.
Removes the rotation schedule for the given key.
## EXAMPLES
The following command removes the rotation schedule for the key
named `frodo` within the keyring `fellowship` and location `global`:
$ {command} frodo \
--location=global \
--keyring=fellowship
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = flags.ParseCryptoKeyName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysPatchRequest(
name=crypto_key_ref.RelativeName(),
cryptoKey=messages.CryptoKey(),
updateMask='rotationPeriod,nextRotationTime')
return client.projects_locations_keyRings_cryptoKeys.Patch(req)

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Set the IAM policy for a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.api_lib.cloudkms import iam
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import resource_args
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class SetIamPolicy(base.Command):
"""Set the IAM policy for a key.
Sets the IAM policy for the given key as defined in a JSON or YAML file.
See https://cloud.google.com/iam/docs/managing-policies for details of
the policy file format and contents.
## EXAMPLES
The following command will read am IAM policy defined in a JSON file
'policy.json' and set it for the key `frodo` with the keyring
`fellowship` and location `global`:
$ {command} frodo policy.json --keyring=fellowship --location=global
"""
# Text from above based on output of function call below
# detailed_help = iam_util.GetDetailedHelpForSetIamPolicy(
# flags.CRYPTO_KEY_COLLECTION, 'example-crypto-key-1')
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
parser.add_argument('policy_file', help=('JSON or YAML '
'file with the IAM policy'))
def Run(self, args):
messages = cloudkms_base.GetMessagesModule()
policy, update_mask = iam_util.ParseYamlOrJsonPolicyFile(args.policy_file,
messages.Policy)
crypto_key_ref = flags.ParseCryptoKeyName(args)
result = iam.SetCryptoKeyIamPolicy(crypto_key_ref, policy, update_mask)
iam_util.LogSetIamPolicy(crypto_key_ref.Name(), 'key')
return result

View File

@@ -0,0 +1,31 @@
- release_tracks: [ALPHA]
help_text:
brief: Set the IAM policy for a key.
description: |
Sets the IAM policy for the given key as defined in a JSON or YAML file.
See https://cloud.google.com/iam/docs/managing-policies for details of
the policy file format and contents.
examples: |
The following command will read am IAM policy defined in a JSON file
'policy.json' and set it for the key `frodo` with the keyring
`fellowship` and location `global`:
$ {command} frodo policy.json --keyring fellowship --location global
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
request:
collection: cloudkms.projects.locations.keyRings.cryptoKeys
modify_request_hooks:
- googlecloudsdk.command_lib.iam.hooks:UseMaxRequestedPolicyVersion:api_field=setIamPolicyRequest.policy.version
- googlecloudsdk.command_lib.iam.hooks:AddVersionToUpdateMaskIfNotPresent:update_mask_path=setIamPolicyRequest.updateMask
arguments:
resource:
help_text: The key whose IAM policy to update.
spec: !REF googlecloudsdk.command_lib.kms.resources:key
iam:
policy_version: 3

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Set the primary version of a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import resource_args
class SetPrimaryVersion(base.Command):
"""Set the primary version of a key.
Sets the specified version as the primary version of the given key.
The version is specified by its version number assigned on creation.
## EXAMPLES
The following command sets version 9 as the primary version of the
key `samwise` within keyring `fellowship` and location `global`:
$ {command} samwise --version=9 --keyring=fellowship --location=global
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
flags.AddCryptoKeyVersionFlag(parser, 'to make primary', required=True)
def Run(self, args):
# pylint: disable=line-too-long
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
key_ref = flags.ParseCryptoKeyName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysUpdatePrimaryVersionRequest(
name=key_ref.RelativeName(),
updateCryptoKeyPrimaryVersionRequest=(
messages.UpdateCryptoKeyPrimaryVersionRequest(
cryptoKeyVersionId=version_ref.cryptoKeyVersionsId)))
return client.projects_locations_keyRings_cryptoKeys.UpdatePrimaryVersion(
req)

View File

@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Update a rotation schedule on a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import resource_args
class SetRotationSchedule(base.UpdateCommand):
r"""Update the rotation schedule for a key.
Updates the rotation schedule for the given key. The schedule
automatically creates a new primary version for the key
according to the `--next-rotation-time` and `--rotation-period` flags.
The flag `--next-rotation-time` must be in ISO or RFC3339 format,
and `--rotation-period` must be in the form INTEGER[UNIT], where units
can be one of seconds (s), minutes (m), hours (h) or days (d).
Key rotations performed manually via `update-primary-version` and the
version `create` do not affect the stored `--next-rotation-time`.
## EXAMPLES
The following command sets a 30 day rotation period for the key
named `frodo` within the keyring `fellowship` and location `global`
starting at the specified time:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--rotation-period=30d \
--next-rotation-time=2017-10-12T12:34:56.1234Z
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
flags.AddRotationPeriodFlag(parser)
flags.AddNextRotationTimeFlag(parser)
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = flags.ParseCryptoKeyName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysPatchRequest(
name=crypto_key_ref.RelativeName(),
cryptoKey=messages.CryptoKey())
flags.SetNextRotationTime(args, req.cryptoKey)
flags.SetRotationPeriod(args, req.cryptoKey)
fields_to_update = []
if args.rotation_period is not None:
fields_to_update.append('rotationPeriod')
if args.next_rotation_time is not None:
fields_to_update.append('nextRotationTime')
if not fields_to_update:
raise exceptions.ArgumentError(
'At least one of --next-rotation-time or --rotation-period must be '
'specified.')
req.updateMask = ','.join(fields_to_update)
return client.projects_locations_keyRings_cryptoKeys.Patch(req)

View File

@@ -0,0 +1,339 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Update a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import maps
from googlecloudsdk.command_lib.kms import resource_args
from googlecloudsdk.command_lib.util.args import labels_util
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Update(base.UpdateCommand):
r"""Update a key.
1. Update the rotation schedule for the given key.
Updates the rotation schedule for the given key. The schedule
automatically creates a new primary version for the key
according to `next-rotation-time` and `rotation-period` flags.
Flag `next-rotation-time` must be in ISO 8601 or RFC3339 format,
and `rotation-period` must be in the form INTEGER[UNIT], where units
can be one of seconds (s), minutes (m), hours (h) or days (d).
Key rotations performed manually via `update-primary-version` and the
version `create` do not affect the stored `next-rotation-time`.
2. Remove the rotation schedule for the given key with
`remove-rotation-schedule` flag.
3. Update/Remove the labels for the given key with `update-labels` and/or
`remove-labels` flags.
4. Update the primary version for the given key with `primary-version` flag.
5. Update the Key Access Justifications policy for the given key with
`allowed-access-reasons` flag to allow specified reasons. The key must be
enrolled in Key Access Justifications to use this flag.
6. Remove the Key Access Justifications policy for the given key with
`remove-key-access-justifications-policy` flag. The key must be enrolled in
Key Access Justifications to use this flag.
7. Update the Key Access Justifications policy for the given key with
`allowed_access_reasons` flag to allow zero access reasons. This effectively
disables the key, because a policy is configured to reject all access reasons.
The key must be enrolled in Key Access Justifications to use this flag.
## EXAMPLES
The following command sets a 30 day rotation period for the key
named `frodo` within the keyring `fellowship` and location `global`
starting at the specified time:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--rotation-period=30d \
--next-rotation-time=2017-10-12T12:34:56.1234Z
The following command removes the rotation schedule for the key
named `frodo` within the keyring `fellowship` and location `global`:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--remove-rotation-schedule
The following command updates the labels value for the key
named `frodo` within the keyring `fellowship` and location `global`. If the
label key does not exist at the time, it will be added:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--update-labels=k1=v1
The following command removes labels k1 and k2 from the key
named `frodo` within the keyring `fellowship` and location `global`:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--remove-labels=k1,k2
The following command updates the primary version for the key
named `frodo` within the keyring `fellowship` and location `global`:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--primary-version=1
The following command updates the default algorithm for the key named `frodo`
within the keyring `fellowship` and location `global`, assuming the key
originally has purpose 'asymmetric-encryption' and algorithm
'rsa-decrypt-oaep-2048-sha256':
$ {command} frodo \
--location=global \
--keyring=fellowship \
--default-algorithm=rsa-decrypt-oaep-4096-sha256
The following command updates the Key Access Justifications policy for the key
named `frodo` within the keyring ``fellowship'' and location ``global'' to
allow only ``customer-initiated-access'' and
``google-initiated-system-operation'':
$ {command} frodo \
--location=global \
--keyring=fellowship \
--allowed-access-reasons=customer-initiated-access,google-initiated-system-operation
The following command removes the Key Access Justifications policy for the key
named `frodo` within the keyring ``fellowship'' and location ``global'', which
results in all access reasons being allowed:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--remove-key-access-justifications-policy
The following command updates the Key Access Justifications policy for the key
named `frodo` within the keyring ``fellowship'' and location ``global'' to
allow only zero access reasons, effectively disabling the key:
$ {command} frodo \
--location=global \
--keyring=fellowship \
--allowed-access-reasons=
"""
@staticmethod
def Args(parser):
resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key')
flags.AddRotationPeriodFlag(parser)
flags.AddNextRotationTimeFlag(parser)
flags.AddRemoveRotationScheduleFlag(parser)
flags.AddCryptoKeyPrimaryVersionFlag(parser, 'to make primary')
labels_util.AddUpdateLabelsFlags(parser)
flags.AddDefaultAlgorithmFlag(parser)
flags.AddAllowedAccessReasonsFlag(parser)
flags.AddRemoveKeyAccessJustificationsPolicyFlag(parser)
def ProcessFlags(self, args):
fields_to_update = []
labels_diff = labels_util.Diff.FromUpdateArgs(args)
if labels_diff.MayHaveUpdates():
fields_to_update.append('labels')
if args.remove_rotation_schedule:
if args.rotation_period or args.next_rotation_time:
raise kms_exceptions.ArgumentError(
'You cannot set and remove rotation schedule at the same time.')
fields_to_update.append('rotationPeriod')
fields_to_update.append('nextRotationTime')
if args.rotation_period:
fields_to_update.append('rotationPeriod')
if args.next_rotation_time:
fields_to_update.append('nextRotationTime')
if args.default_algorithm:
fields_to_update.append('versionTemplate.algorithm')
if (
args.allowed_access_reasons is not None
and args.remove_key_access_justifications_policy
):
raise kms_exceptions.ArgumentError(
'You cannot set and remove a Key Access Justifications policy at the '
'same time.'
)
if (
args.allowed_access_reasons is not None
or args.remove_key_access_justifications_policy
):
fields_to_update.append('keyAccessJustificationsPolicy')
# Raise an exception when no update field is specified.
if not args.primary_version and not fields_to_update:
raise kms_exceptions.UpdateError(
'At least one of --primary-version or --update-labels or '
'--remove-labels or --clear-labels or --rotation-period or '
'--next-rotation-time or --remove-rotation-schedule or '
'--default-algorithm or --allowed-access-reasons or '
'--remove-key-access-justifications-policy must be specified.'
)
return fields_to_update
def UpdatePrimaryVersion(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = args.CONCEPTS.key.Parse()
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysUpdatePrimaryVersionRequest( # pylint: disable=line-too-long
name=crypto_key_ref.RelativeName(),
updateCryptoKeyPrimaryVersionRequest=(
messages.UpdateCryptoKeyPrimaryVersionRequest(
cryptoKeyVersionId=args.primary_version)))
try:
response = client.projects_locations_keyRings_cryptoKeys.UpdatePrimaryVersion( # pylint: disable=line-too-long
req)
except apitools_exceptions.HttpError:
return None
return response
def UpdateOthers(self, args, crypto_key, fields_to_update):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = args.CONCEPTS.key.Parse()
labels_update = labels_util.Diff.FromUpdateArgs(args).Apply(
messages.CryptoKey.LabelsValue, crypto_key.labels)
if labels_update.needs_update:
new_labels = labels_update.labels
else:
new_labels = crypto_key.labels
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysPatchRequest(
name=crypto_key_ref.RelativeName(),
cryptoKey=messages.CryptoKey(
labels=new_labels))
req.updateMask = ','.join(fields_to_update)
flags.SetNextRotationTime(args, req.cryptoKey)
flags.SetRotationPeriod(args, req.cryptoKey)
if args.default_algorithm:
valid_algorithms = maps.VALID_ALGORITHMS_MAP[crypto_key.purpose]
if args.default_algorithm not in valid_algorithms:
raise kms_exceptions.UpdateError(
'Update failed: Algorithm {algorithm} is not valid. Here are the '
'valid algorithm(s) for purpose {purpose}: {all_algorithms}'.format(
algorithm=args.default_algorithm,
purpose=crypto_key.purpose,
all_algorithms=', '.join(valid_algorithms)))
req.cryptoKey.versionTemplate = messages.CryptoKeyVersionTemplate(
algorithm=maps.ALGORITHM_MAPPER.GetEnumForChoice(
args.default_algorithm))
if not args.remove_key_access_justifications_policy:
flags.SetKeyAccessJustificationsPolicy(args, req.cryptoKey)
try:
response = client.projects_locations_keyRings_cryptoKeys.Patch(req)
except apitools_exceptions.HttpError:
return None
return response
def HandleErrors(self, args, set_primary_version_succeeds,
other_updates_succeed, fields_to_update):
"""Handles various errors that may occur during any update stage.
Never returns without an exception.
Args:
args: Input arguments.
set_primary_version_succeeds: True if the primary verion is updated
successfully.
other_updates_succeed: True if all other updates (besides primary verions)
is updated successfully.
fields_to_update: A list of fields to be updated.
Raises:
ToolException: An exception raised when there is error during any update
stage.
"""
err = 'An Error occurred:'
if not set_primary_version_succeeds:
err += " Failed to update field 'primaryVersion'."
elif args.primary_version:
err += " Field 'primaryVersion' was updated."
if not other_updates_succeed:
err += " Failed to update field(s) '{}'.".format(
"', '".join(fields_to_update))
elif fields_to_update:
err += " Field(s) '{}' were updated.".format(
"', '".join(fields_to_update))
raise kms_exceptions.UpdateError(err)
def Run(self, args):
"""Updates the relevant fields (of a CryptoKey) from the flags."""
# Check the flags and raise an exception if any check fails.
fields_to_update = self.ProcessFlags(args)
# Try to get the cryptoKey and raise an exception if the key doesn't exist.
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = args.CONCEPTS.key.Parse()
crypto_key = client.projects_locations_keyRings_cryptoKeys.Get(
messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysGetRequest(
name=crypto_key_ref.RelativeName()))
# Try to update the key's primary version.
set_primary_version_succeeds = True
if args.primary_version:
response = self.UpdatePrimaryVersion(args)
if response:
crypto_key = response # If call succeeds, update the crypto_key.
else:
set_primary_version_succeeds = False
# Try other updates.
other_updates_succeed = True
if fields_to_update:
response = self.UpdateOthers(args, crypto_key, fields_to_update)
if response:
crypto_key = response # If call succeeds, update the crypto_key.
else:
other_updates_succeed = False
if not set_primary_version_succeeds or not other_updates_succeed:
self.HandleErrors(args, set_primary_version_succeeds,
other_updates_succeed, fields_to_update)
else:
return crypto_key

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The command group for versions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.core import resources
class Versions(base.Group):
"""Create and manage versions.
A version represents an individual cryptographic key and the
associated key material.
"""
@staticmethod
def Args(parser):
parser.display_info.AddUriFunc(
cloudkms_base.MakeGetUriFunc(flags.CRYPTO_KEY_VERSION_COLLECTION))

View File

@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Create a new version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Create(base.CreateCommand):
r"""Create a new version.
Creates a new version within the given key.
## EXAMPLES
The following command creates a new version within the `frodo`
key, `fellowship` keyring, and `global` location and sets it as
the primary version:
$ {command} --location=global \
--keyring=fellowship \
--key=frodo --primary
The following command creates a new version within the `legolas`
key, `fellowship` keyring, `us-central1` location,
`https://example.kms/v0/some/key/path` as the address for its external key,
and sets it as the key's primary version:
$ {command} --location=us-central1 \
--keyring=fellowship \
--key=legolas \
--external-key-uri=https://example.kms/v0/some/key/path \
--primary
The following command creates a new version within the `bilbo`
key, `fellowship` keyring, `us-central1` location,
`v0/some/key/path` as the ekm connection key path for its external key,
and sets it as the key's primary version:
$ {command} --location=us-central1 \
--keyring=fellowship \
--key=bilbo \
--ekm-connection-key-path=v0/some/key/path \
--primary
"""
GOOGLE_SYMMETRIC_ENCRYPTION = (
cloudkms_base.GetMessagesModule().CryptoKeyVersion.AlgorithmValueValuesEnum.GOOGLE_SYMMETRIC_ENCRYPTION
)
SYMMETRIC_NEW_PRIMARY_MESSAGE = (
'Successfully created key version [{version}] and set it as the primary '
'version. Future encryption requests will use [{version}] until the next '
'key rotation. Data that was encrypted with an older key version can '
'still be decrypted, and Cloud KMS will automatically choose the correct '
'key for decryption based on the ciphertext.')
@staticmethod
def Args(parser):
flags.AddKeyResourceFlags(parser)
flags.AddExternalKeyUriFlag(parser)
flags.AddEkmConnectionKeyPathFlag(parser)
parser.add_argument(
'--primary',
action='store_true',
help=(
'If specified, immediately makes the new version primary. '
'This should only be used with the `encryption` purpose.'
),
)
def _CreateCreateCKVRequest(self, args):
# pylint: disable=line-too-long
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = flags.ParseCryptoKeyName(args)
if args.external_key_uri and args.ekm_connection_key_path:
raise kms_exceptions.ArgumentError(
'Can not specify both --external-key-uri and '
'--ekm-connection-key-path.')
if args.external_key_uri or args.ekm_connection_key_path:
return messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsCreateRequest(
parent=crypto_key_ref.RelativeName(),
cryptoKeyVersion=messages.CryptoKeyVersion(
externalProtectionLevelOptions=messages
.ExternalProtectionLevelOptions(
externalKeyUri=args.external_key_uri,
ekmConnectionKeyPath=args.ekm_connection_key_path)))
return messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsCreateRequest(
parent=crypto_key_ref.RelativeName())
def Run(self, args):
# pylint: disable=line-too-long
client = cloudkms_base.GetClientInstance()
ckv = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions
new_ckv = ckv.Create(self._CreateCreateCKVRequest(args))
if args.primary:
version_id = new_ckv.name.split('/')[-1]
crypto_key_ref = flags.ParseCryptoKeyName(args)
messages = cloudkms_base.GetMessagesModule()
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysUpdatePrimaryVersionRequest(
name=crypto_key_ref.RelativeName(),
updateCryptoKeyPrimaryVersionRequest=(
messages.UpdateCryptoKeyPrimaryVersionRequest(
cryptoKeyVersionId=version_id)))
client.projects_locations_keyRings_cryptoKeys.UpdatePrimaryVersion(req)
if new_ckv.algorithm == self.GOOGLE_SYMMETRIC_ENCRYPTION:
log.err.Print(
self.SYMMETRIC_NEW_PRIMARY_MESSAGE.format(version=version_id))
return new_ckv

View File

@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Describe a version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.core import log
from googlecloudsdk.core.util import files
@base.UniverseCompatible
class Describe(base.DescribeCommand):
r"""Get metadata for a given version.
Returns metadata for the given version.
The optional flag `attestation-file` specifies file to write the attestation
object into. The attestation object enables the user to verify the integrity
and provenance of the key. See https://cloud.google.com/kms/docs/attest-key
for more information about attestations.
## EXAMPLES
The following command returns metadata for version 2 within key `frodo`
within the keyring `fellowship` in the location `us-east1`:
$ {command} 2 --key=frodo --keyring=fellowship --location=us-east1
For key versions with protection level `HSM`, use the `--attestation-file`
flag to save the attestation to a local file.
$ {command} 2 --key=frodo --keyring=fellowship --location=us-east1 \
--attestation-file=path/to/attestation.dat
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to describe')
flags.AddAttestationFileFlag(parser)
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
if not version_ref.Name():
raise exceptions.InvalidArgumentException(
'version', 'version id must be non-empty.')
version = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions.Get( # pylint: disable=line-too-long
messages.
CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsGetRequest(
name=version_ref.RelativeName()))
# Raise exception if --attestation-file is provided for software
# key versions.
hsm = messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.HSM
hsm_st = (
messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.HSM_SINGLE_TENANT
)
if version.protectionLevel not in [hsm, hsm_st] and args.attestation_file:
raise kms_exceptions.ArgumentError(
'Attestations are only available for HSM or HSM_SINGLE_TENANT key '
'versions.'
)
if (args.attestation_file and version.state ==
messages.CryptoKeyVersion.StateValueValuesEnum.PENDING_GENERATION):
raise kms_exceptions.ArgumentError(
'The attestation is unavailable until the version is generated.')
if args.attestation_file and version.attestation is not None:
try:
log.WriteToFileOrStdout(
args.attestation_file,
version.attestation.content,
overwrite=True,
binary=True)
except files.Error as e:
raise exceptions.BadFileException(e)
if version.attestation is not None:
# Suppress the attestation content in the printed output. Users can use
# --attestation-file to obtain it, instead.
version.attestation.content = None
# Suppress the attestation content in the printed output. Users can use
# get-certificate-chain to obtain it, instead.
version.attestation.certChains = None
return version

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Destroy a version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
class Destroy(base.UpdateCommand):
"""Schedule a version to be destroyed.
Schedules the given version for destruction in 24 hours.
After that time period passes it is automatically destroyed. Once
destroyed, the key material is removed but the version number can
not be reused.
Only versions which are Enabled or Disabled can be Scheduled
for destruction.
## EXAMPLES
The following command schedules version 9 of key `frodo` within
keyring `fellowship` and location `us-east1` for destruction:
$ {command} 9 --location=us-east1 --keyring=fellowship --key=frodo
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to destroy')
def Run(self, args):
# pylint: disable=line-too-long
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsDestroyRequest(
name=version_ref.RelativeName())
ckv = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions
return ckv.Destroy(req)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Make a version deactivated."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.api_lib.cloudkms import cryptokeyversions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
class Disable(base.Command):
"""Disable a given version.
Disables the specified version within the given key.
Only a version which is Enabled can be Disabled.
## EXAMPLES
The following command disables version 3 of key `frodo` within
keyring `fellowship` and location `us-east1`:
$ {command} 3 --location=us-east1 --keyring=fellowship --key=frodo
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to disable')
def Run(self, args):
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
return cryptokeyversions.SetState(
version_ref, messages.CryptoKeyVersion.StateValueValuesEnum.DISABLED)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Make a version active."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.api_lib.cloudkms import cryptokeyversions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
class Enable(base.Command):
"""Enable a given version.
Enables the specified version within the given key.
Only a version which is Disabled can be Enabled.
## EXAMPLES
The following command enables version 3 of key `frodo` within
keyring `fellowship` and location `us-east1`:
$ {command} 3 --location=us-east1 --keyring=fellowship --key=frodo
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to enable')
def Run(self, args):
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
return cryptokeyversions.SetState(
version_ref, messages.CryptoKeyVersion.StateValueValuesEnum.ENABLED)

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Get a PEM-format certificate chain for a given version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.core import log
from googlecloudsdk.core.util import files
DETAILED_HELP = {
'EXAMPLES':
"""\
The following command saves the Cavium certificate chain for
CryptoKey ``frodo'' Version 2 to ``/tmp/my/cavium.pem'':
$ {command} 2 --key=frodo --keyring=fellowship --location=us-east1 --certificate-chain-type=cavium --output-file=/tmp/my/cavium.pem
""",
}
def _GetCertificateChainPem(chains, chain_type):
"""Returns the specified certificate chain(s) from a CertChains object.
Args:
chains: a KeyOperationAttestation.CertChains object.
chain_type: a string specifying the chain(s) to retrieve.
Returns:
A string containing the PEM-encoded certificate chain(s).
Raises:
exceptions.InvalidArgumentException if chain_type is not a valid chain type.
"""
if chain_type == 'cavium':
return ''.join(chains.caviumCerts)
elif chain_type == 'google-card':
return ''.join(chains.googleCardCerts)
elif chain_type == 'google-partition':
return ''.join(chains.googlePartitionCerts)
elif chain_type == 'all':
return ''.join(
chains.caviumCerts
+ chains.googlePartitionCerts
+ chains.googleCardCerts
)
raise exceptions.InvalidArgumentException(
'certificate_chain_type',
'{} is not a valid chain type.'.format(chain_type),
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class GetCertificateChain(base.DescribeCommand):
r"""Get a certificate chain for a given version.
Returns the PEM-format certificate chain for the specified key version.
The optional flag `output-file` indicates the path to store the PEM. If not
specified, the PEM will be printed to stdout.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(
parser, 'from which to get the certificate chain')
flags.AddCertificateChainFlag(parser)
flags.AddOutputFileFlag(parser, 'to store PEM')
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
if not version_ref.Name():
raise exceptions.InvalidArgumentException(
'version', 'version id must be non-empty.')
versions = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions
version = versions.Get(
messages
.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsGetRequest(
name=version_ref.RelativeName()))
hsm = messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.HSM
hsm_st = (
messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.HSM_SINGLE_TENANT
)
if version.protectionLevel not in [hsm, hsm_st]:
raise kms_exceptions.ArgumentError(
'Certificate chains are only available for HSM and HSM_SINGLE_TENANT '
'key versions.'
)
if (version.state ==
messages.CryptoKeyVersion.StateValueValuesEnum.PENDING_GENERATION):
raise kms_exceptions.ArgumentError(
'Certificate chains are unavailable until the version is generated.')
try:
log.WriteToFileOrStdout(
args.output_file if args.output_file else '-',
_GetCertificateChainPem(version.attestation.certChains,
args.certificate_chain_type),
overwrite=True,
binary=False)
except files.Error as e:
raise exceptions.BadFileException(e)

View File

@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Get the public key for a given version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import maps
from googlecloudsdk.core import log
@base.UniverseCompatible
class GetPublicKey(base.DescribeCommand):
r"""Get the public key for a given version.
Returns the public key of the given asymmetric key version in the specified format.
The optional flag `output-file` indicates the path to store the public key.
If not specified, the public key will be printed to stdout.
The optional flag `public-key-format` indicates the format in which the
public key will be returned. For the NIST PQC algorithms, this must be
specified and set to `nist-pqc`. For kem-xwing this must be specified and set to
`xwing-raw-bytes`. For all other algorithms, this flag is
optional and can be either `pem` or `der`; the default value is `pem`.
See "Retrieve a public key" in the Cloud KMS
documentation (https://cloud.google.com/kms/help/get-public-key) for more
information about the supported formats.
## EXAMPLES
The following command saves the public key for CryptoKey `frodo` Version 2
to '/tmp/my/public_key.file':
$ {command} 2 \
--key=frodo \
--keyring=fellowship \
--location=us-east1 \
--public-key-format=pem \
--output-file=/tmp/my/public_key.file
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to get public key')
flags.AddOutputFileFlag(parser, 'to store public key')
flags.AddPublicKeyFormatFlag(parser)
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
if not version_ref.Name():
raise exceptions.InvalidArgumentException(
'version', 'version id must be non-empty.'
)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsGetPublicKeyRequest( # pylint: disable=line-too-long
name=version_ref.RelativeName(),
)
if args.public_key_format:
if args.public_key_format not in [
'pem',
'der',
'nist-pqc',
'xwing-raw-bytes',
]:
raise exceptions.InvalidArgumentException(
'--public-key-format',
'Invalid value for public key format. Supported values are "der",'
' "pem", "xwing-raw-bytes" and "nist-pqc".',
)
req.publicKeyFormat = maps.PUBLIC_KEY_FORMAT_MAPPER.GetEnumForChoice(
args.public_key_format
)
resp = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions.GetPublicKey(
req
)
# DER, NIST_PQC and XWING_RAW_BYTES are in raw byte format.
# So the output is in binary format.
write_binary_file = args.public_key_format in [
'der',
'nist-pqc',
'xwing-raw-bytes',
]
log.WriteToFileOrStdout(
args.output_file if args.output_file else '-',
resp.pem if resp.pem else resp.publicKey.data,
overwrite=True,
binary=write_binary_file,
private=True,
)

View File

@@ -0,0 +1,242 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Import a provided key from file into KMS using an Import Job."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
import sys
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import maps
from googlecloudsdk.core import log
from googlecloudsdk.core.util import files
class Import(base.Command):
r"""Import a version into an existing crypto key.
Imports wrapped key material into a new version within an existing crypto key
following the import procedure documented at
https://cloud.google.com/kms/docs/importing-a-key.
## EXAMPLES
The following command will read the files 'path/to/ephemeral/key' and
'path/to/target/key' and use them to create a new version with algorithm
'google-symmetric-encryption' within the 'frodo' crypto key, 'fellowship'
keyring, and 'us-central1' location using import job 'strider' to unwrap the
provided key material.
$ {command} --location=global \
--keyring=fellowship \
--key=frodo \
--import-job=strider \
--wrapped-key-file=path/to/target/key \
--algorithm=google-symmetric-encryption
"""
@staticmethod
def Args(parser):
flags.AddKeyResourceFlags(parser, 'The containing key to import into.')
flags.AddCryptoKeyVersionFlag(
parser, 'to re-import into. Omit this field for first-time import')
flags.AddRsaAesWrappedKeyFileFlag(parser, 'to import')
flags.AddWrappedKeyFileFlag(parser, 'to import')
flags.AddImportedVersionAlgorithmFlag(parser)
flags.AddRequiredImportJobArgument(parser, 'to import from')
flags.AddPublicKeyFileFlag(parser)
flags.AddTargetKeyFileFlag(parser)
def _ReadFile(self, path, max_bytes):
data = files.ReadBinaryFileContents(path)
if len(data) > max_bytes:
raise exceptions.BadFileException(
'The file is larger than the maximum size of {0} bytes.'.format(
max_bytes))
return data
def _IsSha2ImportMethod(self, import_method, messages):
return import_method in (
messages.ImportJob.ImportMethodValueValuesEnum.RSA_OAEP_3072_SHA256,
messages.ImportJob.ImportMethodValueValuesEnum.RSA_OAEP_4096_SHA256,
messages.ImportJob.ImportMethodValueValuesEnum
.RSA_OAEP_3072_SHA256_AES_256, messages.ImportJob
.ImportMethodValueValuesEnum.RSA_OAEP_4096_SHA256_AES_256)
def _IsRsaAesWrappingImportMethod(self, import_method, messages):
return import_method in (messages.ImportJob.ImportMethodValueValuesEnum
.RSA_OAEP_3072_SHA1_AES_256,
messages.ImportJob.ImportMethodValueValuesEnum
.RSA_OAEP_4096_SHA1_AES_256,
messages.ImportJob.ImportMethodValueValuesEnum
.RSA_OAEP_3072_SHA256_AES_256,
messages.ImportJob.ImportMethodValueValuesEnum
.RSA_OAEP_4096_SHA256_AES_256)
def _ReadPublicKeyBytes(self, args):
try:
return self._ReadFile(args.public_key_file, max_bytes=65536)
except files.Error as e:
raise exceptions.BadFileException(
'Failed to read public key file [{0}]: {1}'.format(
args.public_key_file, e))
def _FetchImportJob(self, args, import_job_name, client, messages):
import_job = client.projects_locations_keyRings_importJobs.Get(
messages.CloudkmsProjectsLocationsKeyRingsImportJobsGetRequest(
name=import_job_name))
if import_job.state != messages.ImportJob.StateValueValuesEnum.ACTIVE:
raise exceptions.BadArgumentException(
'import-job', 'Import job [{0}] is not active (state is {1}).'.format(
import_job_name, import_job.state))
return import_job
def _CkmRsaAesKeyWrap(self, import_method, public_key_bytes, target_key_bytes,
client, messages):
try:
# TODO(b/141249289): Move imports to the top of the file. In the
# meantime, until we're sure that all Cloud SDK users have the
# cryptography module available, let's not error out if we can't load the
# module unless we're actually going down this code path.
# pylint: disable=g-import-not-at-top
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import keywrap
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
except ImportError:
log.err.Print('Cannot load the Pyca cryptography library. Either the '
'library is not installed, or site packages are not '
'enabled for the Google Cloud SDK. Please consult '
'https://cloud.google.com/kms/docs/crypto for further '
'instructions.')
sys.exit(1)
sha = hashes.SHA1()
if self._IsSha2ImportMethod(import_method, messages):
sha = hashes.SHA256()
# RSA-OAEP import methods have a maximum target key size that's a function
# of the RSA modulus size.
if not self._IsRsaAesWrappingImportMethod(import_method, messages):
if (
import_method
== messages.ImportJob.ImportMethodValueValuesEnum.RSA_OAEP_3072_SHA256
):
modulus_byte_length = 3072 // 8
elif (
import_method
== messages.ImportJob.ImportMethodValueValuesEnum.RSA_OAEP_4096_SHA256
):
modulus_byte_length = 4096 // 8
else:
raise ValueError('unexpected import method: {0}'.format(import_method))
# per go/rfc/8017#section-7.1.1
max_target_key_size = modulus_byte_length - (2 * sha.digest_size) - 2
if len(target_key_bytes) > max_target_key_size:
raise exceptions.BadFileException(
'target-key-file',
"The file is larger than the import method's maximum size of {0} "
'bytes.'.format(max_target_key_size),
)
aes_wrapped_key = b''
to_be_rsa_wrapped_key = target_key_bytes
public_key = serialization.load_pem_public_key(
public_key_bytes, backend=default_backend())
if self._IsRsaAesWrappingImportMethod(import_method, messages):
to_be_rsa_wrapped_key = os.urandom(32) # an ephemeral key
aes_wrapped_key = keywrap.aes_key_wrap_with_padding(
to_be_rsa_wrapped_key, target_key_bytes, default_backend())
rsa_wrapped_key = public_key.encrypt(
to_be_rsa_wrapped_key,
padding.OAEP(mgf=padding.MGF1(sha), algorithm=sha, label=None))
return rsa_wrapped_key + aes_wrapped_key
def Run(self, args):
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
import_job_name = flags.ParseImportJobName(args).RelativeName()
# set wrapped_key_file to wrapped_key_file or rsa_aes_wrapped_key_file
wrapped_key_file = None
if args.wrapped_key_file:
wrapped_key_file = args.wrapped_key_file
if args.rsa_aes_wrapped_key_file:
raise exceptions.OneOfArgumentsRequiredException(
('--wrapped-key-file', '--rsa-aes-wrapped-key-file'),
'Either wrapped-key-file or rsa-aes-wrapped-key-file should be provided.') # pylint: disable=line-too-long
else:
wrapped_key_file = args.rsa_aes_wrapped_key_file
if bool(wrapped_key_file) == bool(args.target_key_file):
raise exceptions.OneOfArgumentsRequiredException(
('--target-key-file', '--wrapped-key-file/--rsa-aes-wrapped-key-file'), # pylint: disable=line-too-long
'Either a pre-wrapped key or a key to be wrapped must be provided.')
wrapped_key_bytes = None
if wrapped_key_file:
try:
# This should be less than 64KiB.
wrapped_key_bytes = self._ReadFile(wrapped_key_file, max_bytes=65536)
except files.Error as e:
raise exceptions.BadFileException(
'Failed to read wrapped key file [{0}]: {1}'.format(
wrapped_key_file, e))
import_job = self._FetchImportJob(args, import_job_name, client, messages)
if args.target_key_file:
target_key_bytes = None
try:
# This should be less than 64KiB.
target_key_bytes = self._ReadFile(args.target_key_file, max_bytes=8192)
except files.Error as e:
raise exceptions.BadFileException(
'Failed to read target key file [{0}]: {1}'.format(
args.target_key_file, e))
# Read the public key off disk if provided, otherwise, fetch it from KMS.
public_key_bytes = None
if args.public_key_file:
public_key_bytes = self._ReadPublicKeyBytes(args)
else:
public_key_bytes = import_job.publicKey.pem.encode('ascii')
wrapped_key_bytes = self._CkmRsaAesKeyWrap(import_job.importMethod,
public_key_bytes,
target_key_bytes, client,
messages)
# Send the request to KMS.
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsImportRequest( # pylint: disable=line-too-long
parent=flags.ParseCryptoKeyName(args).RelativeName())
req.importCryptoKeyVersionRequest = messages.ImportCryptoKeyVersionRequest(
algorithm=maps.ALGORITHM_MAPPER_FOR_IMPORT.GetEnumForChoice(
args.algorithm),
importJob=import_job_name,
wrappedKey=wrapped_key_bytes)
if args.version:
req.importCryptoKeyVersionRequest.cryptoKeyVersion = flags.ParseCryptoKeyVersionName(
args).RelativeName()
return client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions.Import(
req)

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""List the versions within a key."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
class List(base.ListCommand):
r"""List the versions within a key.
Lists all of the versions within the given key.
## EXAMPLES
The following command lists all versions within the
key `frodo`, keyring `fellowship`, and location `global`:
$ {command} --location=global \
--keyring=fellowship \
--key=frodo
"""
@staticmethod
def Args(parser):
flags.AddKeyResourceFlags(parser)
parser.display_info.AddFormat('table(name, state)')
def Run(self, args):
# pylint: disable=line-too-long
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
crypto_key_ref = flags.ParseCryptoKeyName(args)
request = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsListRequest(
parent=crypto_key_ref.RelativeName())
return list_pager.YieldFromList(
client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions,
request,
field='cryptoKeyVersions',
limit=args.limit,
batch_size_attribute='pageSize')

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Restore a version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import flags
class Restore(base.UpdateCommand):
"""Restore a version scheduled for destruction.
Restores the given version that was scheduled to be destroyed.
This moves the version from Scheduled for destruction to Disabled.
Only versions which are Scheduled for destruction can be Restored.
## EXAMPLES
The following command restores version 9 of key `frodo` within
keyring `fellowship` and location `us-east1` which was previously scheduled
for destruction:
$ {command} 9 --location=us-east1 --keyring=fellowship --key=frodo
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to restore')
def Run(self, args):
# pylint: disable=line-too-long
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsRestoreRequest(
name=version_ref.RelativeName())
ckv = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions
return ckv.Restore(req)

View File

@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Update a key version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions
from googlecloudsdk.command_lib.kms import flags
from googlecloudsdk.command_lib.kms import maps
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Update(base.UpdateCommand):
r"""Update a key version.
{command} can be used to update the key versions. Updates can be made to the
the key versions's state (enabling or disabling it), to its external key
URI (if the key version has protection level EXTERNAL), or to its ekm
connection key path (if the key version has protection level EXTERNAL_VPC).
## EXAMPLES
The following command enables the key version 8 of key `frodo`
within keyring `fellowship` and location `us-east1`:
$ {command} 8 --location=us-east1 \
--keyring=fellowship \
--key=frodo \
--state=enabled
The following command disables the key version 8 of key `frodo`
within keyring `fellowship` and location `us-east1`:
$ {command} 8 --location=us-east1 \
--keyring=fellowship \
--key=frodo \
--state=disabled
The following command updates the external key URI of version 8 of key `frodo`
within keyring `fellowship` and location `us-east1`:
$ {command} 8 --location=us-east1 \
--keyring=fellowship \
--key=frodo \
--external-key-uri=https://example.kms/v0/some/key/path
The following command updates the ekm connection key path of version 8 of key
`bilbo` within keyring `fellowship` and location `us-east1`:
$ {command} 8 --location=us-east1 \
--keyring=fellowship \
--key=bilbo \
--ekm-connection-key-path=v0/some/key/path
"""
@staticmethod
def Args(parser):
flags.AddKeyVersionResourceArgument(parser, 'to describe')
flags.AddExternalKeyUriFlag(parser)
flags.AddEkmConnectionKeyPathFlag(parser)
flags.AddStateFlag(parser)
def ProcessFlags(self, args):
fields_to_update = []
if args.external_key_uri:
fields_to_update.append('externalProtectionLevelOptions.externalKeyUri')
if args.ekm_connection_key_path:
fields_to_update.append(
'externalProtectionLevelOptions.ekmConnectionKeyPath')
if args.state:
fields_to_update.append('state')
# Raise an exception when no update field is specified.
if not fields_to_update:
raise kms_exceptions.UpdateError(
'An error occurred: --external-key-uri or --ekm-connection-key-path'
' or --state must be specified.')
return fields_to_update
def CreateRequest(self, args, messages, fields_to_update):
# pylint: disable=line-too-long
version_ref = flags.ParseCryptoKeyVersionName(args)
req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsPatchRequest(
name=version_ref.RelativeName(),
cryptoKeyVersion=messages.CryptoKeyVersion(
state=maps.CRYPTO_KEY_VERSION_STATE_MAPPER.GetEnumForChoice(
args.state),
externalProtectionLevelOptions=messages
.ExternalProtectionLevelOptions(
externalKeyUri=args.external_key_uri,
ekmConnectionKeyPath=args.ekm_connection_key_path)))
req.updateMask = ','.join(fields_to_update)
return req
def CheckKeyIsExternal(self, key_version, messages):
if (key_version.protectionLevel !=
messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.EXTERNAL):
raise kms_exceptions.UpdateError(
'External key URI updates are only available for key versions '
'with EXTERNAL protection level')
def CheckKeyIsExternalVpc(self, key_version, messages):
if (key_version.protectionLevel !=
messages.CryptoKeyVersion.ProtectionLevelValueValuesEnum.EXTERNAL_VPC):
raise kms_exceptions.UpdateError(
'EkmConnection key path updates are only available for key versions '
'with EXTERNAL_VPC protection level')
def Run(self, args):
# pylint: disable=line-too-long
fields_to_update = self.ProcessFlags(args)
client = cloudkms_base.GetClientInstance()
messages = cloudkms_base.GetMessagesModule()
version_ref = flags.ParseCryptoKeyVersionName(args)
# Try to get the cryptoKeyVersion and raise an exception if it doesn't exist.
key_version = client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions.Get(
messages
.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsGetRequest(
name=version_ref.RelativeName()))
# Check that this key version's ProtectionLevel is EXTERNAL
if args.external_key_uri:
self.CheckKeyIsExternal(key_version, messages)
if args.ekm_connection_key_path:
self.CheckKeyIsExternalVpc(key_version, messages)
# Make update request
update_req = self.CreateRequest(args, messages, fields_to_update)
return client.projects_locations_keyRings_cryptoKeys_cryptoKeyVersions.Patch(
update_req)