Signing and verifying Yandex Container Registry Docker images in Yandex Managed Service for Kubernetes
This scenario describes how to sign Docker images using Cosign
To sign Docker images and set up their verification:
If you no longer need the resources you created, delete them.
Getting started
Prepare the infrastructure
-
- Service account for resources with the editor role for the folder where the Managed Service for Kubernetes cluster is created. This service account will be used to create the resources required for the Managed Service for Kubernetes cluster.
- Service account for nodes with the container-registry.images.puller role for the folder with the Docker image registry. Nodes will pull the required Docker images from the registry on behalf of this account.
You can use the same service account for both operations.
-
Create security groups for the Managed Service for Kubernetes cluster and its node groups.
Warning
The configuration of security groups determines the performance and availability of the cluster and the services and applications running in it.
-
Create a Managed Service for Kubernetes cluster and a node group. When creating a cluster, specify the previously created service accounts for resources and nodes and the security group.
-
If you do not have Terraform yet, install it.
-
Get the authentication credentials. You can add them to environment variables or specify them later in the provider configuration file.
-
Configure and initialize a provider. There is no need to create a provider configuration file manually, you can download it
. -
Place the configuration file in a separate working directory and specify the parameter values. If you did not add the authentication credentials to environment variables, specify them in the configuration file.
-
Download the k8s-validate-cr-image.tf
configuration file to the same working directory.This file describes:
-
Container Registry registry.
-
Managed Service for Kubernetes cluster.
-
Service account required for the Managed Service for Kubernetes cluster and node group to operate.
-
Security groups which contain rules required for the Managed Service for Kubernetes cluster and its node groups.
Warning
The configuration of security groups determines the performance and availability of the cluster and the services and applications running in it.
-
Specify the following in the
k8s-validate-cr-image.tf
file:- Folder ID.
- Kubernetes version for the Managed Service for Kubernetes cluster and node groups.
- Managed Service for Kubernetes cluster CIDR.
- Name of the cluster service account.
- Name of the Container Registry registry.
-
Check that the Terraform configuration files are correct using this command:
terraform validate
If there are any errors in the configuration files, Terraform will point them out.
-
Create the required infrastructure:
-
Run the command to view planned changes:
terraform plan
If the resource configuration descriptions are correct, the terminal will display a list of the resources to modify and their parameters. This is a test step. No resources are updated.
-
If you are happy with the planned changes, apply them:
-
Run the command:
terraform apply
-
Confirm the update of resources.
-
Wait for the operation to complete.
-
All the required resources will be created in the specified folder. You can check resource availability and their settings in the management console
. -
Before you start working with the Managed Service for Kubernetes cluster
-
Install kubectl
and configure it to work with the created cluster. - Install the Kubernetes Helm package manager
.
Add multiple Docker images to the Container Registry registry
- Configure Docker and get authenticated in Container Registry.
- Create multiple Docker images. One image will be signed using Cosign, while others will remain unsigned.
- Push Docker images to the Container Registry registry.
Sign a Docker image using Cosign
-
Install a special Cosign build for your OS:
-
Get an Yandex Identity and Access Management token and save it to the
$YC_IAM_TOKEN
environment variable:-
Bash:
export YC_IAM_TOKEN=$(yc iam create-token)
-
PowerShell:
$env:YC_IAM_TOKEN = $(yc iam create-token)
-
-
Log in to Container Registry:
-
Bash:
docker login \ --username iam \ --password $YC_IAM_TOKEN \ cr.yandex
-
PowerShell:
docker login ` --username iam ` --password $Env:YC_IAM_TOKEN ` cr.yandex
Result:
WARNING! Using --password via the CLI is insecure. Use --password-stdin. Login Succeeded
Note
To avoid using a credential helper for authentication, edit the
${HOME}/.docker/config.json
configuration file to delete thecr.yandex
domain line from thecredHelpers
section. -
-
Create a digital signature key pair and save it to Key Management Service:
cosign generate-key-pair \ --kms yckms:///folder/<folder_ID>/keyname/<key_pair_name>
Where:
<folder_ID>
: ID of the folder to save the new key pair to.<key_pair_name>
: Name of the signature key pair you are creating.
Result:
client.go:183: Using IAM Token from 'YC_IAM_TOKEN' environment variable as credentials client.go:310: generated yckms KEY_ID: '<key_pair_ID>' Public key written to cosign.pub
The utility will return the ID of the created signature key pair and save a public signature key to a local file. Save the key pair ID, you will need it in the next steps.
You can always get the ID of your signature key pair in the management console
or using a CLI command. -
Sign the image in Container Registry:
cosign sign \ --key yckms:///<key_pair_ID> \ cr.yandex/<registry_ID>/<Docker_image_name>:<tag> \ --tlog-upload=false
Where:
<key_pair_ID>
: ID of the signature key pair you got in the previous step.<registry_ID>
: ID of the Container Registry registry the image for signing is in.<Docker_image_name>
: Name of the Docker image you are signing in Container Registry.<tag>
: Tag of the image version to sign.
Result:
Pushing signature to: cr.yandex/<registry_ID>/<Docker_image_name>
A second object with the
sha256-....sig
tag andcr.yandex/<registry_ID>/<Docker_image_name>@sha256:...
hash should appear in the Container Registry registry. -
Check manually that the Docker image signature is correct:
cosign verify \ --key yckms:///<key_pair_ID> \ cr.yandex/<registry_ID>/<Docker_image_name>:<tag> \ --insecure-ignore-tlog
Where:
<key_pair_ID>
: Signature key pair ID you got earlier.<registry_ID>
: ID of the Container Registry registry the image is in.<Docker_image_name>
: Docker image name in the Container Registry registry.<tag>
: Tag of the image version to verify the signature for.
Result:
Verification for cr.yandex/<registry_ID>/<Docker_image_name>:<tag> -- The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key [{"critical":{"identity":{"docker-reference":"cr.yandex/<registry_ID>/<Docker_image_name>"},"image":{"docker-manifest-digest":"sha256:..."},"type":"cosign container image signature"},"optional":null}]
-
Generate a key pair using Cosign:
cosign generate-key-pair
Set a private key's password and enter it twice.
Result:
Enter password for private key: Enter password for private key again: Private key written to cosign.key Public key written to cosign.pub
-
Sign the Docker image in the Container Registry registry:
cosign sign \ --key cosign.key \ cr.yandex/<registry_ID>/<Docker_image_name>:<tag>
The signed image will be used when checking results.
Enter the password for the private key. Result:
Enter password for private key: Pushing signature to: cr.yandex/<registry_ID>/<Docker_image_name>
A second object with the
sha256-....sig
tag andcr.yandex/<registry_ID>/<Docker_image_name>@sha256:...
hash should appear in the Container Registry registry. -
Check manually that the Docker image signature is correct:
cosign verify \ --key cosign.pub \ cr.yandex/<registry_ID>/<Docker_image_name>:<tag>
Result:
Verification for cr.yandex/<registry_ID>/<Docker_image_name>:<tag> -- The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key [{"critical":{"identity":{"docker-reference":"cr.yandex/<registry_ID>/<Docker_image_name>"},"image":{"docker-manifest-digest":"sha256:..."},"type":"cosign container image signature"},"optional":null}]
Create a policy for signature verification
-
Create an authorized key for the service account with the container-registry.images.puller role and save it to the file:
yc iam key create \ --service-account-name=<service_account_name> \ --output authorized-key.json
Where
--service-account-name
: Name of the service account with the container-registry.images.puller role. -
Install the Kyverno
app to the Managed Service for Kubernetes cluster. You need it to create a policy for verifying Docker image signatures.-
Add a repository named
kyverno
:helm repo add kyverno https://kyverno.github.io/kyverno/
Result:
"kyverno" has been added to your repositories
-
Install Kyverno to the
kyverno
namespace:helm install kyverno kyverno/kyverno \ --namespace kyverno \ --create-namespace \ --set replicaCount=1 \ --set imagePullSecrets.regcred.registry=cr.yandex \ --set imagePullSecrets.regcred.username=json_key \ --set-file imagePullSecrets.regcred.password=./authorized-key.json
Result:
NAME: kyverno LAST DEPLOYED: Thu Sep 8 10:43:00 2022 NAMESPACE: kyverno STATUS: deployed ...
-
-
Create a policy:
-
Save the
ClusterPolicy
creation specification to a YAML file namedpolicy.yaml
:apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: check-image spec: validationFailureAction: enforce background: false webhookTimeoutSeconds: 30 failurePolicy: Fail rules: - name: check-image match: any: - resources: kinds: - Pod verifyImages: - imageReferences: - "cr.yandex/<registry_ID>/*" attestors: - count: 1 entries: - keys: publicKeys: |- <cosign.pub_contents>
Sample filled out policy.yaml file
apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: check-image spec: validationFailureAction: enforce background: false webhookTimeoutSeconds: 30 failurePolicy: Fail rules: - name: check-image match: any: - resources: kinds: - Pod verifyImages: - imageReferences: - "cr.yandex/crpd2f2bnrlb********/*" attestors: - count: 1 entries: - keys: publicKeys: |- -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1jTu/9rJZZvUFi4bGhlvgMQdIY97 7NuGl2zzpV7olAyIu/WiywxI7Fny5tk6JmNPIFvSAtys3c08gfEc******** -----END PUBLIC KEY-----
Note
By default, when you create a policy, a signature verification request is made to the Transparency Log immutable record storage. You can disable it by adding the
rekor: ignoreTlog: true
parameter to thekeys
element of the policy specification. For more information, see the Kyverno documentation . -
Run this command:
kubectl apply -f ./policy.yaml
Result:
clusterpolicy.kyverno.io/check-image configured
-
-
(Optional) Install Policy Reporter in your Managed Service for Kubernetes cluster to be able to save and process policy results.
Check the result
-
Create a pod from the signed Docker image:
kubectl run pod --image=cr.yandex/<registry_ID>/<Docker_image_name>:<tag>
Result:
pod/pod created
-
Create a pod from an unsigned Docker image:
kubectl run pod2 --image=cr.yandex/<registry_ID>/<unsigned_Docker_image_name>:<tag>
Result:
Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request: resource Pod/default/pod2 was blocked due to the following policies check-image: check-image: failed to verify signature for cr.yandex/crpsere9njsa********/alpine:2.0: .attestors[0].entries[0].keys: no matching signatures:
Delete the resources you created
Some resources are not free of charge. To avoid paying for them, delete the resources you no longer need:
- Delete the Managed Service for Kubernetes cluster.
- If you reserved a public static IP address for the cluster, delete it.
- Delete the service accounts.
- Delete all Docker images from the Container Registry registry.
- Delete the Container Registry registry.
-
In the terminal window, go to the directory containing the infrastructure plan.
Warning
Make sure the directory has no Terraform manifests with the resources you want to keep. Terraform deletes all resources that were created using the manifests in the current directory.
-
Delete resources:
-
Run this command:
terraform destroy
-
Confirm deleting the resources and wait for the operation to complete.
All the resources described in the Terraform manifests will be deleted.
-