|
| 1 | +# Container Image Security Enforcement |
| 2 | + |
| 3 | +Container Image Security Enforcement enables trust within your container workloads. |
| 4 | + |
| 5 | +Container Image Security Enforcement verifies container images before deploying them to a cluster. You can control where images are deployed from, enforce Vulnerability Advisor policies, and ensure that content trust is properly applied to the image. If an image does not meet your policy requirements, the pod is not deployed to your cluster or updated. |
| 6 | + |
| 7 | +Container Image security is applied on a per-cluster basis. This can be enabled in for a cluster in IBM Cloud in a single command using the [IBM Cloud CLI](https://cloud.ibm.com/docs/cli). |
| 8 | + |
| 9 | +```shell |
| 10 | +ibmcloud oc cluster image-security enable -c <cluster name> |
| 11 | +``` |
| 12 | + |
| 13 | +This command will configure the cluster to use Container Image Security, which installs [Portieris](https://github.com/IBM/portieris) in the cluster, and will also create `ClusterImagePolicy` instances to enforce signatures for IBM Cloud images. |
| 14 | + |
| 15 | +Portieris is a Kubernetes admission controller for the enforcement of image security policies. With Portieris you can create image security policies either for each Kubernetes namespace or at the cluster level, and enforce different rules for different images. Portieris will block deployment for any image that fails signature validation as defined by the image policies. Thus ensuring that the images running inside of your cluster are unmodified from their original source. |
| 16 | + |
| 17 | + |
| 18 | +!!!note |
| 19 | + Portieris can be used in clusters that are not running on IBM Cloud by <a href="https://github.com/IBM/portieris#installing-portieris">installing via the helm chart</a>. |
| 20 | + |
| 21 | +## Enabling Policy Enforcement |
| 22 | + |
| 23 | +Portieris uses [RedHat Signatures](https://www.redhat.com/en/blog/container-image-signing) to sign container images. |
| 24 | + |
| 25 | +To take advantage of Portieris and policy enforcement, you need 3 things: |
| 26 | +1. A GnuPG key to sign container images, stored in a vault |
| 27 | +2. A process to sign container images using the key from the credentials vault |
| 28 | +3. An `ImagePolicy` or `ClusterImagePolicy` that can instruct Portieris to apply enforcemnt rules |
| 29 | + |
| 30 | +The following steps are based on [signing images for trusted content](https://cloud.ibm.com/docs/Registry?topic=Registry-registry_trustedcontent). |
| 31 | + |
| 32 | +### Getting started quickly |
| 33 | + |
| 34 | +A script that demonstrates how to easily create a GPG key, publish it to a vault, setup cluster secrets, and setup a default ClusterImagePolicy (as described below) is available at https://github.com/IBM/ibm-garage-tekton-tasks/blob/image-signing/utilities/setup-image-signing-keys.sh |
| 35 | + |
| 36 | +The [toolkit's 2-build-tag-push.yaml](https://github.com/IBM/ibm-garage-tekton-tasks/blob/main/tasks/2-build-tag-push.yaml) tekton task has also been updated to accept the output of this script and enforce signatures during the builder's push phase. |
| 37 | + |
| 38 | +### Create an Image Signing Key |
| 39 | + |
| 40 | +An image signing key must be created to sign the container images. This can be done by executing the following command: |
| 41 | + |
| 42 | +```shell |
| 43 | +gpg --generate-key |
| 44 | +``` |
| 45 | + |
| 46 | +This will create a public and private key combination that can be used to sign and verify container images. The output of the `gpg --generate-key` command will show the fingerprint of the newly generated key. This fingerprint will be needed in the following steps. |
| 47 | + |
| 48 | +#### Saving the private key in a vault |
| 49 | + |
| 50 | + |
| 51 | +Once your key has been generated, the private key should be stored within a credentials vault, such as [Key Protect](/tools/secret-management-with-key-protect) or [IBM HyperProtect Crypto Services](https://cloud.ibm.com/docs/hs-crypto?topic=hs-crypto-overview). |
| 52 | + |
| 53 | +The private key should be placed in the vault as a base64 encoded string, which can be accessed by your [Tekton](/tools/tekton) pipeline during the image building task. |
| 54 | + |
| 55 | +To place the private key in a vault |
| 56 | + |
| 57 | +```shell |
| 58 | +ENCODED_PRIVATE_KEY=$(gpg --export-secret-key <KEY_FINGERPRINT> | base64) |
| 59 | + |
| 60 | +curl -X POST https://<region>.kms.cloud.ibm.com/api/v2/keys \ |
| 61 | + -H 'authorization: Bearer <IAM_token>' \ |
| 62 | + -H 'bluemix-instance: <instance_ID>' \ |
| 63 | + -H 'content-type: application/vnd.ibm.kms.key+json' \ |
| 64 | + -d "{ |
| 65 | + \"metadata\": { |
| 66 | + \"collectionType\": \"application/vnd.ibm.kms.key+json\", |
| 67 | + \"collectionTotal\": 1 |
| 68 | + }, |
| 69 | + \"resources\": [ |
| 70 | + { |
| 71 | + \"type\": \"application/vnd.ibm.kms.key+json\", |
| 72 | + \"name\": \"image-signing-key\", |
| 73 | + \"aliases\": [], |
| 74 | + \"description\": \"Private key for signing container images\", |
| 75 | + \"payload\": \"$ENCODED_PRIVATE_KEY\", |
| 76 | + \"extractable\": true |
| 77 | + } |
| 78 | + ] |
| 79 | + }" |
| 80 | +``` |
| 81 | + |
| 82 | +Both [Key Protect](https://cloud.ibm.com/apidocs/key-protect) and [Hyper Protect Crypto Services](https://cloud.ibm.com/apidocs/hs-crypto) have an identical API, so the previous steps are identical except that a different API endpoint is used. |
| 83 | + |
| 84 | +#### Saving the public key |
| 85 | + |
| 86 | +The public key needs to be made available to the cluster for verifying container image signatures by either creating a secret within the cluster, or making the public key available through [Artifactory](/tools/artifactory/). |
| 87 | + |
| 88 | +Use the following commands to make the public key available for policy enforcement by creating a secret within the cluster: |
| 89 | + |
| 90 | +```shell |
| 91 | +gpg --export --armour <KEY_FINGERPRINT> > key.pubkey |
| 92 | +oc create secret generic image-signing-public-key --from-file=key=key.pubkey |
| 93 | +``` |
| 94 | + |
| 95 | +### Signing container images |
| 96 | + |
| 97 | +Container image policy enforcement will reject images that are not signed, so you need to sign images either when they are pushed to the container registry. This can be done using either the `skopeo copy` command or `buildah push` command, depending when you want to sign your images. |
| 98 | + |
| 99 | +#### Extracting the private key for signing |
| 100 | + |
| 101 | +The follwoing commands can be used to access your private key from the vault, and import it into gpg for use in signing. This would be used inside of your pipeline: |
| 102 | + |
| 103 | +```shell |
| 104 | +echo "Getting private key from keystore for image signing" |
| 105 | +curl -s -o payload \ |
| 106 | + https://<region>.kms.cloud.ibm.com/api/v2/keys/<key_ID_or_alias> \ |
| 107 | + -H "Authorization: Bearer <IAM_token>" \ |
| 108 | + -H "Content-Type: application/json" \ |
| 109 | + -H "bluemix-instance: <instance_ID>" |
| 110 | + |
| 111 | +ENCODEDKEY=$(jq ".resources[0].payload" -r payload) |
| 112 | +echo $ENCODEDKEY > encodedkey |
| 113 | +base64 -d encodedkey > decodedkey |
| 114 | + |
| 115 | +echo "Importing key" |
| 116 | +gpg --import decodedkey |
| 117 | +``` |
| 118 | + |
| 119 | +Once the key is imported, then the image can be signed. If the image is being signed at build time, the signature can be specified by the `--sign-by` paramter to the `buidah` command: |
| 120 | + |
| 121 | +```shell |
| 122 | +buildah --sign-by <KEY_FINGERPRINT> --storage-driver=overlay push --digestfile ./image-digest ${APP_IMAGE} docker://${APP_IMAGE} |
| 123 | +``` |
| 124 | + |
| 125 | +If the image is being signed at copy-time, it can be specified as a parameter to the `skopeo` command: |
| 126 | + |
| 127 | +```shell |
| 128 | +skopeo --sign-by <KEY_FINGERPRINT> copy ${IMAGE_FROM_CREDS} docker://${IMAGE_FROM} docker://${IMAGE_TO} |
| 129 | +``` |
| 130 | + |
| 131 | +### Create image policies |
| 132 | + |
| 133 | +Finally, image policies need to be created to instruct Portieris which keys should be used to sign images from specific container registries. These policies can be applied globally to the entire cluster using a `ClusterImagePolicy`, or to a specific namespace using an `ImagePolicy`. In those policies, rules can be defined for enforcement for specific container registries/namespaces, or globally to all container registries used by the cluster. |
| 134 | + |
| 135 | +For example, the following `ClusterImagePolicy` enforces a policy that all images in the container registry `icr.io/mynamespace/*` must be signed by the public key that was earlier created and placed into the `image-signing-public-key` cluster secret. |
| 136 | + |
| 137 | +```yaml |
| 138 | +apiVersion: portieris.cloud.ibm.com/v1 |
| 139 | +kind: ClusterImagePolicy |
| 140 | +metadata: |
| 141 | + name: mynamespace-cluster-image-policy |
| 142 | +spec: |
| 143 | + repositories: |
| 144 | + - name: "icr.io/mynamespace/*" |
| 145 | + policy: |
| 146 | + simple: |
| 147 | + requirements: |
| 148 | + - type: "signedBy" |
| 149 | + keySecret: image-signing-public-key |
| 150 | +``` |
| 151 | +
|
| 152 | +More information about policies and enforcement can be found in the [Portieris Policies documentation](https://github.com/IBM/portieris/blob/master/POLICIES.md) |
| 153 | +
|
| 154 | +
|
| 155 | +## Tekton tasks |
| 156 | +
|
| 157 | +A script that demonstrates how to easily create a GPG key, publish it to a vault, setup cluster secrets, and setup a default ClusterImagePolicy is available at https://github.com/IBM/ibm-garage-tekton-tasks/blob/image-signing/utilities/setup-image-signing-keys.sh |
| 158 | +
|
| 159 | +The [toolkit's 2-build-tag-push.yaml](https://github.com/IBM/ibm-garage-tekton-tasks/blob/main/tasks/2-build-tag-push.yaml) tekton task has also been updated to accept the output of this script and enforce signatures during the builder's push phase. |
| 160 | +
|
| 161 | +
|
| 162 | +## Additional Information |
| 163 | +
|
| 164 | +Additional information on trusted content and policy enforcement can be found at: |
| 165 | +- [Signing images for trusted content](https://cloud.ibm.com/docs/Registry?topic=Registry-registry_trustedcontent) |
| 166 | +- [Gnu Privact Guard (GPG)](https://gnupg.org/) |
| 167 | +- [RedHat Signatures](https://www.redhat.com/en/blog/container-image-signing) |
| 168 | +- [Portieris](https://github.com/IBM/portieris) |
| 169 | +- [Portieris Policies](https://github.com/IBM/portieris/blob/master/POLICIES.md) |
| 170 | +- [Key Protect API Docs](https://cloud.ibm.com/apidocs/key-protect) |
| 171 | +- [Hyper Protect API Docs](https://cloud.ibm.com/apidocs/hs-crypto) |
0 commit comments