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,151 @@
# -*- 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 base surface for Binary Authorization signatures."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Binauthz(base.Group):
r"""Manage attestations for Binary Authorization on Google Cloud Platform.
Binary Authorization is a feature which allows binaries to run on Google
Cloud Platform only if they are appropriately attested. Binary
Authorization is configured by creating a policy.
## EXAMPLES
This example assumes that you have created a keypair using gpg, usually
by running `gpg --gen-key ...`, with `Name-Email` set to
`attesting_user@example.com` for your attestor.
First, some convenience variables for brevity:
```sh
ATTESTING_USER="attesting_user@example.com"
DIGEST="000000000000000000000000000000000000000000000000000000000000abcd"
ARTIFACT_URL="gcr.io/example-project/example-image@sha256:${DIGEST}"
ATTESTOR_NAME="projects/example-project/attestors/canary"
```
Export your key's fingerprint (note this may differ based on version and
implementations of gpg):
```sh
gpg \
--with-colons \
--with-fingerprint \
--force-v4-certs \
--list-keys \
"${ATTESTING_USER}" | grep fpr | cut --delimiter=':' --fields 10
```
This should produce a 40 character, hexidecimal encoded string. See
https://tools.ietf.org/html/rfc4880#section-12.2 for more information on
key fingerprints.
Create your attestation payload:
```sh
{command} create-signature-payload \
--artifact-url="${ARTIFACT_URL}" \
> example_payload.txt
```
Create a signature from your attestation payload:
```sh
gpg \
--local-user "${ATTESTING_USER}" \
--armor \
--clearsign \
--output example_signature.pgp \
example_payload.txt
```
Upload the attestation:
```sh
{command} attestations create \
--public-key-id=${KEY_FINGERPRINT} \
--signature-file=example_signature.pgp \
--artifact-url="${ARTIFACT_URL}" \
--attestor=${ATTESTOR_NAME}
```
List the attestation by artifact URL. `--format` can be passed to
output the attestations as json or another supported format:
```sh
{command} attestations list \
--artifact-url="${ARTIFACT_URL}" \
--format=yaml
---
- |
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
... SNIP ...
-----END PGP PUBLIC KEY BLOCK-----
- |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
... SNIP ...
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
... SNIP ...
-----END PGP SIGNATURE-----
```
List all artifact URLs on the project for which Container Analysis
Occurrences exist. This list includes the list of all URLs with BinAuthz
attestations:
```sh
{command} attestations list
---
https://gcr.io/example-project/example-image@sha256:000000000000000000000000000000000000000000000000000000000000abcd
...
```
Listing also works for kind=ATTESTATION_AUTHORITY attestations, just pass
the attestor:
```sh
{command} attestations list \
--artifact-url="${ARTIFACT_URL}" \
--attestor=${ATTESTOR_NAME} \
--format=yaml
...
```
"""
category = base.COMPUTE_CATEGORY
def Filter(self, context, args):
"""See base class."""
base.RequireProjectID(args)
# Explicitly override container group's LEGACY billing configuration.
properties.VALUES.billing.quota_project.Set(
properties.VALUES.billing.CURRENT_PROJECT)
return context

View File

@@ -0,0 +1,25 @@
# -*- 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 Attestations base group for Binary Authorization signatures."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Attestations(base.Group):
"""Create and manage Google Binary Authorization attestations."""

View File

@@ -0,0 +1,406 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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 Create command for Binary Authorization attestations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import functools
import textwrap
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.api_lib.container.binauthz import containeranalysis
from googlecloudsdk.api_lib.container.binauthz import containeranalysis_apis as ca_apis
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import exceptions
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import util as binauthz_command_util
from googlecloudsdk.command_lib.container.binauthz import validation
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.util import files
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.CreateCommand):
r"""Create a Binary Authorization attestation.
This command creates a Binary Authorization attestation for your project. The
attestation is created for the specified artifact (e.g. a gcr.io container
URL), associate with the specified attestor, and stored under the specified
project.
## EXAMPLES
To create an attestation in the project "my_proj" as the attestor with
resource path "projects/foo/attestors/bar", run:
$ {command} \
--project=my_proj \
--artifact-url='gcr.io/example-project/example-image@sha256:abcd' \
--attestor=projects/foo/attestors/bar \
--signature-file=signed_artifact_attestation.pgp.sig \
--public-key-id=AAAA0000000000000000FFFFFFFFFFFFFFFFFFFF
To create an attestation in the project "my_proj" in note "projects/foo/notes/bar",
run:
$ {command} \
--project=my_proj \
--artifact-url='gcr.io/example-project/example-image@sha256:abcd' \
--note=projects/foo/notes/bar \
--signature-file=signed_artifact_attestation.pgp.sig \
--public-key-id=AAAA0000000000000000FFFFFFFFFFFFFFFFFFFF
"""
@classmethod
def Args(cls, parser):
flags.AddArtifactUrlFlag(parser)
parser.add_argument(
'--signature-file',
required=True,
type=str,
help=textwrap.dedent("""\
Path to file containing the signature to store, or `-` to read
signature from stdin."""),
)
parser.add_argument(
'--payload-file',
required=False,
type=str,
help=textwrap.dedent("""\
Path to file containing the payload over which the signature was
calculated.
This defaults to the output of the standard payload command:
$ {grandparent_command} create-signature-payload
NOTE: If you sign a payload with e.g. different whitespace or
formatting, you must explicitly provide the payload content via this
flag.
"""),
)
exclusive_group = parser.add_mutually_exclusive_group()
group = exclusive_group.add_group()
flags.AddConcepts(
group,
flags.GetAttestorPresentationSpec(
base_name='attestor',
required=False,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Attestor whose Container Analysis Note will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must
be able to read this attestor and must have the
`containeranalysis.notes.attachOccurrence` permission for the
Attestor's underlying Note resource (usually via the
`containeranalysis.notes.attacher` role)."""),
),
)
flags.AddConcepts(
exclusive_group,
flags.GetNotePresentationSpec(
base_name='note',
required=False,
positional=False,
group_help=textwrap.dedent("""\
The Container Analysis Note which will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must have the
`containeranalysis.notes.attachOccurrence` permission for the
Note (usually via the `containeranalysis.notes.attacher` role)."""),
),
)
parser.add_argument(
'--public-key-id',
required=True,
type=str,
help=textwrap.dedent("""\
The ID of the public key that will be used to verify the signature
of the created Attestation. This ID must match the one found on the
Attestor resource(s) which will verify this Attestation.
For PGP keys, this must be the version 4, full 160-bit fingerprint,
expressed as a 40 character hexadecimal string. See
https://tools.ietf.org/html/rfc4880#section-12.2 for details."""),
)
group.add_argument(
'--validate',
action='store_true',
default=False,
help=textwrap.dedent("""\
Whether to validate that the Attestation can be verified by the
provided Attestor.
"""),
)
def Run(self, args):
project_ref = resources.REGISTRY.Parse(
properties.VALUES.core.project.Get(required=True),
collection='cloudresourcemanager.projects',
)
artifact_url_without_scheme = binauthz_command_util.RemoveArtifactUrlScheme(
args.artifact_url
)
signature = console_io.ReadFromFileOrStdin(args.signature_file, binary=True)
if args.payload_file:
payload = files.ReadBinaryFileContents(args.payload_file)
else:
payload = binauthz_command_util.MakeSignaturePayload(
artifact_url_without_scheme
)
if args.attestor:
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = attestors.Client(api_version)
attestor = client.Get(attestor_ref)
validation_callback = functools.partial(
validation.validate_attestation,
attestor_ref=attestor_ref,
api_version=api_version,
)
return containeranalysis.Client().CreateAttestationOccurrence(
project_ref=project_ref,
note_ref=resources.REGISTRY.ParseResourceId(
'containeranalysis.projects.notes',
client.GetNoteAttr(attestor).noteReference,
{},
),
artifact_url=artifact_url_without_scheme,
public_key_id=args.public_key_id,
signature=signature,
plaintext=payload,
validation_callback=validation_callback
if 'validate' in args and args.validate
else None,
)
elif args.note:
return containeranalysis.Client(
ca_apis.GetApiVersion(self.ReleaseTrack())
).CreateAttestationOccurrence(
project_ref=project_ref,
note_ref=args.CONCEPTS.note.Parse(),
artifact_url=artifact_url_without_scheme,
public_key_id=args.public_key_id,
signature=signature,
plaintext=payload,
validation_callback=None,
)
else:
# This code is unreachable since Args() handles this case.
raise exceptions.InvalidArgumentError(
'One of --attestor or --note must be provided.'
)
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class CreateWithPkixSupport(base.CreateCommand):
r"""Create a Binary Authorization attestation.
This command creates a Binary Authorization attestation for your project. The
attestation is created for the specified artifact (e.g. a gcr.io container
URL), associate with the specified attestor, and stored under the specified
project.
## EXAMPLES
To create an attestation in the project "my_proj" as the attestor with
resource path "projects/foo/attestors/bar", run:
$ {command} \
--project=my_proj \
--artifact-url=gcr.io/example-project/example-image@sha256:abcd \
--attestor=projects/foo/attestors/bar \
--signature-file=signed_artifact_attestation.pgp.sig \
--public-key-id=AAAA0000000000000000FFFFFFFFFFFFFFFFFFFF
To create an attestation in the project "my_proj" in note "projects/foo/notes/bar",
run:
$ {command} \
--project=my_proj \
--artifact-url='gcr.io/example-project/example-image@sha256:abcd' \
--note=projects/foo/notes/bar \
--signature-file=signed_artifact_attestation.pgp.sig \
--public-key-id=AAAA0000000000000000FFFFFFFFFFFFFFFFFFFF
"""
@classmethod
def Args(cls, parser):
flags.AddArtifactUrlFlag(parser)
parser.add_argument(
'--signature-file',
required=True,
type=str,
help=textwrap.dedent("""\
Path to file containing the signature to store, or `-` to read
signature from stdin."""),
)
parser.add_argument(
'--payload-file',
required=False,
type=str,
help=textwrap.dedent("""\
Path to file containing the payload over which the signature was
calculated.
This defaults to the output of the standard payload command:
$ {grandparent_command} create-signature-payload
NOTE: If you sign a payload with e.g. different whitespace or
formatting, you must explicitly provide the payload content via this
flag.
"""),
)
exclusive_group = parser.add_mutually_exclusive_group()
group = exclusive_group.add_group()
flags.AddConcepts(
group,
flags.GetAttestorPresentationSpec(
base_name='attestor',
required=False,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Attestor whose Container Analysis Note will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must
be able to read this attestor and must have the
`containeranalysis.notes.attachOccurrence` permission for the
Attestor's underlying Note resource (usually via the
`containeranalysis.notes.attacher` role)."""),
),
)
flags.AddConcepts(
exclusive_group,
flags.GetNotePresentationSpec(
base_name='note',
required=False,
positional=False,
group_help=textwrap.dedent("""\
The Container Analysis Note which will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must have the
`containeranalysis.notes.attachOccurrence` permission for the
Note (usually via the `containeranalysis.notes.attacher` role)."""),
),
)
parser.add_argument(
'--public-key-id',
required=True,
type=str,
help=textwrap.dedent("""\
The ID of the public key that will be used to verify the signature
of the created Attestation. This ID must match the one found on the
Attestor resource(s) which will verify this Attestation.
For PKIX keys, this will be the URI-formatted `id` field of the
associated Attestor public key.
For PGP keys, this must be the version 4, full 160-bit fingerprint,
expressed as a 40 character hexadecimal string. See
https://tools.ietf.org/html/rfc4880#section-12.2 for details."""),
)
group.add_argument(
'--validate',
action='store_true',
default=False,
help=textwrap.dedent("""\
Whether to validate that the Attestation can be verified by the
provided Attestor.
"""),
)
def Run(self, args):
project_ref = resources.REGISTRY.Parse(
properties.VALUES.core.project.Get(required=True),
collection='cloudresourcemanager.projects',
)
artifact_url_without_scheme = binauthz_command_util.RemoveArtifactUrlScheme(
args.artifact_url
)
signature = console_io.ReadFromFileOrStdin(args.signature_file, binary=True)
if args.payload_file:
payload = files.ReadBinaryFileContents(args.payload_file)
else:
payload = binauthz_command_util.MakeSignaturePayload(
artifact_url_without_scheme
)
if args.attestor:
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = attestors.Client(api_version)
attestor = client.Get(attestor_ref)
validation_callback = functools.partial(
validation.validate_attestation,
attestor_ref=attestor_ref,
api_version=api_version,
)
return containeranalysis.Client().CreateAttestationOccurrence(
project_ref=project_ref,
note_ref=resources.REGISTRY.ParseResourceId(
'containeranalysis.projects.notes',
client.GetNoteAttr(attestor).noteReference,
{},
),
artifact_url=artifact_url_without_scheme,
public_key_id=args.public_key_id,
signature=signature,
plaintext=payload,
validation_callback=validation_callback
if 'validate' in args and args.validate
else None,
)
elif args.note:
note_ref = args.CONCEPTS.note.Parse()
return containeranalysis.Client(
ca_apis.GetApiVersion(self.ReleaseTrack())
).CreateAttestationOccurrence(
project_ref=project_ref,
note_ref=note_ref,
artifact_url=artifact_url_without_scheme,
public_key_id=args.public_key_id,
signature=signature,
plaintext=payload,
validation_callback=None,
)
else:
# This code is unreachable since Args() handles this case.
raise exceptions.InvalidArgumentError(
'One of --attestor or --note must be provided.'
)

View File

@@ -0,0 +1,121 @@
# -*- 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 List command for Binary Authorization signatures."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.api_lib.container.binauthz import containeranalysis
from googlecloudsdk.api_lib.container.binauthz import containeranalysis_apis as ca_apis
from googlecloudsdk.api_lib.container.binauthz import util as binauthz_api_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import util as binauthz_command_util
from googlecloudsdk.core import resources
@base.DefaultUniverseOnly
class List(base.ListCommand):
r"""List Binary Authorization attestations.
This command lists Binary Authorization attestations for your
project. Command line flags specify which attestor and artifact to
list the attestations for. If no attestor is specified, this lists
all attestations in the project, which requires the
`containeranalysis.occurrences.get` permission. If no artifact is
specified, then this lists all URLs with associated occurrences.
## EXAMPLES
List the Occurrence messages for all attestations bound to the passed
attestor:
$ {command} \
--attestor=projects/foo/attestor/bar
List the Occurrence messages for all attestations for the passed artifact-url
bound to the passed attestor:
$ {command} \
--attestor=projects/foo/attestors/bar \
--artifact-url='gcr.io/foo/example-image@sha256:abcd'
"""
@classmethod
def Args(cls, parser):
flags.AddArtifactUrlFlag(parser, required=False)
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
base_name='attestor',
required=False,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Attestor whose Container Analysis Note will be queried
for attestations. Note that the caller must have the
`containeranalysis.notes.listOccurrences` permission on the note
being queried."""),
),
)
def Run(self, args):
artifact_digest = None
if args.artifact_url:
artifact_digest = binauthz_command_util.GetImageDigest(args.artifact_url)
if args.attestor:
return self.ListInAttestor(args, artifact_digest)
return self.ListInProject(args, artifact_digest)
def ListInAttestor(self, args, artifact_digest):
attestors_client = attestors.Client(apis.GetApiVersion(self.ReleaseTrack()))
drydock_client = containeranalysis.Client(
ca_apis.GetApiVersion(self.ReleaseTrack())
)
attestor_ref = args.CONCEPTS.attestor.Parse()
attestor = attestors_client.Get(attestor_ref)
note_ref = resources.REGISTRY.ParseResourceId(
'containeranalysis.projects.notes',
attestors_client.GetNoteAttr(attestor).noteReference,
{},
)
return drydock_client.YieldAttestations(
note_ref=note_ref,
artifact_digest=artifact_digest,
page_size=args.page_size,
limit=args.limit,
)
def ListInProject(self, args, artifact_digest):
drydock_client = containeranalysis.Client(
ca_apis.GetApiVersion(self.ReleaseTrack())
)
return drydock_client.YieldAttestations(
note_ref=None,
project_ref=binauthz_api_util.GetProjectRef(),
artifact_digest=artifact_digest,
page_size=args.page_size,
limit=args.limit,
)

View File

@@ -0,0 +1,256 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""A command to sign and create attestations for Binary Authorization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import functools
import textwrap
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.api_lib.container.binauthz import containeranalysis
from googlecloudsdk.api_lib.container.binauthz import containeranalysis_apis as ca_apis
from googlecloudsdk.api_lib.container.binauthz import kms
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import exceptions
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import util as binauthz_command_util
from googlecloudsdk.command_lib.container.binauthz import validation
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class SignAndCreate(base.CreateCommand):
r"""Sign and create a Binary Authorization Attestation using a Cloud KMS key.
This command signs and creates a Binary Authorization attestation for your
project. The attestation is created for the specified artifact (e.g. a gcr.io
container URL), associate with the specified attestor, and stored under the
specified project.
## EXAMPLES
To sign and create an attestation in the project "my_proj" as the attestor
with resource path "projects/foo/attestors/bar" with the key
"projects/foo/locations/us-west1/keyRings/aring/cryptoKeys/akey/cryptoKeyVersions/1",
run:
$ {command} \
--project=my_proj \
--artifact-url='gcr.io/example-project/example-image@sha256:abcd' \
--attestor=projects/foo/attestors/bar \
--keyversion-project=foo \
--keyversion-location=us-west1 \
--keyversion-keyring=aring \
--keyversion-key=akey \
--keyversion=1
To sign and create an attestation in the project "my_proj" in note "bar"
with the key "projects/foo/locations/us-west1/keyRings/aring/cryptoKeys/akey/cryptoKeyVersions/1",
run:
$ {command} \
--project=my_proj \
--artifact-url='gcr.io/example-project/example-image@sha256:abcd' \
--note=projects/my_proj/notes/bar \
--keyversion-project=foo \
--keyversion-location=us-west1 \
--keyversion-keyring=aring \
--keyversion-key=akey \
--keyversion=1
"""
@classmethod
def Args(cls, parser):
flags.AddArtifactUrlFlag(parser)
exclusive_group = parser.add_mutually_exclusive_group()
group = exclusive_group.add_group()
flags.AddConcepts(
group,
flags.GetAttestorPresentationSpec(
base_name='attestor',
required=False,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Attestor whose Container Analysis Note will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must
be able to read this attestor and must have the
`containeranalysis.notes.attachOccurrence` permission for the
Attestor's underlying Note resource (usually via the
`containeranalysis.notes.attacher` role)."""),
),
)
flags.AddConcepts(
parser,
flags.GetCryptoKeyVersionPresentationSpec(
base_name='keyversion',
required=True,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Cloud KMS (Key Management Service) CryptoKeyVersion to use to
sign the attestation payload."""),
),
)
flags.AddConcepts(
exclusive_group,
flags.GetNotePresentationSpec(
base_name='note',
required=False,
positional=False,
group_help=textwrap.dedent("""\
The Container Analysis Note which will be used to host
the created attestation. In order to successfully attach the
attestation, the active gcloud account (core/account) must have the
`containeranalysis.notes.attachOccurrence` permission for the
Note (usually via the `containeranalysis.notes.attacher` role)."""),
),
)
parser.add_argument(
'--public-key-id-override',
type=str,
help=textwrap.dedent("""\
If provided, the ID of the public key that will be used to verify the
Attestation instead of the default generated one. This ID should match
the one found on the Attestor resource(s) which will use this
Attestation.
This parameter is only necessary if the `--public-key-id-override`
flag was provided when this KMS key was added to the Attestor."""),
)
group.add_argument(
'--validate',
action='store_true',
default=False,
help=textwrap.dedent("""\
Whether to validate that the Attestation can be verified by the
provided Attestor.
"""),
)
parser.add_argument(
'--pae-encode-payload',
action='store_true',
default=False,
help=textwrap.dedent("""\
Whether to pae-encode the payload before signing.
"""),
)
parser.add_argument(
'--dsse-type',
type=str,
default='application/vnd.dev.cosign.simplesigning.v1+json',
help=textwrap.dedent("""\
DSSE type used for pae encoding."""),
)
def Run(self, args):
project_ref = resources.REGISTRY.Parse(
properties.VALUES.core.project.Get(required=True),
collection='cloudresourcemanager.projects',
)
artifact_url_without_scheme = binauthz_command_util.RemoveArtifactUrlScheme(
args.artifact_url
)
# NOTE: This will hit the alpha Binauthz API until we promote this command
# to the beta surface or hardcode it e.g. to Beta.
api_version = apis.GetApiVersion(self.ReleaseTrack())
key_ref = args.CONCEPTS.keyversion.Parse()
key_id = args.public_key_id_override or kms.GetKeyUri(key_ref)
# TODO(b/138719072): Remove when validation is on by default
validation_enabled = 'validate' in args and args.validate
validation_callback = None
if args.attestor:
attestor_ref = args.CONCEPTS.attestor.Parse()
attestor = attestors.Client(api_version).Get(attestor_ref)
# TODO(b/79709480): Add other types of attestors if/when supported.
note_ref = resources.REGISTRY.ParseResourceId(
'containeranalysis.projects.notes',
attestor.userOwnedDrydockNote.noteReference,
{},
)
if validation_enabled:
validation_callback = functools.partial(
validation.validate_attestation,
attestor_ref=attestor_ref,
api_version=api_version,
)
else:
if key_id not in set(
pubkey.id for pubkey in attestor.userOwnedDrydockNote.publicKeys
):
log.warning(
'No public key with ID [%s] found on attestor [%s]',
key_id,
attestor.name,
)
console_io.PromptContinue(
prompt_string='Create and upload Attestation anyway?',
cancel_on_no=True,
)
elif args.note:
note_ref = args.CONCEPTS.note.Parse()
else:
# This code is unreachable since Args() handles this case.
raise exceptions.InvalidArgumentError(
'One of --attestor or --note must be provided.'
)
payload = binauthz_command_util.MakeSignaturePayload(args.artifact_url)
payload_for_signing = payload
if args.pae_encode_payload:
payload_for_signing = binauthz_command_util.PaeEncode(
args.dsse_type, payload.decode('utf-8')
)
kms_client = kms.Client()
pubkey_response = kms_client.GetPublicKey(key_ref.RelativeName())
sign_response = kms_client.AsymmetricSign(
key_ref.RelativeName(),
kms.GetAlgorithmDigestType(pubkey_response.algorithm),
payload_for_signing,
)
client = containeranalysis.Client(
ca_apis.GetApiVersion(self.ReleaseTrack())
)
return client.CreateAttestationOccurrence(
project_ref=project_ref,
note_ref=note_ref,
artifact_url=artifact_url_without_scheme,
public_key_id=key_id,
signature=sign_response.signature,
plaintext=payload,
validation_callback=validation_callback,
)

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 attestor management group for Binary Authorization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Attestors(base.Group):
"""Create and manage Google Binary Authorization Attestors."""

View File

@@ -0,0 +1,33 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Binary Authorization attestor.
description: |
Add an IAM policy binding to the IAM policy of a Binary Authorization attestor. One binding consists of a member,
a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of `roles/binaryauthorization.attestorsEditor` for the user `test-user@gmail.com`
on attestor `my_attestor`, run:
$ {command} my_attestor --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestorsEditor'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
`roles/binaryauthorization.attestorsEditor` and the user `test-user@gmail.com` on attestor `my_attestor`, run:
$ {command} my_attestor --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestorsEditor' --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.
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion
request:
collection: binaryauthorization.projects.attestors
arguments:
resource:
help_text: The Binary Authorization attestor whose IAM policy to add an IAM policy binding to.
spec: !REF googlecloudsdk.command_lib.container.resources:attestor

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Attestor command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
DETAILED_HELP = {
'DESCRIPTION':
"""
Create an Attestor.
""",
'EXAMPLES':
"""
To create an Attestor with an existing Note `projects/my_proj/notes/my_note`:
$ {command} \
my_new_attestor
--attestation-authority-note=my_note \
--attestation-authority-note-project=my_proj \
""",
}
@base.DefaultUniverseOnly
class Create(base.CreateCommand):
r"""Create an Attestor.
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
positional=True,
group_help='The attestor to be created.',
),
flags.GetNotePresentationSpec(
base_name='attestation-authority-note',
required=True,
positional=False,
group_help=textwrap.dedent("""\
The Container Analysis Note to which the created attestor will
be bound.
For the attestor to be able to access and use the Note,
the Note must exist and the active gcloud account (core/account)
must have the `containeranalysis.notes.listOccurrences` permission
for the Note. This can be achieved by granting the
`containeranalysis.notes.occurrences.viewer` role to the active
account for the Note resource in question.
"""),
),
)
parser.add_argument(
'--description', required=False, help='A description for the attestor')
def Run(self, args):
attestor_ref = args.CONCEPTS.attestor.Parse()
note_ref = args.CONCEPTS.attestation_authority_note.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
return attestors.Client(api_version).Create(
attestor_ref, note_ref, description=args.description)
# This is the user-visible help text for the command. Workaround for web
# version of help text not being generated correctly (b/319501293).
Create.detailed_help = DETAILED_HELP

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Delete Attestor command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
class Delete(base.DeleteCommand):
"""Delete an Attestor.
## EXAMPLES
To delete an existing Attestor `my_attestor`:
$ {command} my_attestor
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
positional=True, group_help='The attestor to be deleted.'),
)
def Run(self, args):
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
return attestors.Client(api_version).Delete(attestor_ref)

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Attestor command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
class Describe(base.DescribeCommand):
"""Describe an Attestor.
## EXAMPLES
To describe an existing Attestor `my_attestor`:
$ {command} my_attestor
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
positional=True, group_help='The attestor to describe.'),
)
def Run(self, args):
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
return attestors.Client(api_version).Get(attestor_ref)

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 an attestor."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import iam
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for an attestor.
Returns an empty policy if the resource does not have an existing IAM policy
set.
## EXAMPLES
The following command gets the IAM policy for the attestor `my_attestor`:
$ {command} my_attestor
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
positional=True,
group_help='The attestor whose IAM policy will be fetched.',
),
)
def Run(self, args):
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
return iam.Client(api_version).Get(attestor_ref)

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 attestors command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
class List(base.ListCommand):
"""List Attestors associated with the current project.
## EXAMPLES
To list attestors:
$ {command}
To list attestors in a verbose format (including
information about public keys associated with each attestor:
$ {command} --format=yaml
"""
@classmethod
def Args(cls, parser):
parser.display_info.AddFormat("""
table[box](
name.scope().segment(3):sort=1,
{note_field}.noteReference:label=NOTE,
{note_field}.publicKeys.len():label=NUM_PUBLIC_KEYS
)
""".format(note_field='userOwnedGrafeasNote' if cls.ReleaseTrack() ==
base.ReleaseTrack.GA else 'userOwnedDrydockNote'))
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
return attestors.Client(api_version).List(
util.GetProjectRef(), page_size=args.page_size, limit=args.limit)

View File

@@ -0,0 +1,152 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 public key management group for attestors."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class PublicKeys(base.Group):
r"""Create and manage public keys associated with Attestation Authorities.
## BACKGROUND
PGP is an encryption standard used by Binary Authorization to create and
verify attestations. A PGP identity is encapsulated by a "key" which can be
used to sign arbitrary data and/or verify signatures to be valid. As with
other asymmetric key cryptosystems, PGP keys have a "public" part and a
"private" part.
## PGP KEY STRUCTURE
An important feature of PGP keys is that they are hierarchical: Every "PGP
key" is composed of a "primary" key pair and zero or more "subkey" pairs
certified by the primary. These key pairs are collectively known as the "PGP
key." The "public" part of this PGP key contains the public keys of all the
constituent keys as well as all associated metadata (e.g. an email address).
And, as might be expected, the "private" part of the PGP key contains all
constituent private keys and metadata.
One property of subkeys is that they may be marked as "revoked" if they are
compromised or otherwise need to be retired. This does not remove the subkey
from the PGP key but simply adds metadata indicating this revocation. The
primary key pair cannot be revoked by this same mechanism.
### COMMON KEY STRUCTURE
The most common key structure is to have the primary key pair only used to
certify subkey pairs while the subkeys are used to encrypt and sign as
necessary. This allows the PGP key as a whole to act as a durable identity
even if an encryption key is used improperly or a signing key is compromised.
## USAGE IN BINARY AUTHORIZATION
- Authorities hold a set of PGP public keys that are used to verify
attestations.
- These must be submitted in ASCII-armored format. With GPG, this is
accomplished by adding the `--armor` flag to the export command.
- If any of the public keys held by an attestor verify a given attestation,
then the attestor considers that attestation to be valid (see gcloud alpha
container binauthz attestations create help for more details).
- As a result, the compromise of any constituent private key means that the
attestor is at risk. The compromised subkey should be revoked and the PGP
key re-uploaded or removed from the attestor.
## EXAMPLES
GPG is a common tool that implements the PGP standard.
- For general `gpg` usage examples, see gcloud alpha container binauthz help.
- For more detailed and complete documentation, see the GPG manual:
https://gnupg.org/documentation/manuals.html
To get the fingerprint of the public key:
```sh
$ gpg \
--with-colons \
--with-fingerprint \
--force-v4-certs \
--list-keys \
"${ATTESTING_USER}" | grep fpr | cut --delimiter=':' --fields 10
```
To export a public key:
```sh
$ gpg \
--armor \
--export "${FINGERPRINT}" \
--output public_key1.pgp
```
To add your new key to the attestor:
```sh
$ {command} add \
--attestor my_attestor \
--pgp-public-key-file=public_key1.pgp
```
To add a subkey to your PGP key:
```sh
$ gpg \
--quick-add-key ${FINGERPRINT} \
default \
sign
... FOLLOW PROMPTS ...
```
To revoke a subkey from your PGP key:
```sh
$ gpg \
--edit-key ${FINGERPRINT}
... SNIP ...
sec rsa2048/8C124F0F782DA097
created: 2018-01-01 expires: never usage: SCEA
trust: ultimate validity: ultimate
ssb rsa3072/C9597E8F28359AE3
created: 2018-01-01 expires: never usage: E
[ultimate] (1). User <attesting_user@example.com>
gpg> key C9597E8F28359AE3
... SNIP ...
gpg> revkey
... FOLLOW PROMPTS ...
```
To update the modified PGP key on the attestor:
```sh
$ {command} update \
${FINGERPRINT} \
--attestor=my_attestor \
--pgp-public-key-file=public_key1_updated.pgp
```
To remove this new key from the attestor:
```sh
$ {command} remove \
${FINGERPRINT} \
--attestor my_attestor
```
"""

View File

@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Add Attestor public key command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.api_lib.container.binauthz import kms
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import exceptions
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import pkix
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Add(base.Command):
r"""Add a public key to an Attestor.
## EXAMPLES
To add a new KMS public key to an existing Attestor `my_attestor`:
$ {command} \
--attestor=my_attestor \
--keyversion-project=foo \
--keyversion-location=us-west1 \
--keyversion-keyring=aring \
--keyversion-key=akey \
--keyversion=1
To add a new PGP public key to an existing Attestor `my_attestor`:
$ {command} \
--attestor=my_attestor \
--pgp-public-key-file=my_key.pub
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
required=True,
positional=False,
group_help=(
'The attestor to which the public key should be added.'),
),
)
parser.add_argument(
'--comment', help='The comment describing the public key.')
key_group = parser.add_mutually_exclusive_group(required=True)
pgp_group = key_group.add_group(help='PGP key definition')
pgp_group.add_argument(
'--pgp-public-key-file',
type=arg_parsers.FileContents(),
help='The path to the file containing the '
'ASCII-armored PGP public key to add.')
kms_group = key_group.add_group(help='Cloud KMS key definition')
flags.AddConcepts(
kms_group,
flags.GetCryptoKeyVersionPresentationSpec(
base_name='keyversion',
required=True,
positional=False,
use_global_project_flag=False,
group_help=textwrap.dedent("""\
The Cloud KMS (Key Management Service) CryptoKeyVersion whose
public key will be added to the attestor.""")),
)
pkix_group = key_group.add_group(help='PKIX key definition')
pkix_group.add_argument(
'--pkix-public-key-file',
required=True,
type=arg_parsers.FileContents(),
help='The path to the file containing the PKIX public key to add.')
pkix_group.add_argument(
'--pkix-public-key-algorithm',
choices=pkix.GetAlgorithmMapper().choices,
required=True,
help=textwrap.dedent("""\
The signing algorithm of the associated key. This will be used to
verify the signatures associated with this key."""))
parser.add_argument(
'--public-key-id-override',
type=str,
help=textwrap.dedent("""\
If provided, the ID to replace the default API-generated one. All IDs
must be valid URIs as defined by RFC 3986
(https://tools.ietf.org/html/rfc3986).
When creating Attestations to be verified by this key, one must always
provide this custom ID as the public key ID."""))
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
attestors_client = attestors.Client(api_version)
attestor_ref = args.CONCEPTS.attestor.Parse()
if args.pgp_public_key_file and args.public_key_id_override:
raise exceptions.InvalidArgumentError(
'--public-key-id-override may not be used with old-style PGP keys')
if args.keyversion:
key_resource = args.CONCEPTS.keyversion.Parse()
public_key = kms.Client().GetPublicKey(key_resource.RelativeName())
return attestors_client.AddPkixKey(
attestor_ref,
pkix_pubkey_content=public_key.pem,
pkix_sig_algorithm=attestors_client.ConvertFromKmsSignatureAlgorithm(
public_key.algorithm),
id_override=(args.public_key_id_override or
kms.GetKeyUri(key_resource)),
comment=args.comment)
elif args.pkix_public_key_file:
alg_mapper = pkix.GetAlgorithmMapper(api_version)
return attestors_client.AddPkixKey(
attestor_ref,
pkix_pubkey_content=args.pkix_public_key_file,
pkix_sig_algorithm=alg_mapper.GetEnumForChoice(
args.pkix_public_key_algorithm),
id_override=args.public_key_id_override,
comment=args.comment)
else:
# TODO(b/71700164): Validate the contents of the public key file.
return attestors_client.AddPgpKey(
attestor_ref,
pgp_pubkey_content=args.pgp_public_key_file,
comment=args.comment)

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Attestor public key command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
class Remove(base.Command):
r"""Remove a public key from an Attestor.
## EXAMPLES
To remove a public key from the Attestor `my_attestor`:
$ {command} 0638AADD940361EA2D7F14C58C124F0E663DA097 \
--attestor=my_attestor
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
required=True,
positional=False,
group_help=(
'The attestor from which the public key should be removed.'),
),
)
parser.add_argument(
'public_key_id',
help='The ID of the public key to remove.')
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
attestors_client = attestors.Client(api_version)
attestor_ref = args.CONCEPTS.attestor.Parse()
attestors_client.RemoveKey(attestor_ref, pubkey_id=args.public_key_id)

View File

@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Attestor public key command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Update(base.UpdateCommand):
r"""Update a public key on an Attestor.
## EXAMPLES
To update a PGP public key on an existing Attestor `my_attestor`:
$ {command} \
0638AADD940361EA2D7F14C58C124F0E663DA097 \
--attestor=my_attestor \
--pgp-public-key-file=my_key.pub
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
required=True,
positional=False,
group_help=(
'The attestor on which the public key should be updated.'),
),
)
parser.add_argument(
'public_key_id',
help='The ID of the public key to update.')
parser.add_argument(
'--pgp-public-key-file',
type=arg_parsers.FileContents(),
help='The path to a file containing the '
'updated ASCII-armored PGP public key.')
parser.add_argument(
'--comment', help='The comment describing the public key.')
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
attestors_client = attestors.Client(api_version)
attestor_ref = args.CONCEPTS.attestor.Parse()
# TODO(b/71700164): Validate the contents of the public key file.
return attestors_client.UpdateKey(
attestor_ref,
args.public_key_id,
pgp_pubkey_content=args.pgp_public_key_file,
comment=args.comment)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class UpdateAlpha(base.UpdateCommand):
"""Update a public key on an Attestor."""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
required=True,
positional=False,
group_help=(
'The attestor on which the public key should be updated.'),
),
)
parser.add_argument(
'public_key_id',
help='The ID of the public key to update.')
parser.add_argument(
'--pgp-public-key-file',
type=arg_parsers.FileContents(),
help='The path to a file containing the '
'updated ASCII-armored PGP public key.')
parser.add_argument(
'--comment', help='The comment describing the public key.')
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
attestors_client = attestors.Client(api_version)
attestor_ref = args.CONCEPTS.attestor.Parse()
# TODO(b/71700164): Validate the contents of the public key file.
return attestors_client.UpdateKey(
attestor_ref,
args.public_key_id,
pgp_pubkey_content=args.pgp_public_key_file,
comment=args.comment)

View File

@@ -0,0 +1,33 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Binary Authorization attestor.
description: |
Remove an IAM policy binding from the IAM policy of a Binary Authorization attestor. One binding consists of a
member, a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of `roles/binaryauthorization.attestorsEditor` for the user `test-user@gmail.com`
on attestor `my_attestor`, run:
$ {command} my_attestor --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestorsEditor'
To remove an IAM policy binding which expires at the end of the year 2018 for the role of
`roles/binaryauthorization.attestorsEditor` and the user `test-user@gmail.com` on attestor `my_attestor`, run:
$ {command} my_attestor --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestorsEditor' --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.
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion
request:
collection: binaryauthorization.projects.attestors
arguments:
resource:
help_text: The Binary Authorization attestor whose IAM policy to remove an IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.container.resources:attestor

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 an attestor."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import iam
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
@base.DefaultUniverseOnly
class SetIamPolicy(base.Command):
"""Set the IAM policy for an attestor.
See https://cloud.google.com/iam/docs/managing-policies for details of
the policy file format and contents.
## EXAMPLES
The following command will read an IAM policy defined in a JSON file
'iam_policy.json' and set it for the attestor `my_attestor`:
$ {command} my_attestor iam_policy.json
"""
# The above text is based on output of
# iam_util.GetDetailedHelpForSetIamPolicy.
@classmethod
def Args(cls, parser):
parser.add_argument(
'attestor_name',
help=('The name of the attestor '
'whose IAM policy will be '
'updated.'))
parser.add_argument(
'policy_file',
help=('The JSON or YAML '
'file containing the IAM policy.'))
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = iam.Client(api_version)
attestor_ref = util.GetAttestorRef(args.attestor_name)
policy, _ = iam_util.ParseYamlOrJsonPolicyFile(args.policy_file,
client.messages.IamPolicy)
result = client.Set(attestor_ref, policy)
iam_util.LogSetIamPolicy(attestor_ref.Name(), 'attestor')
return result

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Attestor command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import attestors
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
class Update(base.UpdateCommand):
r"""Update an existing Attestor.
## EXAMPLES
To update an existing Attestor `my_attestor`:
$ {command} my_attestor \
--description="my new attestor description"
"""
@classmethod
def Args(cls, parser):
flags.AddConcepts(
parser,
flags.GetAttestorPresentationSpec(
positional=True, group_help='The attestor to update.'),
)
parser.add_argument(
'--description',
required=False,
help='The new description for the attestor')
def Run(self, args):
attestor_ref = args.CONCEPTS.attestor.Parse()
api_version = apis.GetApiVersion(self.ReleaseTrack())
return attestors.Client(api_version).Update(
attestor_ref, description=args.description)

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.
"""The continuous-validation group for Binary Authorization."""
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 ContinuousValidation(base.Group):
"""Manage Binary Authorization Continuous Validation."""

View File

@@ -0,0 +1,53 @@
# -*- 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.
"""Describe command: show the configuration of Binary Authorization Continuous Validation for the project."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import continuous_validation
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Describe(base.DescribeCommand):
"""Show the configuration of Binary Authorization Continuous Validation for the project.
The output will contain "enabled: true" if Continuous Validation is enabled
for the project, or "enabled: false" if CV is disabled.
## EXAMPLES
$ {command}
enforcementPolicyConfig:
enabled: true
name: projects/my-project/continuousValidationConfig
updateTime: '2020-01-01T01:23:45.678901234Z'
"""
@staticmethod
def Args(parser):
# Change the output format to exclude irrelevant parts of the response.
parser.display_info.AddFormat(continuous_validation.CV_CONFIG_OUTPUT_FORMAT)
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
cv_config = continuous_validation.Client(api_version).Get(
util.GetCvConfigRef())
return continuous_validation.EnsureEnabledFalseIsShown(cv_config)

View File

@@ -0,0 +1,45 @@
# -*- 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.
"""Disable Binary Authorization Continuous Validation for the project."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import continuous_validation
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Disable(base.UpdateCommand):
"""Disable Binary Authorization Continuous Validation for the project.
## EXAMPLES
To disable Continuous Validation for the project:
$ {command}
"""
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = continuous_validation.Client(api_version)
cv_config = client.Get(util.GetCvConfigRef())
cv_config.enforcementPolicyConfig.enabled = False
return client.Set(util.GetCvConfigRef(), cv_config)

View File

@@ -0,0 +1,45 @@
# -*- 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.
"""Enable Binary Authorization Continuous Validation for the project."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import continuous_validation
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Enable(base.UpdateCommand):
"""Enable Binary Authorization Continuous Validation for the project.
## EXAMPLES
To enable Continuous Validation for the project:
$ {command}
"""
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = continuous_validation.Client(api_version)
cv_config = client.Get(util.GetCvConfigRef())
cv_config.enforcementPolicyConfig.enabled = True
return client.Set(util.GetCvConfigRef(), cv_config)

View File

@@ -0,0 +1,53 @@
# -*- 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 CreateSignaturePayload command for Binary Authorization signatures."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags as binauthz_flags
from googlecloudsdk.command_lib.container.binauthz import util as binauthz_command_util
@base.DefaultUniverseOnly
class CreateSignaturePayload(base.Command):
r"""Create a JSON container image signature object.
Given a container image URL specified by the manifest digest, this command
will produce a JSON object whose signature is expected by Cloud Binary
Authorization.
## EXAMPLES
To output serialized JSON to sign, run:
$ {command} \
--artifact-url="gcr.io/example-project/example-image@sha256:abcd"
"""
@classmethod
def Args(cls, parser):
binauthz_flags.AddArtifactUrlFlag(parser)
parser.display_info.AddFormat('object')
def Run(self, args):
# Dumping a bytestring to the object formatter doesn't output the string in
# PY3 but rather the repr of the object. Decoding it to a unicode string
# achieves the desired result.
payload_bytes = binauthz_command_util.MakeSignaturePayload(
args.artifact_url)
return payload_bytes.decode('utf-8')

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Policy base group for Binary Authorization policy management."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Policy(base.Group):
"""Create and manage Google Binary Authorization policies."""

View File

@@ -0,0 +1,34 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Binary Authorization policy.
description: |
Add an IAM policy binding to the IAM policy of a Binary Authorization policy. One binding consists of a member,
a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/binaryauthorization.attestationAuthoritiesEditor' for the user 'test-user@gmail.com'
on the current project's Binary Authorization policy, run:
$ {command} --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestationAuthoritiesEditor'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/binaryauthorization.attestationAuthoritiesEditor' and the user 'test-user@gmail.com'
on the current project's Binary Authorization policy, run:
$ {command} --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestationAuthoritiesEditor' --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.
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion
request:
collection: binaryauthorization.projects.policy
arguments:
resource:
help_text: The Binary Authorization policy whose IAM policy to add an IAM policy binding to.
spec: !REF googlecloudsdk.command_lib.container.resources:policy

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import parsing
import six
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class Create(base.CreateCommand):
r"""Create a Binary Authorization platform policy.
## EXAMPLES
To create a policy for GKE in the current project:
$ {command} my-policy --platform=gke --policy-file=my_policy.yaml
To create a policy for GKE in a specific project:
$ {command} my-policy --platform=gke --project=my-project-id \
--policy-file=my_policy.yaml
or
$ {command} /projects/my-project-id/platforms/gke/policies/my-policy
\
--policy-file=my_policy.yaml
"""
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to create')
parser.add_argument(
'--policy-file',
required=True,
help='The JSON or YAML file containing the new policy.')
parser.display_info.AddFormat('yaml')
def Run(self, args):
"""Runs the command.
Args:
args: argparse.Namespace with command-line arguments.
Returns:
The policy resource.
"""
policy_resource_name = args.CONCEPTS.policy_resource_name.Parse()
# Load the policy file into a Python dict.
policy_obj = parsing.LoadResourceFile(
# Avoid 'u' prefix in Python 2 when this file path gets embedded in
# error messages.
six.ensure_str(args.policy_file))
# Decode the dict into a PlatformPolicy message, allowing DecodeErrors to
# bubble up to the user if they are raised.
policy = messages_util.DictToMessageWithErrorCheck(
policy_obj,
# The API is only available in v1.
apis.GetMessagesModule('v1').PlatformPolicy)
return platform_policy.Client('v1').Create(policy_resource_name, policy)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.core import log
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class Delete(base.DeleteCommand):
"""Delete a Binary Authorization platform policy.
## EXAMPLES
To delete a policy using its resource name:
$ {command} projects/my_proj/platforms/gke/policies/policy1
To delete the same policy using flags:
$ {command} policy1 --platform=gke --project=my_proj
"""
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to delete')
def Run(self, args):
policy_ref = args.CONCEPTS.policy_resource_name.Parse().RelativeName()
# The API is only available in v1.
result = platform_policy.Client('v1').Delete(policy_ref)
log.DeletedResource(policy_ref, kind='Policy')
return result

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class Describe(base.DescribeCommand):
"""Describe a Binary Authorization platform policy.
## EXAMPLES
To describe an existing policy using its resource name:
$ {command} projects/my_proj/platforms/gke/policies/policy1
To describe the same policy using flags:
$ {command} policy1 --platform=gke --project=my_proj
"""
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to describe')
def Run(self, args):
policy_ref = args.CONCEPTS.policy_resource_name.Parse().RelativeName()
# The API is only available in v1.
return platform_policy.Client('v1').Describe(policy_ref)

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Evaluate policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import parsing
from googlecloudsdk.command_lib.container.binauthz import util
from googlecloudsdk.core.exceptions import Error
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class Evaluate(base.Command):
"""Evaluate a Binary Authorization platform policy.
## EXAMPLES
To evaluate a policy using its resource name:
$ {command} projects/my-proj/platforms/gke/policies/my-policy
--resource=$KUBERNETES_RESOURCE
To evaluate the same policy using flags against an image:
$ {command} my-policy --platform=gke --project=my-proj --image=$IMAGE
"""
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to evaluate')
flags.AddEvaluationUnitArg(parser)
def Run(self, args):
policy_ref = args.CONCEPTS.policy_resource_name.Parse().RelativeName()
platform_id = policy_ref.split('/')[3]
if platform_id != 'gke':
raise Error(
"Found unsupported platform '{}'. Currently only 'gke' platform "
"policies are supported.".format(platform_id)
)
if args.resource:
resource_obj = parsing.LoadResourceFile(args.resource)
response = platform_policy.Client('v1').Evaluate(
policy_ref, resource_obj, False
)
else:
pod_spec = util.GeneratePodSpecFromImages(args.image)
response = platform_policy.Client('v1').Evaluate(
policy_ref, pod_spec, False
)
# Set non-zero exit code for non-conformant verdicts to improve the
# command's scriptability.
if (
response.verdict
!= apis.GetMessagesModule(
'v1'
).EvaluateGkePolicyResponse.VerdictValueValuesEnum.CONFORMANT
):
self.exit_code = 2
return response

View File

@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Evaluate policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import parsing
from googlecloudsdk.command_lib.container.binauthz import sigstore_image
from googlecloudsdk.command_lib.container.binauthz import util
from googlecloudsdk.core import log
from googlecloudsdk.core import yaml
from googlecloudsdk.core.exceptions import Error
@base.Hidden
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class EvaluateAndSign(base.Command):
"""Evaluate a Binary Authorization platform policy and sign the results, if conformant.
## EXAMPLES
To evaluate and sign a policy using its resource name:
$ {command} projects/my-proj/platforms/gke/policies/my-policy
--resource=$KUBERNETES_RESOURCE
To evaluate the same policy using flags against multiple images:
$ {command} my-policy --platform=gke --project=my-proj --image=$IMAGE1
--image=$IMAGE2
To return a modified resource with attestations added as an annotation on the
input resource, without uploading attestations to the registry:
$ {command} projects/my-proj/platforms/gke/policies/my-policy
--resource=$KUBERNETES_RESOURCE --output-file=$MODIFIED_RESOURCE --no-upload
To upload attestations using Docker credentials located in a custom directory:
$ {command} projects/my-proj/platforms/gke/policies/my-policy
--image=$IMAGE --use-docker-creds --docker-config-dir=$CUSTOM_DIR
"""
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to evaluate and sign')
flags.AddEvaluationUnitArg(parser)
flags.AddNoUploadArg(parser)
flags.AddOutputFileArg(parser)
flags.AddDockerCredsArgs(parser)
def Run(self, args):
policy_ref = args.CONCEPTS.policy_resource_name.Parse().RelativeName()
platform_id = policy_ref.split('/')[3]
if platform_id != 'gke':
raise Error(
"Found unsupported platform '{}'. Currently only 'gke' platform "
'policies are supported.'.format(platform_id)
)
if args.output_file and not args.resource:
raise util.Error('Cannot specify --output-file without --resource.')
if args.use_docker_creds and args.no_upload:
raise util.Error('Cannot specify --use-docker-creds with --no-upload.')
if args.docker_config_dir and not args.use_docker_creds:
raise util.Error(
'Cannot specify --docker-config-dir without --use-docker-creds.'
)
if args.resource:
resource_obj = parsing.LoadResourceFile(args.resource)
else:
resource_obj = util.GeneratePodSpecFromImages(args.image)
response = platform_policy.Client('v1').Evaluate(
policy_ref, resource_obj, True
)
# Set non-zero exit code for non-conformant verdicts to improve the
# command's scriptability.
if (
response.verdict
!= apis.GetMessagesModule(
'v1'
).EvaluateGkePolicyResponse.VerdictValueValuesEnum.CONFORMANT
):
self.exit_code = 2
return response
# Upload attestations.
if not args.no_upload:
for attestation in response.attestations:
image_url = sigstore_image.AttestationToImageUrl(attestation)
log.Print('Uploading attestation for {}'.format(image_url))
sigstore_image.UploadAttestationToRegistry(
image_url=image_url,
attestation=sigstore_image.StandardOrUrlsafeBase64Decode(
attestation
),
use_docker_creds=args.use_docker_creds,
docker_config_dir=args.docker_config_dir,
)
# Write inline attestations.
if args.output_file:
modified_resource = util.AddInlineAttestationsToResource(
resource_obj, response.attestations
)
if (
parsing.GetResourceFileType(args.resource)
== parsing.ResourceFileType.YAML
):
modified_resource = yaml.dump(modified_resource)
log.WriteToFileOrStdout(
args.output_file,
modified_resource,
overwrite=True,
binary=False,
private=True,
)
return response

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Export Binary Authorization policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import policies
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
from googlecloudsdk.core.exceptions import Error
OLD_SYSTEM_POLICY_PROJECT_NAME = 'binauthz-global-policy'
@base.DefaultUniverseOnly
class Export(base.Command):
"""Export the Binary Authorization policy for the current project.
This function's default output is a valid policy YAML file. If dumped to a
file and edited, the new policy can be provided to the `$ {parent_command}
import` command to cause these edits to be reflected in the project policy.
## EXAMPLES
To export the current project's policy:
$ {command} > my_policy.yaml
"""
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
ref = util.GetPolicyRef()
if ref.Name() == OLD_SYSTEM_POLICY_PROJECT_NAME:
raise Error(
'The Binary Authorization system policy is no longer accessible via '
'the binauthz-global-policy project. Use the following command to '
'display the system policy:\n'
' $ gcloud alpha container binauthz policy export-system-policy\n'
'For details, see https://cloud.google.com/binary-authorization/docs/'
'key-concepts#google-maintained_system_images.')
return policies.Client(api_version).Get(ref)

View File

@@ -0,0 +1,68 @@
# -*- 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.
"""Export Binary Authorization system policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import system_policy
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import arg_parsers
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class ExportSystemPolicy(base.Command):
"""Export the Binary Authorization system policy.
For reliability reasons, the system policy is updated one region at a time.
Because of this precaution, the system policy can differ between regions
during an update. Use --location to view the system policy of a specific
region.
If --location is not specified, an arbitrary region is used. (Specifically, a
region in the last group of regions to receive updates. Since most changes are
additions, this will show the minimal set of system images that are allowed
in all regions.)
## EXAMPLES
To view the system policy:
$ {command}
To view the system policy in the region us-central1:
$ {command} --location=us-central1
"""
@classmethod
def Args(cls, parser):
parser.add_argument(
'--location',
# Although the name is "location" for consistency with other gcloud
# commands, only regions are allowed (not other locations, like zones).
choices=arg_parsers.BINAUTHZ_ENFORCER_REGIONS,
required=False,
default='global',
help='The region for which to get the system policy (or "global").')
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
return system_policy.Client(api_version).Get(
util.GetSystemPolicyRef(args.location))

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Binary Authorization policy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import iam
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for a Binary Authorization policy.
Returns an empty policy if the resource does not have an existing IAM policy
set.
## EXAMPLES
The following command gets the IAM policy for the current project's Binary
Authorization policy:
$ {command}
"""
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
return iam.Client(api_version).Get(util.GetPolicyRef())

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Binary Authorization policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import policies
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import arg_parsers
from googlecloudsdk.command_lib.container.binauthz import parsing
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
# TODO(b/77499756): Add help text for etags here (or maybe to the group help).
class Import(base.Command):
"""Import a Binary Authorization policy to the current project.
This command accepts a description of the desired policy in the form of a
YAML-formatted file. A representation of the current policy can be retrieved
using the $ {parent_command} export command. One method of modifying the
policy is to run `$ {parent_command} export`, dump the contents to a file,
modify the policy file to reflect the desired new policy, and provide this
modified file to `$ {command}`.
## EXAMPLES
To update the current project's policy:
$ {parent_command} export > my_policy.yaml
$ edit my_policy.yaml
$ {command} my_policy.yaml
"""
@classmethod
def Args(cls, parser):
parser.add_argument(
'policy_file',
type=arg_parsers.PolicyFileName,
help='The file containing the YAML-formatted policy description.')
parser.add_argument(
'--strict-validation',
action='store_true',
required=False,
help='Whether to perform additional checks on the validity of policy '
'contents.')
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
messages = apis.GetMessagesModule(api_version)
# Load the policy file into a Python object.
policy_obj = parsing.LoadResourceFile(args.policy_file)
if not policy_obj:
# NOTE: This is necessary because apitools falls over when you provide it
# with None and that's what the yaml returns when passed an empty string.
policy_obj = {}
# Make sure the user meant to do this.
log.warning('Empty Policy provided!')
console_io.PromptContinue(
prompt_string='Do you want to import an empty policy?',
cancel_on_no=True)
# Decode the dict into a Policy message, allowing DecodeErrors to bubble up
# to the user if they are raised.
policy = messages_util.DictToMessageWithErrorCheck(
policy_obj, messages.Policy)
return policies.Client(api_version).Set(util.GetPolicyRef(), policy)

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class List(base.ListCommand):
"""List Binary Authorization platform policies.
## EXAMPLES
To list the policies for GKE in the current project:
$ {command} gke
To list the policies for GKE in a specific project:
$ {command} gke --project=my-project-id
or
$ {command} projects/my-project-id/gke
"""
@staticmethod
def Args(parser):
flags.AddPlatformResourceArg(parser, 'to list')
parser.display_info.AddFormat('list(name,description)')
def Run(self, args):
platform_ref = args.CONCEPTS.platform_resource_name.Parse().RelativeName()
# The API is only available in v1.
return platform_policy.Client('v1').List(
platform_ref, page_size=args.page_size, limit=args.limit)

View File

@@ -0,0 +1,34 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Binary Authorization policy.
description: |
Remove an IAM policy binding from the IAM policy of a Binary Authorization policy. One binding consists of a member,
a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of 'roles/binaryauthorization.attestationAuthoritiesEditor' for the user 'test-user@gmail.com'
on the current project's Binary Authorization policy, run:
$ {command} --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestationAuthoritiesEditor'
To remove an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/binaryauthorization.attestationAuthoritiesEditor' and the user 'test-user@gmail.com'
on the current project's Binary Authorization policy, run:
$ {command} --member='user:test-user@gmail.com' --role='roles/binaryauthorization.attestationAuthoritiesEditor' --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.
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: options_requestedPolicyVersion
request:
collection: binaryauthorization.projects.policy
arguments:
resource:
help_text: The Binary Authorization policy whose IAM policy to remove an IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.container.resources:policy

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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 Binary Authorization policy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import iam
from googlecloudsdk.api_lib.container.binauthz import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
@base.DefaultUniverseOnly
class SetIamPolicy(base.Command):
"""Set the IAM policy for a Binary Authorization policy.
See https://cloud.google.com/iam/docs/managing-policies for details of
the policy file format and contents.
## EXAMPLES
The following command will read an IAM policy defined in a JSON file
'iam_policy.json' and set it for the current project's Binary Authorization
policy:
$ {command} iam_policy.json
"""
# The above text is based on output of
# iam_util.GetDetailedHelpForSetIamPolicy.
@classmethod
def Args(cls, parser):
parser.add_argument(
'policy_file',
help=('The JSON or YAML '
'file containing the IAM policy.'))
def Run(self, args):
api_version = apis.GetApiVersion(self.ReleaseTrack())
client = iam.Client(api_version)
policy_ref = util.GetPolicyRef()
policy, _ = iam_util.ParseYamlOrJsonPolicyFile(args.policy_file,
client.messages.IamPolicy)
result = client.Set(policy_ref, policy)
iam_util.LogSetIamPolicy(policy_ref.Name(), 'policy')
return result

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 policy command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.container.binauthz import apis
from googlecloudsdk.api_lib.container.binauthz import platform_policy
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.container.binauthz import flags
from googlecloudsdk.command_lib.container.binauthz import parsing
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class Update(base.UpdateCommand):
# pylint: disable=line-too-long
"""Update a Binary Authorization platform policy.
## EXAMPLES
To update an existing policy using its resource name:
$ {command} projects/my_proj/platforms/gke/policies/policy1 --policy-file=policy1.json
To update the same policy using flags:
$ {command} policy1 --platform=gke --project=my_proj --policy-file=policy1.json
"""
# pylint: enable=line-too-long
@staticmethod
def Args(parser):
flags.AddPlatformPolicyResourceArg(parser, 'to update')
parser.add_argument(
'--policy-file',
required=True,
help='The JSON or YAML file containing the new policy.')
parser.display_info.AddFormat('yaml')
def Run(self, args):
# The API is only available in v1.
messages = apis.GetMessagesModule('v1')
policy_ref = args.CONCEPTS.policy_resource_name.Parse().RelativeName()
# Load the policy file into a Python dict.
policy_obj = parsing.LoadResourceFile(args.policy_file)
# Decode the dict into a PlatformPolicy message, allowing DecodeErrors to
# bubble up to the user if they are raised.
policy = messages_util.DictToMessageWithErrorCheck(policy_obj,
messages.PlatformPolicy)
return platform_policy.Client('v1').Update(policy_ref, policy)