Configuring PolicyServers to use a private Sigstore instance
You can configure a PolicyServer to use a private or self-hosted Sigstore instance for policy signature verification, instead of the default public Sigstore infrastructure. This is useful in air-gapped environments or when you operate your own Sigstore deployment.
Prerequisites​
- A running private Sigstore instance, with accessible Fulcio, Rekor, TSA, and CT log services
kubectlaccess to thekubewardennamespace- The
cosignCLI installed locally. jqinstalled locally
Step 1 – Generate the ClientTrustConfig JSON​
The PolicyServer expects a ClientTrustConfig JSON that describes your private
Sigstore instance's trust anchors and signing configuration.
Obtain certificates and public keys​
Retrieve the following from your private Sigstore instance:
fulcio.pem— Fulcio CA certificate chain (PEM)rekor.pub— Rekor transparency log public keytsa.pem— Timestamp Authority certificate chain (PEM)ctfe.pub— CT log public key
# Fulcio CA certificate chain
curl --fail -o fulcio.pem "${FULCIO_URL}/api/v1/rootCert"
# Rekor transparency log public key
curl --fail -o rekor.pub "${REKOR_URL}/api/v1/log/publicKey"
# Timestamp Authority certificate chain
curl --fail -o tsa.pem "${TSA_URL}/api/v1/timestamp/certchain"
# CT log public key (from the Kubernetes secret in tuf-system namespace)
kubectl get secret -o json -n tuf-system ctlog-public-key \
| jq -r ".data.public" | base64 -d > ctfe.pub
Make sure you that you safely download the certificates and keys from you Sigstore instance. Otherwise, you may ended configuring your Kubewarden with compromised data.
The above commands are examples on how to do that in a testing environment. Please, consult the appropriate team in our organization to learn how to get this information properly.
Generate the trusted root and signing config​
Set environment variables pointing to your private Sigstore service URLs:
export FULCIO_URL=https://fulcio.example.com
export REKOR_URL=https://rekor.example.com
export TSA_URL=https://tsa.example.com
export CTLOG_URL=https://ctlog.example.com
export ISSUER_URL=https://oidc.example.com
Run cosign to generate the trusted root:
cosign trusted-root create \
--fulcio="url=$FULCIO_URL,certificate-chain=fulcio.pem" \
--rekor="url=$REKOR_URL,public-key=rekor.pub,start-time=2024-01-01T00:00:00Z" \
--tsa="url=$TSA_URL,certificate-chain=tsa.pem" \
--ctfe="url=$CTLOG_URL,public-key=ctfe.pub,start-time=2024-01-01T00:00:00Z" \
--out trusted_root.json
Run cosign to generate the signing config:
cosign signing-config create \
--fulcio="url=$FULCIO_URL,api-version=1,start-time=2024-01-01T00:00:00Z,operator=sigstore.dev" \
--rekor="url=$REKOR_URL,api-version=1,start-time=2024-01-01T00:00:00Z,operator=sigstore.dev" \
--rekor-config="ANY" \
--oidc-provider="url=$ISSUER_URL/auth,api-version=1,start-time=2024-01-01T00:00:00Z,operator=sigstore.dev" \
--tsa="url=$TSA_URL/api/v1/timestamp,api-version=1,start-time=2024-01-01T00:00:00Z,operator=sigstore.dev" \
--tsa-config="EXACT:1" \
--out signing_config.json
--oidc-provider is optional. Include it only if your private Sigstore instance
has a dedicated OIDC provider. If your policies are signed using Kubernetes service
account tokens (via kubectl create token), omit this flag.
The start-time value should be set to a date before your certificates were issued
(or the deployment date of your Sigstore instance).
For a full description of all available flags and JSON field definitions for both commands, refer to the cosign CLI reference and the Sigstore client trust config specification.
Combine into trust_config.json​
cat << EOF > trust_config.json
{
"mediaType": "application/vnd.dev.sigstore.clienttrustconfig.v0.1+json",
"trustedRoot": $(cat trusted_root.json),
"signingConfig": $(cat signing_config.json)
}
EOF
Step 2 – Create a ConfigMap in the kubewarden namespace​
Store the ClientTrustConfig JSON in a ConfigMap with the key
sigstore-trust-config. The ConfigMap must be in the admission
controller namespace. In this example is kubewarden:
kubectl --namespace kubewarden create configmap my-sigstore-trust-config \
--from-file=sigstore-trust-config=trust_config.json
Step 3 – Create a verification config ConfigMap​
Create a verification_config.yaml file specifying the certificate identity
constraints for your signed policies. For example, when using a Kubernetes
service account token:
allOf:
- kind: genericIssuer
issuer: https://kubernetes.default.svc.cluster.local
subject:
equal: https://kubernetes.io/namespaces/<namespace>/serviceaccounts/<serviceaccount>
anyOf: null
Replace <namespace> and <serviceaccount> with the namespace and service
account used when signing the policy.
Create the ConfigMap:
kubectl --namespace kubewarden create configmap my-verification-config \
--from-file=verification-config=verification_config.yaml
The ConfigMap key must be verification-config (not the filename).
Kubewarden looks for this exact key.
Step 4 – Configure the PolicyServer​
Set spec.sigstoreTrustConfig and spec.verificationConfig on your PolicyServer
to the names of the ConfigMaps you created:
apiVersion: policies.kubewarden.io/v1
kind: PolicyServer
metadata:
name: default
spec:
image: ghcr.io/kubewarden/policy-server:latest
replicas: 1
sigstoreTrustConfig: my-sigstore-trust-config
verificationConfig: my-verification-config
The controller mounts the ConfigMaps into the policy-server pod and configures it to use your private Sigstore instance for signature verification, enforcing the identity constraints specified in the verification config.
Any user with write access to the ConfigMap referenced by sigstoreTrustConfig
can influence policy signature verification. This could allow them to substitute
a different Sigstore trust root and bypass signature checks.
Restrict access to this ConfigMap using Kubernetes RBAC, following the principle of least privilege.