Skip to content

Commit

Permalink
Enable usage of keyed signing with custom sigstore instance
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
prezha committed Jun 18, 2024
1 parent 157bfd8 commit 6bfd6b2
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 91 deletions.
73 changes: 57 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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)
Expand Down
149 changes: 77 additions & 72 deletions hooks/post-command
Original file line number Diff line number Diff line change
Expand Up @@ -33,134 +33,132 @@ display_success() {
buildkite-agent annotate --style success "$message<br />" --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 <<EOF | buildkite-agent annotate --style success --context "cosign-signature"
### Signed image
\`\`\`
$image
\`\`\`
### Signature
\`\`\`
$signature
\`\`\`
EOF

rm -f out.sig
}

cosign_keyed() {
echo "--- :key: Cosign keyed signing"

local key=${BUILDKITE_PLUGIN_COSIGN_KEYED_CONFIG_KEY:-}
if [[ -z "${key}" ]]; then
fail_with_message "cosign" "Key not specified"
fi

if [[ ! -f "${key}" ]]; then
fail_with_message "cosign" "Key file not found in path ${key}"
fi

sign_flags+=("--key" "${key}")
}

# sign the image
cosign_sign() {
echo "--- :key: Signing image with cosign"

rm -f out.sig

cosign sign \
-y \
--key="${key}" \
--output-signature=out.sig \
"${sign_flags[@]}" \
"${image}"

status=$?
Expand All @@ -171,7 +169,7 @@ cosign_keyed() {
local signature
signature=$(cat out.sig)

display_success "cosign" "Successfully signed image."
display_success "cosign" "Successfully signed image"
cat <<EOF | buildkite-agent annotate --style success --context "cosign-signature"
### Signed image
\`\`\`
Expand All @@ -187,8 +185,15 @@ EOF
rm -f out.sig
}

# Main
#######

cosign_init

if [[ "${is_keyless}" == true ]]; then
cosign_keyless
setup_keyless
else
cosign_keyed
setup_keyed
fi

cosign_sign
15 changes: 12 additions & 3 deletions plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ configuration:
tuf-root-url:
type: string
description: "TUF root"
fulcio-url:
type: string
description: "Fulcio URL"
rekor-url:
type: string
description: "Rekor URL"
fulcio-url:
type: string
description: "Fulcio URL"
oidc-issuer:
type: string
description: "OIDC issuer"
Expand All @@ -40,6 +40,15 @@ configuration:
keyed-config:
type: object
properties:
tuf-mirror-url:
type: string
description: "TUF URL"
tuf-root-url:
type: string
description: "TUF root"
rekor-url:
type: string
description: "Rekor URL"
key:
type: string
description: "Signing key path"
Expand Down

0 comments on commit 6bfd6b2

Please sign in to comment.