Signature verifier policies
Kubewarden implements support for the Sigstore project. This allows implementing a "Secure Supply Chain" for your cluster.
Part of the function of the secure supply chain is to ensure that all container images running in the cluster are signed and verified. This proves that they come from their stated authors, with no tampering. For further reading, check the docs on how we implement a Secure Supply Chain for the policies themselves.
Sigstore signatures are stored inside of container registries, next to the OCI object being signed. They can be a container image or a more generic OCI artifact, like a Kubewarden policy. When an object is signed, its signatures are stored as layers of an OCI object created by Sigstore. Policies needing to check signatures of containers need to check those layers, and need to pull the signature layers to see the signatures themselves.
Obtaining and operating with those OCI layers needs to happen outside the WebAssembly guest (the policy).
Hence, this is done by the WebAssembly runtime,
Kubewarden's policy-server
or kwctl
.
The different language SDKs for Kubewarden policies take care of this. They provide functions for verification of container image, Kubewarden policies, Helm charts and other kinds of OCI artifact. This ensures a safe and tested code path for verification.
Pulling data from a registry and cryptographically verifying signatures can be time and computationally expensive,
so the WebAssembly runtime (PolicyServer, kwctl
) ensures both
signature pulls and verification computations are cached.
The cached entries are automatically expired after 60 seconds to help prevent stale data from being served.
The SDKs provide functions similar to the following:
-
verify_pub_keys_image(
image_url: string,
vector_of_pub_keys: vector<string>,
vector_of_sigstore_annotations: Vector<(key, value: string)>
)
returns (is_trusted: bool, digest_of_verified_image: string) -
verify_keyless_exact_match(
image_url: string,
vector_of_tuples_issuer_and_subject: vector<(issuer, subject: string)>,
vector_of_sigstore_annotations: vector<(key, value: string)>
)
returns (is_trusted: bool, digest_of_verified_image: string)
Both functions verify that the image is signed and satisfy the constraints.
On success, the functions return the digest of the verified image. It is now policy responsibility to ensure that containers are instantiated from that digest, and not from a tag that may not match that checksum digest (and therefore be compromised).
An example​
The Kubewarden team provides a verifier policy that enforces Sigstore signatures for all containers. It's built using Rust and with the Rust SDK. The policy ensures that the containers are signed, and optionally, mutates the requests. The mutation appends the verified checksum to the tag of the image. Check the documentation for specifics.
This policy may cover all your needs, but in case you would prefer a different UX, of course you can build on top of it or any of the other SDKs.
WaPC protocol contract
In case you are implementing your own language SDK, these are the functions a policy, that verifies signatures, can use:
waPC function name | Input payload | Output payload |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Marked for deprecation:
WaPC function name | Input payload | Output payload |
|
|
|
|
|
|