|
| 1 | +# Integrate `cosign` with `kpack` |
| 2 | + |
| 3 | +## Problem |
| 4 | + |
| 5 | +The signing solution currently supported by `kpack` is |
| 6 | +[Notary v1](https://github.com/theupdateframework/notary). However, Notary |
| 7 | +presents itself with several different challenges, including: |
| 8 | +- It is unable to sign container image digests. The user must always specify a |
| 9 | +tag to be signed. |
| 10 | +- It is unable to verify container image signature by digests. |
| 11 | +- Signature relocation is not possible with this tool. |
| 12 | +- It is a project that currently has low activity and community involvement, |
| 13 | + since the community is currently focused on the development of Notary v2. |
| 14 | + |
| 15 | +[`cosign`](https://github.com/sigstore/cosign) presents itself as a solution to |
| 16 | +some of those aforementioned challenges. With `cosign`, the user is able to: |
| 17 | +- Relocate container images across registries along with its signatures, using |
| 18 | + either: |
| 19 | + - `crane cp` to copy images and signatures separately. |
| 20 | + - `cosign copy` to copy images along with their signatures from one registry |
| 21 | + to another. |
| 22 | +- Sign container images by tag or by digest. |
| 23 | +- Verify container image signatures by specifying either a tag or a digest. |
| 24 | +- Verify container image signatures after relocation. |
| 25 | + |
| 26 | +Moreover, `cosign` is a project that has had a lot of activity and community |
| 27 | +involvement recently. |
| 28 | + |
| 29 | +## Outcome |
| 30 | + |
| 31 | +`kpack` integrates with `cosign` to sign images it builds and push the |
| 32 | +signatures to a registry so that users can ensure the chain of custody of a |
| 33 | +generated artifact. |
| 34 | + |
| 35 | +This proposal aims to cover specifically the flow of signing an image produced |
| 36 | +by the `kpack` `Image`, `Build`, `Builder` and `ClusterBuilder` resources, without |
| 37 | +verification of any container images pulled in the process. |
| 38 | + |
| 39 | +## Actions to take |
| 40 | + |
| 41 | +### Enable image signing using `cosign` |
| 42 | + |
| 43 | +- Implement a flow that generates the signature payload for the image, then |
| 44 | + calculates its signature and pushes it either to the registry where the image |
| 45 | + is located, using the same credentials that were used to push the image, or |
| 46 | + [to the registry specified in the `COSIGN_REPOSITORY` environment variable](#key-generation-and-storage). |
| 47 | + `kpack` should sign images produced by `kpack` builds and images produced by |
| 48 | + the `Builder` and `ClusterBuilder` resources, using their respective service |
| 49 | + accounts to find credentials. This flow must happen after each of the images |
| 50 | + have been pushed to the registry. |
| 51 | + |
| 52 | +- If `cosign` fails to sign an image, the build should fail and output an error |
| 53 | + message in the build log, so the operator can troubleshoot the issue. The |
| 54 | + error messages should also be added in any other places where error messages |
| 55 | + are presented. |
| 56 | +- If `cosign` fails to sign an image produced by a `Builder` resource, the |
| 57 | +`Builder` should not enter a Ready status. |
| 58 | +- Whenever `kpack` signs an image produced within a `kpack` build using `cosign`, |
| 59 | +it should add these annotations: |
| 60 | + - Build number. |
| 61 | + - Build time. |
| 62 | + |
| 63 | +- Signing with `cosign` should not affect any configurations that enable signing |
| 64 | + using the Notary v1 mechanism. These two signing mechanisms should be able to |
| 65 | + coexist. |
| 66 | + |
| 67 | +### Key generation and storage |
| 68 | + |
| 69 | +`cosign` can use file-based keys, key management systems or hardware keys to |
| 70 | +ingest private keys, as well as a keyless flow that requires integration with |
| 71 | +[Rekor](https://github.com/sigstore/rekor). |
| 72 | + |
| 73 | +`kpack` should not generate the keys used for signing and verification. The user |
| 74 | +should pass them in using one of the mechanisms supported by `cosign`. |
| 75 | +In this RFC, we suggest that the first mechanism implemented be the use of |
| 76 | +Kubernetes `Secret`s for sending the relevant data into the build pod, by means |
| 77 | +of attaching secrets to the service account used by `kpack`. |
| 78 | +These secrets must follow the format generated by |
| 79 | +[`cosign generate-key-pair -k8s <namespace>/<name>`](https://github.com/sigstore/cosign/pull/345). |
| 80 | +Any build that has a service account with `cosign` secrets attached to it will |
| 81 | +have signatures generated. |
| 82 | + |
| 83 | +The service account can have more than one `cosign` secret attached to it. For |
| 84 | +each `cosign` secret attached, a separate signature should be generated and |
| 85 | +stored. |
| 86 | + |
| 87 | +`kpack` will be able to determine that the user wants to sign images using |
| 88 | +`cosign` by scanning the existing keys in each of the secrets. Secrets to be |
| 89 | +used by `cosign` signing must contain at least the `cosign.key` and |
| 90 | +`cosign.password` keys on the `data` section. Optionally, the user can also |
| 91 | +annotate the secrets with the following information: |
| 92 | +- `kpack.io/cosign.repository`: a separate registry URL to upload the generated |
| 93 | +signatures, instead of pushing the signatures collocated with the images. Adding |
| 94 | +this annotation should have the same effect as setting the `COSIGN_REPOSITORY` |
| 95 | +variable. The user must provide credentials for `kpack` to authenticate with the |
| 96 | +specified registry. In the case where this repository is inaccessible, the build |
| 97 | +should fail and output an error message to allow the operator to troubleshoot. |
| 98 | +- `kpack.io/cosign.docker-media-types`: a flag indicating whether legacy media |
| 99 | +types should be used instead of OCI media types. Adding this annotation should |
| 100 | +have the same effect as setting the `COSIGN_DOCKER_MEDIA_TYPES` variable. |
| 101 | + |
| 102 | +Secret format example: |
| 103 | +```yaml |
| 104 | +--- |
| 105 | +apiVersion: v1 |
| 106 | +kind: Secret |
| 107 | +metadata: |
| 108 | + name: cosign-key |
| 109 | + annotations: |
| 110 | + # Optional |
| 111 | + kpack.io/cosign.repository: "<repository-url>" |
| 112 | + # Optional |
| 113 | + kpack.io/cosign.docker-media-types: "1" |
| 114 | +type: Opaque |
| 115 | +data: |
| 116 | + cosign.key: <cosign-private-key> |
| 117 | + cosign.pub: <cosign-public-key> |
| 118 | + cosign.password: <cosign-private-key-passphrase> |
| 119 | +``` |
| 120 | +
|
| 121 | +### Configure custom static annotations |
| 122 | +
|
| 123 | +Create a new optional configuration in the `Image` resource that allow users to |
| 124 | +parameterize optional annotations to add to the generated signatures. These |
| 125 | +annotations will be static and configured as a list of key-value pairs: |
| 126 | +```yaml |
| 127 | +cosign: |
| 128 | + annotations: |
| 129 | + - name: "key1" |
| 130 | + value: "value1" |
| 131 | + - name: "key2" |
| 132 | + value: "value2" |
| 133 | +``` |
| 134 | + |
| 135 | +### Authentication to a container registry |
| 136 | + |
| 137 | +`cosign` uses the `DefaultKeychain` from |
| 138 | +[`go-containerregistry`](https://github.com/google/go-containerregistry/blob/main/pkg/authn/README.md#tldr-for-consumers-of-this-package) |
| 139 | +to authenticate the command-line interface using the machine's Docker CLI |
| 140 | +credentials. For `kpack`, it may be possible to use the same authentication |
| 141 | +mechanism that is in-place for uploading an image. |
| 142 | + |
| 143 | +## Complexity |
| 144 | + |
| 145 | +Integration with `cosign` is estimated to be of medium complexity. Since |
| 146 | +`cosign` is also written in Go, it is possible to import code from the tool |
| 147 | +directly within `kpack`, which can help make the integration easier. |
| 148 | + |
| 149 | +## Prior art |
| 150 | + |
| 151 | +- [Issue](https://github.com/pivotal/kpack/issues/684) filed on May 4th. |
| 152 | +- [Use of `cosign` in Kubernetes](https://github.com/kubernetes/release/pull/2016) |
| 153 | + for validation of distroless images. |
| 154 | +- [Use of `cosign` in Connaisseur](https://github.com/sse-secure-systems/connaisseur/pull/107) |
| 155 | + for policy enforcement in container images. |
| 156 | +- [Notary v1 implementation pull request](https://github.com/pivotal/kpack/pull/541). |
| 157 | + |
| 158 | +## Alternatives |
| 159 | + |
| 160 | +- Use a CI/CD step to sign images using `cosign` instead of implementing it as |
| 161 | + a part of `kpack`. |
| 162 | +- Configure `cosign` integration on a per-`Image` resource basis. |
| 163 | + |
| 164 | +## Risks |
| 165 | + |
| 166 | +- Notary v2 support might be a requirement in the future. |
| 167 | +- `cosign` is at an early stage of development and may have breaking changes in |
| 168 | + the future. |
0 commit comments