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..08265bf 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; 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 <