From 685c014d298bf89acd0d265f0f06d4643a48b067 Mon Sep 17 00:00:00 2001 From: Predrag Rogic Date: Tue, 18 Jun 2024 10:33:55 +0100 Subject: [PATCH] Enable usage of keyed signing with custom sigstore instance Refactor code to group and then reuse the common code logic. Amend documentation to emphasise usage of image digest vs tag and add more examples for both keyless and keyed signing using the Public-Good and custom sigstore instance. --- README.md | 73 +++++++++++++++++----- hooks/post-command | 149 +++++++++++++++++++++++---------------------- plugin.yml | 15 ++++- 3 files changed, 146 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 431a8b6..4da4e4b 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ open-source cosign OCI container image signing tool for your containers. For more information about cosign, please refer to their [documentation](https://docs.sigstore.dev/cosign/overview). -**Important notes**: +**Important notes** -To be certain you know what you're signing: +To ensure you know what you're signing: -- It's best to have this plugin run as part of the image ci build step (where the +- It's best to have this plugin run as part of the image CI build step (where the built image is stored locally) and not as a separate step (signing a remote image). - -- It's strongly recommended to use image digest instead of image tag. Otherwise, -you might get a warning from cosign, or it may even refuse to sign the image: +- It's strongly recommended to use image digest instead of image tag (plugin will +automatically try to infer and use digest based on the provided image tag). +Otherwise, you might get a warning from cosign, or it may even refuse to sign the image: >WARNING: Image reference ghcr.io/my-project/my-image:v1.2.3 uses a tag, not a digest, to identify the image to sign. This can lead you to sign a different image than the intended one. Please use a @@ -28,8 +28,13 @@ digest, to identify the image to sign. ## Basic signing examples -The following code snippet demonstrates how to use the plugin in a pipeline -step with the default plugin configuration parameters: +The following code snippets demonstrates how to use the plugin in a pipeline +step with the configuration parameters and upload the signature to the same +repository as the container image. + +### Keyless signing + +#### Using the Public-Good Sigstore Instance ```yml steps: @@ -38,11 +43,7 @@ steps: image: "ghcr.io/my-project/my-image@sha256:1e1e4f97dd84970160975922715909577d6c12eaaf6047021875674fa7166c27" ``` -This will use the `keyless` signing with the `Public-Good` Sigstore Instance by -default and upload the signature to the same repository as the container image. - -The following code snippet demonstrates how to use the plugin for `keyless` signing -with specified `custom` Sigstore instance URLs: +#### Using a custom Sigstore Instance ```yml steps: @@ -52,8 +53,39 @@ steps: keyless-config: tuf-mirror-url: "https://tuf.my-sigstore.dev" tuf-root-url: "https://tuf.my-sigstore.dev/root.json" + rekor-url: "https://rekor.my-sigstore.dev" fulcio-url: "https://fulcio.my-sigstore.dev" +``` + +### Keyed signing + +Note: Currently, only the file-based keyed signing is supported. + +#### Using the Public-Good Sigstore Instance + +```yml +steps: + - plugins: + - equinixmetal-buildkite/cosign#v0.1.0: + image: "ghcr.io/my-project/my-image@sha256:1e1e4f97dd84970160975922715909577d6c12eaaf6047021875674fa7166c27" + keyless: false + keyed-config: + key: "/path-to/cosign.key" +``` + +#### Using a custom Sigstore Instance + +```yml +steps: + - plugins: + - equinixmetal-buildkite/cosign#v0.1.0: + image: "ghcr.io/my-project/my-image@sha256:1e1e4f97dd84970160975922715909577d6c12eaaf6047021875674fa7166c27" + keyless: false + keyed-config: + tuf-mirror-url: "https://tuf.my-sigstore.dev" + tuf-root-url: "https://tuf.my-sigstore.dev/root.json" rekor-url: "https://rekor.my-sigstore.dev" + key: "/path-to/cosign.key" ``` ## Configuration @@ -81,12 +113,12 @@ parameters to sign the container image: - `tuf-root-url` (Optional, string): The URL of the TUF root JSON file to use. If not specified, the plugin will use the default TUF root JSON file URL of the Public-Good Sigstore Instance. -- `fulcio_url` (Optional, string): - The URL of the Fulcio server to use. If not specified, the plugin will use - the default Fulcio URL of the Public-Good Sigstore Instance. - `rekor_url` (Optional, string): The URL of the Rekor server to use. If not specified, the plugin will use the default Rekor URL of the Public-Good Sigstore Instance. +- `fulcio_url` (Optional, string): + The URL of the Fulcio server to use. If not specified, the plugin will use + the default Fulcio URL of the Public-Good Sigstore Instance. - `oidc-issuer` (Optional, string): The URL of the OIDC issuer. If not specified, the plugin will use the default OIDC issuer URL of the Public-Good Sigstore Instance. @@ -99,6 +131,15 @@ parameters to sign the container image: If `keyless` is set to `false`, the plugin will use the following configuration parameters to sign the image: +- `tuf-mirror-url` (Optional, string): + The URL of the TUF server to use. If not specified, the plugin will use + the default TUF URL of the Public-Good Sigstore Instance. +- `tuf-root-url` (Optional, string): + The URL of the TUF root JSON file to use. If not specified, the plugin will use + the default TUF root JSON file URL of the Public-Good Sigstore Instance. +- `rekor_url` (Optional, string): + The URL of the Rekor server to use. If not specified, the plugin will use + the default Rekor URL of the Public-Good Sigstore Instance. - `key` (Required, string): The path to the private key to use. ### `cosign-version` (Optional, string) diff --git a/hooks/post-command b/hooks/post-command index c91e173..1d00f2c 100755 --- a/hooks/post-command +++ b/hooks/post-command @@ -33,134 +33,132 @@ display_success() { buildkite-agent annotate --style success "$message
" --context "$ctx" } -# Parameters -############ +# if the supplied image reference does not contain a digest, +# try getting the local image digest to use it instead, and +# if that fails, warn then continue using the supplied image reference +use_image_digest() { + if [[ $image != *"@sha256:"* ]]; then + echo "--- :docker: Getting the local image digest for ${image}" + + local digest + digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${image}") -# This is a required parameter + status=$? + if [[ $status -ne 0 ]]; then + display_error "docker inspect" "Failed to get the local image digest, will continue using supplied image reference ${image}" + else + display_success "docker inspect" "Will continue using ${digest}" + image="${digest}" + fi + fi +} + +# Common parameters +################### + +# image is a required parameter image=${BUILDKITE_PLUGIN_COSIGN_IMAGE} if [[ -z "${image}" ]]; then - fail_with_message "cosign" "No image specified" + fail_with_message "cosign" "Image not specified" fi +use_image_digest + +# flags for the cosign sign command +sign_flags=("-y" "--output-signature" "out.sig") is_keyless=${BUILDKITE_PLUGIN_COSIGN_KEYLESS:-true} # Hook functions ################ -cosign_keyless() { - echo "--- :key: Cosign keyless signing" - - rm -f out.sig +# if provided, initialise cosign with a custom TUF configuration +cosign_init() { + echo "--- :key: Init cosign" # flags for the cosign initialize command init_flags=() - local tuf_mirror_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_TUF_MIRROR_URL} + if [[ "${is_keyless}" == true ]]; then + local tuf_mirror_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_TUF_MIRROR_URL} + local tuf_root_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_TUF_ROOT_URL} + else + local tuf_mirror_url=${BUILDKITE_PLUGIN_COSIGN_KEYED_CONFIG_TUF_MIRROR_URL} + local tuf_root_url=${BUILDKITE_PLUGIN_COSIGN_KEYED_CONFIG_TUF_ROOT_URL} + fi + if [[ -n "${tuf_mirror_url}" ]]; then init_flags+=("--mirror" "${tuf_mirror_url}") fi - local tuf_root_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_TUF_ROOT_URL} if [[ -n "${tuf_root_url}" ]]; then init_flags+=("--root" "${tuf_root_url}") fi - # if supplied, initialise cosign with a custom TUF configuration if [ ${#init_flags[@]} -gt 0 ]; then - echo "--- :key: Initialising cosign with the custom TUF configuration provided" cosign initialize "${init_flags[@]}" status=$? if [[ $status -ne 0 ]]; then fail_with_message "cosign" "Failed to initialise" fi + display_success "cosign" "Successfully initialised" + else + display_success "cosign" "Initialisation not required, skipping" fi +} - # flags for the cosign sign command - sign_flags=() - - local fulcio_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_FULCIO_URL} - if [[ -n "${fulcio_url}" ]]; then - sign_flags+=("--fulcio-url" "${fulcio_url}") - fi +setup_keyless() { + echo "--- :key: Setup cosign keyless signing" local rekor_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_REKOR_URL} if [[ -n "${rekor_url}" ]]; then sign_flags+=("--rekor-url" "${rekor_url}") fi + local fulcio_url=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_FULCIO_URL} + if [[ -n "${fulcio_url}" ]]; then + sign_flags+=("--fulcio-url" "${fulcio_url}") + fi + local oidc_issuer=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_OIDC_ISSUER} if [[ -n "${oidc_issuer}" ]]; then sign_flags+=("--oidc-issuer" "${oidc_issuer}") fi local oidc_provider=${BUILDKITE_PLUGIN_COSIGN_KEYLESS_CONFIG_OIDC_PROVIDER:-"buildkite-agent"} - sign_flags+=("--oidc-provider" "${oidc_provider}") - - # if the supplied image reference does not contain a digest, try getting the local image digest to use it instead - if [[ $image != *"@sha256:"* ]]; then - echo "--- :docker: Getting the local image digest for ${image}" - - local digest - digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${image}") - - status=$? - if [[ $status -ne 0 ]]; then - display_error "docker inspect" "Failed to get the local image digest, will continue using supplied image reference ${image}" - else - display_success "docker inspect" "Will continue using ${digest}" - image="${digest}" - fi + if [[ -n "${oidc_provider}" ]]; then + sign_flags+=("--oidc-provider" "${oidc_provider}") fi +} - # sign the image - cosign sign \ - -y \ - "${sign_flags[@]}" \ - --output-signature=out.sig \ - "${image}" +setup_keyed() { + echo "--- :key: Setup cosign keyed signing" - status=$? - if [[ $status -ne 0 ]]; then - fail_with_message "cosign" "Failed to sign image" + local rekor_url=${BUILDKITE_PLUGIN_COSIGN_KEYED_CONFIG_REKOR_URL} + if [[ -n "${rekor_url}" ]]; then + sign_flags+=("--rekor-url" "${rekor_url}") fi - local signature - signature=$(cat out.sig) - - display_success "cosign" "Successfully signed image." - cat <