Secure Supply Chain
A Secure Supply Chain infrastructure allows software developers and users to ensure
the validity of the artefacts' chain of custody, thus mitigating
security issues in their environments. The goal of the Sigstore project
is to provide the tools and infrastructure for validating the integrity of the supply chain.
Kubewarden leverages the cosign
utility together
with the fulcio and rekor
infrastructure offered by the Sigstore project to bring these security measures to its users.
Therefore, cluster operators can configure Kubewarden to only run policies signed by entities that they trust while policy developers can sign their policies and publish them in a registry.
Prerequisites
In the following sections, we will need a few tools to be installed so that
users can sign and verify OCI artefacts signatures. For key-based signing, the examples demonstrate
the use of cosign
and kwctl
utilities for signing and inspecting policies.
Users may also want to leverage GitHub to sign their policies. In which case, they can choose to install and use Github actions
Keyless signing has been demonstrated using the default fulcio and rekor instances provided by the Sigstore project. Users may choose to utilise their own infrastructure by checking the Sigstore project documentation for more details.
Signing policies
Kubewarden recommends using Sigstore's cosign utility
for signing policies. This subsection details a key-based method of signing policies
in which, users need to generate a private-public key pair. The generated keys help
in verifying whether the signed artefacts came from the expected user i.e. the
owner of the keys. Using the cosign generate-key-pair
command, as illustrated
below, it is possible to generate the aforementioned keypair.
cosign generate-key-pair
Enter password for private key:
Enter password for private key again:
Private key written to cosign.key
Public key written to cosign.pub
Once the keys are generated, users can use it to sign policies.
WARNING: The private key file,
cosign.key
, should not be shared. This is a secret file that should be used only by the owner of the key for signing policies.
To sign a policy users can execute the cosign sign
command passing the --key
command line argument with their private key file:
cosign sign --key cosign.key ghcr.io/jvanz/policies/user-group-psp:latest
an error occurred: no provider found for that key reference, will try to load key from disk...
Enter password for private key:
Pushing signature to: ghcr.io/jvanz/policies/user-group-psp
This command will sign the policy by creating a new signature object. The signature object is then uploaded into the registry, next to the policy. Now the policy is ready to be used in a Kubewarden installation with signature verification enabled.
The same policy can be signed multiple times, either by the same user or by different ones. All these signatures are appended to the same signature object.
NOTE: For more information about how the signing process works under the hood, check out the Sigstore project documentation
Keyless signing
Many times the policies are automatically built in CI/CD pipelines which complicates the key generation process. The Sigstore keyless workflow described below is specifically designed for these situations and others where key generation is not possible. Instead of using long-lived singing keys, the keyless workflow uses certificate authorities (CAs) and certificate chains. A short-lived certificate key is generated, and linked into a chain of trust by completing an identity challenge to confirm the signer's identity. The life of the certificate key is just about enough for the signing to occur. The identity challenge is performed by authenticating against an OpenID Connect (OIDC) provider. Sigstore's Fulcio public infrastructure is used for the chain of trust.
For signing, we will again use Sigstore's cosign utility. However, as of this writing, this feature is not enabled by default in cosign. So, it's necessary to enable this experimental feature before proceeding with keyless signing as shown below:
$ COSIGN_EXPERIMENTAL=1 cosign sign ghcr.io/jvanz/policies/user-group-psp:latest
Generating ephemeral keys...
Retrieving signed certificate...
Your browser will now be opened to:
https://oauth2.sigstore.dev/auth/auth?access_type=online&client_id=sigstore&code_challenge=<REDACTED>&code_challenge_method=S256&nonce=<REDACTED>&redirect_uri=http%3A%2F%2Flocalhost%3A34021%2Fauth%2Fcallback&response_type=code&scope=openid+email&state=<REDACTED>
client.go:196: root pinning is not supported in Spec 1.0.19
Successfully verified SCT...
tlog entry created with index: 1819248
Pushing signature to: ghcr.io/jvanz/policies/user-group-psp
With that, the policy has been signed and pushed to the repository. Note that there are no keys generated as a byproduct.
How to sign artefacts in GitHub workflows
cosign
does not require the user to log on to an OIDC provider when using keyless signing
in the context of a GitHub action environment. In this scenario,
the GitHub token available during the execution of the workflow will be used to
authenticate the user and generate the ephemeral keys. The signing process is
the same used in the Keyless
mode. This is an example of how Kubewarden project
signs its policies:
# ... beginning of the workflow file ...
jobs:
build:
name: Build container image
runs-on: ubuntu-latest
steps:
# ... other steps building the container image ...
-
name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ inputs.GITHUB_TOKEN }}
-
name: Publish Wasm policy artifact to OCI registry with the 'latest' tag
shell: bash
if: ${{ startsWith(github.ref, 'refs/heads/') }}
env:
COSIGN_EXPERIMENTAL: 1
run: |
set -ex
echo Pushing policy to OCI container registry
IMMUTABLE_REF=$(kwctl push -o json ${{ PATH_TO_BUILT_WASM_FILE }} ghcr.io/myorg/policies/my-great-policy:latest | jq -r .immutable_ref)
echo Keyless signing of policy using cosign
cosign sign ${IMMUTABLE_REF}
# ... other build steps ...
# ... remaining of the workflow file ...
NOTE: The policy templates repositories provided by the Kubewarden team already have Github actions to build, test, sign and publish policies. It's highly recommended that policy developers use those templates.
In the example above, we demonstrate using kwctl
to publish the container image and find
the reference to it. This is not mandatory. You can call cosign
with the image
reference generated elsewhere.
Listing policy signatures
kwctl
also allows users to check the signatures in a published
policy. The kwctl inspect
command shows all the relevant information about the
policy and its signatures as illustrated in an example below:
$ kwctl inspect registry://ghcr.io/kubewarden/policies/user-group-psp:v0.2.0
Details
title: psp-user-group
description: Short description
author: José Guilherme Vanz <jguilhermevanz@suse.com>
url: https://github.com/kubewarden/user-group-psp-policy
source: https://github.com/kubewarden/user-group-psp-policy
license: Apache-2.0
mutating: true
context aware: false
execution mode: kubewarden-wapc
protocol version: 1
Annotations
io.kubewarden.kwctl 0.2.5-rc2
Rules
────────────────────
---
- apiGroups:
- ""
apiVersions:
- v1
resources:
- pods
operations:
- CREATE
────────────────────
Usage
This policy enforce the user and group used in the container.
Sigstore signatures
Digest: sha256:026af67682a85d424e7d95db460171635f5c3957d67b53499bece912cc0413cc
Media type: application/vnd.dev.cosign.simplesigning.v1+json
Size: 258
Annotations
dev.sigstore.cosign/certificate -----BEGIN CERTIFICATE-----
MIIDRzCCAsygAwIBAgITbPUZlUFkkAHtbzc3rzC/3zXj1DAKBggqhkjOPQQDAzAq
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIy
MDIyNTE2MzAwMloXDTIyMDIyNTE2NDAwMVowEzERMA8GA1UEChMIc2lnc3RvcmUw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR/O5c6ZI5BzBweoEIam4uWu5fqzHx0
3PTCgfXyyvIjorz9wX08bsndkHdWfFObU+PztbxX78An43Yw9/fHtO93o4IB5jCC
AeIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFCP/v7NEJQglbDmyC5VMgnvhiuBUMB8GA1UdIwQYMBaA
FFjAHl+RRaVmqXrMkKGTItAqxcX6MHgGA1UdEQRxMG+GbWh0dHBzOi8vZ2l0aHVi
LmNvbS9rdWJld2FyZGVuL2dpdGh1Yi1hY3Rpb25zLy5naXRodWIvd29ya2Zsb3dz
L3JldXNhYmxlLXJlbGVhc2UtcG9saWN5LXJ1c3QueW1sQHJlZnMvaGVhZHMvdjEw
NgYKKwYBBAGDvzABAwQoMmJiMGQ4NjZjMzFmOGMyZTQ3NDMxMDI4M2ExNmFkMWFi
NjBlZjA1YjAuBgorBgEEAYO/MAEFBCBrdWJld2FyZGVuL3VzZXItZ3JvdXAtcHNw
LXBvbGljeTAcBgorBgEEAYO/MAEEBA5SZWxlYXNlIHBvbGljeTASBgorBgEEAYO/
MAECBARwdXNoMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5n
aXRodWJ1c2VyY29udGVudC5jb20wHgYKKwYBBAGDvzABBgQQcmVmcy90YWdzL3Yw
LjIuMDAKBggqhkjOPQQDAwNpADBmAjEAyGQbNCkOifStO7yCCfF8yXyc144ANn2x
Ty92WYC0pTaVhviOED47fgD6TncKf+92AjEAjBfjLmCG/Mwrh8t+gfHJEAWWEc9Q
+j9NR4wF66uABS/TTh5CYlrnIuqSD+GBHGwV
-----END CERTIFICATE-----
dev.sigstore.cosign/timestamp {"signatures":[{"keyid":"b6710623a30c010738e64c5209d367df1c0a18cf90e6ab5292fb01680f83453d","sig":"3046022100f666a7f4b3d85d8003f2c166e27827dfa0c4ab9282e9dab19485f4e702c61700022100dfe826e0edab5f80a40f08cc87b87777a4db30775d85684fe4950e797f2f565c"}],"signed":{"_type":"timestamp","spec_version":"1.0","version":15,"expires":"2022-03-08T19:14:05Z","meta":{"snapshot.json":{"length":1655,"hashes":{"sha256":"36cf063d0717f6dc03e23027721adcd69b684d293956d3a1a7db7b0848f711d7","sha512":"f90946d0a2dc58dae4505cfb91517a40299adf9e8719f52af187e2025aad69fcdeaeded271ec25db24869841c16fbe24f3fc56f56af8fdbb8808dccec4636b64"},"version":15}}}}
dev.sigstore.cosign/bundle {"SignedEntryTimestamp":"MEUCIEfu4qR+HsexSDk5h2QXMduvoRCX10J+4CLQWtYw5VD6AiEAyYCEjvJdv2Sr5tZ4LApnddH/4v+CoV1QkuvbCQ3iIUM=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwMjZhZjY3NjgyYTg1ZDQyNGU3ZDk1ZGI0NjAxNzE2MzVmNWMzOTU3ZDY3YjUzNDk5YmVjZTkxMmNjMDQxM2NjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNXNWZRZ1BUUTdaTlNuRkhzbHJOTlFrS2dTSVFpOGNSMTU5UEExc0s4VGlRSWhBSndMOWJPcUJKbVduN1lLZG9Tem80c2xPZ2s4SkJCanFYZHNydDNyeVF0QiIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUlNla05EUVhONVowRjNTVUpCWjBsVVlsQlZXbXhWUm10clFVaDBZbnBqTTNKNlF5OHplbGhxTVVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWw1VGxSRk1rMTZRWGROYkc5WVJGUkplVTFFU1hsT1ZFVXlUa1JCZDAxV2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlNMMDgxWXpaYVNUVkNla0ozWlc5RlNXRnROSFZYZFRWbWNYcEllREFLTTFCVVEyZG1XSGw1ZGtscWIzSjZPWGRZTURoaWMyNWthMGhrVjJaR1QySlZLMUI2ZEdKNFdEYzRRVzQwTTFsM09TOW1TSFJQT1ROdk5FbENOV3BEUXdwQlpVbDNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSa05RTDNZM1RrVktVV2RzWWtSdGVVTTFWazFuYm5ab2FYVkNWVTFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFJWjBkQk1WVmtSVkZTZUUxSEswZGlWMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbVJYU214a01rWjVXa2RXZFV3eVpIQmtSMmd4V1dreGFGa3pVbkJpTWpWNlRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2Q2t3elNteGtXRTVvV1cxNGJFeFlTbXhpUjFab1l6SlZkR05IT1hOaFYwNDFURmhLTVdNelVYVmxWekZ6VVVoS2JGcHVUWFpoUjFab1draE5kbVJxUlhjS1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOU5iVXBwVFVkUk5FNXFXbXBOZWtadFQwZE5lVnBVVVROT1JFMTRUVVJKTkUweVJYaE9iVVpyVFZkR2FRcE9ha0pzV21wQk1WbHFRWFZDWjI5eVFtZEZSVUZaVHk5TlFVVkdRa05DY21SWFNteGtNa1o1V2tkV2RVd3pWbnBhV0VsMFdqTktkbVJZUVhSalNFNTNDa3hZUW5aaVIyeHFaVlJCWTBKbmIzSkNaMFZGUVZsUEwwMUJSVVZDUVRWVFdsZDRiRmxZVG14SlNFSjJZa2RzYW1WVVFWTkNaMjl5UW1kRlJVRlpUeThLVFVGRlEwSkJVbmRrV0U1dlRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpCaFZ6bDFZM2sxYmdwaFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NHZFpTMHQzV1VKQ1FVZEVkbnBCUWtKblVWRmpiVlp0WTNrNU1GbFhaSHBNTTFsM0NreHFTWFZOUkVGTFFtZG5jV2hyYWs5UVVWRkVRWGRPY0VGRVFtMUJha1ZCZVVkUllrNURhMDlwWmxOMFR6ZDVRME5tUmpoNVdIbGpNVFEwUVU1dU1uZ0tWSGs1TWxkWlF6QndWR0ZXYUhacFQwVkVORGRtWjBRMlZHNWpTMllyT1RKQmFrVkJha0ptYWt4dFEwY3ZUWGR5YURoMEsyZG1TRXBGUVZkWFJXTTVVUW9yYWpsT1VqUjNSalkyZFVGQ1V5OVVWR2cxUTFsc2NtNUpkWEZUUkN0SFFraEhkMVlLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUW89In19fX0=","integratedTime":1645806604,"logIndex":1506651,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}
dev.sigstore.cosign/chain -----BEGIN CERTIFICATE-----
MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl
LmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7
XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex
X69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j
YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY
wB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ
KsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM
WP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9
TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ
-----END CERTIFICATE-----
dev.cosignproject.cosign/signature MEYCIQCW5fQgPTQ7ZNSnFHslrNNQkKgSIQi8cR159PA1sK8TiQIhAJwL9bOqBJmWn7YKdoSzo4slOgk8JBBjqXdsrt3ryQtB
Verifying policies
Users can check if a policy is properly signed with cosign
or kwctl
.
Both have similar command line flags which allow users to check the policy
signatures. To check if the binary is signed with a key, the users can use kwctl
like this:
$ kwctl verify -k cosign.pub ghcr.io/jvanz/policies/user-group-psp:latest
2022-03-29T14:49:31.878180Z INFO kwctl::verify: Policy successfully verified
Or cosign
:
$ cosign verify --key cosign.pub ghcr.io/jvanz/policies/user-group-psp:latest
Verification for ghcr.io/jvanz/policies/user-group-psp:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"ghcr.io/jvanz/policies/user-group-psp"},"image":{"docker-manifest-digest":"sha256:af520a8ccee03811d426c48634b7007f1220c121cc23e14962bb64510585ce97"},"type":"cosign container image signature"},"optional":null}]
Configuring the policy server to check policy signatures
To configure Kubewarden to run only trusted policies, the users must create
a ConfigMap
with the minimum signature requirements needed for policies to be
executed in the environment. The ConfigMap
follows the same structure of the
file described in the Signature Config Reference section used to verify policy
in the kwctl
utility. The only difference in this case would be that the
ConfigMap
should define all the configurations under the verification-config
field. For example, let's consider that the users want to run policies signed
by the Kubewarden GitHub organization. A sample ConfigMap
for this scenario would be:
$ cat kubewarden_signatures.yaml
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden
# note that the data is stored under verification-config field
$ kubectl create configmap my-signatures-configuration --from-file=verification-config=kubewarden_signatures.yaml
$ kubectl get configmap -o yaml my-signatures-configuration
apiVersion: v1
data:
verification-config: |
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden
kind: ConfigMap
metadata:
creationTimestamp: "2022-03-29T18:27:20Z"
name: my-signatures-configuration
namespace: default
resourceVersion: "10279"
uid: d53e1c56-1fee-45de-92f5-9bd73b8cead4
It is also possible to use the kwctl scaffold verification-config
to generate
a default verification config file to be used in the ConfigMap
:
$ kwctl scaffold verification-config > verification_config.yaml
$ cat verification_config.yaml
# Default Kubewarden verification config
#
# With this config, the only valid policies are those signed by Kubewarden
# infrastructure.
#
# This config can be saved to its default location (for this OS) with:
# kwctl scaffold verification-config > /home/jvanz/.config/kubewarden/verification-config.yml
#
# Providing a config in the default location enables Sigstore verification.
# See https://docs.kubewarden.io for more Sigstore verification options.
---
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden
repo: ~
annotations: ~
anyOf: ~
$ kubectl create configmap my-signatures-configuration --from-file=verification-config=verification_config.yaml
configmap/my-signatures-configuration created
$ kubectl get configmap -o yaml my-signatures-configuration
apiVersion: v1
data:
verification-config: |+
# Default Kubewarden verification config
#
# With this config, the only valid policies are those signed by Kubewarden
# infrastructure.
#
# This config can be saved to its default location (for this OS) with:
# kwctl scaffold verification-config > /home/jvanz/.config/kubewarden/verification-config.yml
#
# Providing a config in the default location enables Sigstore verification.
# See https://docs.kubewarden.io for more Sigstore verification options.
---
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden
repo: ~
annotations: ~
anyOf: ~
kind: ConfigMap
metadata:
creationTimestamp: "2022-04-07T11:54:27Z"
name: my-signatures-configuration
namespace: default
resourceVersion: "1317"
uid: 74dec846-7fcd-4b4b-8184-700c816f685a
After creating the ConfigMap
to store the signature requirements, the users
can configure a Policy Server to start validating policy signatures by setting the
ConfigMap
name in the field verificationConfig
.
apiVersion: policies.kubewarden.io/v1alpha2
kind: PolicyServer
metadata:
name: default
finalizers:
- kubewarden
spec:
image: ghcr.io/kubewarden/policy-server:v0.2.7
serviceAccountName: policy-server
replicas: 1
verificationConfig: your_configmap #name of the confimap with the signatures requirements
env:
- name: KUBEWARDEN_ENABLE_METRICS
value: "1"
- name: KUBEWARDEN_LOG_FMT
value: otlp
- name: "KUBEWARDEN_LOG_LEVEL"
value: "info"
If you deploy the default Policy Server using the kubewarden-defaults
Helm chart, you can configure this field by setting the ConfigMap
name in the
policyServer.verificationConfig
value.
Now, the PolicyServer will reject untrusted AdmissionPolicies and ClusterAdmissionPolicies, by refusing to start. The user must remove the untrusted policy or fix the signatures requirements in order to get the PolicyServer running again.
Signature Config Reference
Users can also provide a file with all the signature requirements they want to validate. A sample file is shown below:
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden # mandatory
annotations:
env: prod
anyOf: # at least `anyOf.minimumMatches` are required to match
minimumMatches: 2 # default is 1
signatures:
- kind: pubKey
owner: flavio # optional
key: .... # mandatory
annotations: # optional
env: prod
foo: bar
- kind: pubKey
owner: victor # optional
key: .... # mandatory
- kind: genericIssuer
issuer: https://github.com/login/oauth
subject:
equal: alice@example.com
- kind: genericIssuer
issuer: https://token.actions.githubusercontent.com
subject:
equal: https://github.com/flavio/policy-secure-pod-images/.github/workflows/release.yml@refs/heads/main
- kind: genericIssuer
issuer: https://token.actions.githubusercontent.com
subject:
urlPrefix: https://github.com/flavio/
- kind: genericIssuer
issuer: https://token.actions.githubusercontent.com
subject:
urlPrefix: https://github.com/kubewarden # <- it will be post-fixed with `/` for security reasons
- kind: githubAction
owner: flavio # mandatory
repo: policy1 # optional
- kind: pubKey
owner: alice # optional
key: .... # mandatory
The configuration is composed of two sections:
anyOf
: The policy will be trusted if theminimumMatches
criterion is fulfilled. In the above example, theminimumMatches
field has been assigned a value 2. This means that at least two of the signature requirements listed need to be fulfilled for the policy to be trusted. The default value that theminimumMatches
field assumes is1
.allOf
: All the signature requirements defined in this section must be valid so that the policy can be trusted.
Each of these sections can contain one or more signature requirements.
Users can also define both sections in one file as shown above. In this situations, all
the signatures requirements from the allOf
AND a minimum number of matches
from the anyOf
section as described in the minimumMatches
field must be satisfied.
Signature validation
The users can validate different signatures in the anyOf
and allOf
sections.
It's possible to validate the public key and the keyless data used to sign the policy.
Public key validation
To ensure a policy is signed with the given public key, the users can
define the key data and the owner of the key used to sign the policy. As illustrated
bellow, it is necessary to define the kind as pubKey
and insert the public
key data in the key
field. The owner field is optional, but can be useful to clarify who owns the key.
Example
- kind: pubKey
owner: bob # optional
key: |
-----BEGIN PUBLIC KEY-----
MBFKHFDGHKIJH0CAQYIKoZIzj0DAQcDQgAEX0HFTtCfTtPmkx5p1RbDE6HJSGAVD
BVDF6SKFSF87AASUspkQsN3FO4iyWodCy5j3o0CdIJD/KJHDJFHDFIu6sA==
-----END PUBLIC KEY-----
Keyless signatures validation
When signed in keyless mode, we do not have the public key to verify. In this
situation, the users can verify the field filled with the OIDC data used during
the signing process. For that, it's necessary to define the kind of the signature
validation as genericIssuer
. Therefore, it's possible to verify three pieces
of information from the signature:
issuer
(mandatory): matches theIssuer
attribute in the certificate generated by Fulcio. This shows the OIDC used to sign the policysubject
: field used to match theSubject
attribute in Fulcio's certificate. TheSubject
field contains the information of the user used to authenticate again the OIDC provider. The verification field,subject
, can have two children fields:equal
forces theSubject
from the certificate to be equal to the value defined in the signature validation;urlPrefix
forces the value of the certificate'sSubject
field value to be prefixed by the value defined in the signature requirement.
Note well: both the
cosign verify
and thekwctl inspect
commands can be used to show the information about keyless signatures. Examples
The following configuration requires the policy to be signed by Alice in a keyless manner using the GitHub OIDC:
- kind: genericIssuer
issuer: https://github.com/login/oauth
subject:
equal: alice@example.com
While the next configuration enforces the policy to be signed in the context of a GitHub actions environment
from a repository owned by the GitHub user flavio
:
- kind: genericIssuer
issuer: https://token.actions.githubusercontent.com
subject:
urlPrefix: https://github.com/flavio
GitHub actions signature verification
The signature validation kind, githubAction
is used to validate policies signed in a GitHub
Actions environment. It can be achieved with the genericIssuer
kind as well. But
githubAction
simplifies the signature requirement creation process by defining two additional fields:
owner
(mandatory): GitHub ID of the user or organization to be trustedrepo
: the name of the repository to be trusted
For example, the last configuration snippet shown above that leveraged a genericIssuer
, could be rewritten in this way:
- kind: githubAction
owner: flavio
Signature annotations validation
All the signature types can have one additional optional validation field, annotations
.
These fields are key/value data added by the users during the signing process. Kubewarden
makes possible to ensure that policies are signed by trusted users and have specific annotations.
Examples
If the users want to trust only policies which have been signed with a specific key
and the annotation environment
with the value production
, they can define the
following validation:
- kind: pubKey
key: |
-----BEGIN PUBLIC KEY-----
MBFKHFDGHKIJH0CAQYIKoZIzj0DAQcDQgAEX0HFTtCfTtPmkx5p1RbDE6HJSGAVD
BVDF6SKFSF87AASUspkQsN3FO4iyWodCy5j3o0CdIJD/KJHDJFHDFIu6sA==
-----END PUBLIC KEY-----
annotations:
environment: production
How to use signature verification config file to check a policy OCI artefact
To test if a given policy passes signature verification using the
verification config file, use the --verification-config-path
flag of the kwctl verify
command
$ cat signatures_requirements.yaml
apiVersion: v1
allOf:
- kind: pubKey
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5Q+cN1Jj2S7N05J4AXnqwP2DyzSg
Mc+raYce2Wthrd30MSgFtoh5ADAkCd/nML2Nx8UD9KBuASRb0gG5jXqgMQ==
-----END PUBLIC KEY-----
$ kwctl verify --verification-config-path signatures_requirements.yaml ghcr.io/jvanz/policies/user-group-psp:latest
2022-03-29T17:34:37.847169Z INFO kwctl::verify: Policy successfully verified
While the previous example tests if the policy is signed by the given key, the next one checks if a given policy came from the Kubewarden organization:
$ cat kubewarden_signatures.yaml
apiVersion: v1
allOf:
- kind: githubAction
owner: kubewarden
$ kwctl verify --verification-config-path kubewarden_signatures.yaml ghcr.io/kubewarden/policies/user-group-psp:latest
2022-03-29T18:07:39.062292Z INFO kwctl::verify: Policy successfully verified