diff --git a/.spelling b/.spelling
index 1e39ea11eab..6d87f42c56d 100644
--- a/.spelling
+++ b/.spelling
@@ -443,6 +443,7 @@ retryable
retweets
routable
runtime
+runtimes
signoff
sigstore
stdout
@@ -650,6 +651,8 @@ v1.13.
v1.12.5
v1.12.6
v1.12.7
+v1.14.0
+v1.14.x
liveness
apiservices
arm64
diff --git a/content/docs/manifest.json b/content/docs/manifest.json
index 2b2bea6fc75..e6ca68dbc47 100644
--- a/content/docs/manifest.json
+++ b/content/docs/manifest.json
@@ -586,6 +586,10 @@
{
"title": "Managing public trust in kubernetes with trust-manager",
"path": "/docs/tutorials/getting-started-with-trust-manager/README.md"
+ },
+ {
+ "title": "Setting default certificate values",
+ "path": "/docs/tutorials/certificate-defaults/README.md"
}
]
},
diff --git a/content/docs/tutorials/README.md b/content/docs/tutorials/README.md
index b286d96594b..d50999605ea 100644
--- a/content/docs/tutorials/README.md
+++ b/content/docs/tutorials/README.md
@@ -25,6 +25,8 @@ for you to learn from. Take a look!
- [Securing an Istio service mesh with cert-manager](./istio-csr/istio-csr.md): Tutorial for
securing an Istio service mesh using a cert-manager issuer.
- [Obtaining SSL certificates with the ZeroSSL](./zerossl/zerossl.md): Tutorial describing usage of the ZeroSSL as external ACME server.
+- [Managing public trust in Kubernetes with trust-manager](./getting-started-with-trust-manager/README.md): Learn how to deploy and configure trust-manager to automatically distribute your approved Public CA configuration to your Kubernetes cluster.
+- [Learn how to set Certificate defaults automatically](./certificate-defaults/README.md): Learn how to use Kyverno `ClusterPolicy` to set default values for cert-manager `Certificates`.
### External Tutorials
diff --git a/content/docs/tutorials/certificate-defaults/README.md b/content/docs/tutorials/certificate-defaults/README.md
new file mode 100644
index 00000000000..d55c04ddaa3
--- /dev/null
+++ b/content/docs/tutorials/certificate-defaults/README.md
@@ -0,0 +1,571 @@
+---
+title: Learn how to set Certificate defaults automatically
+description: |
+ Learn how to use Kyverno ClusterPolicy to set default values for cert-manager Certificates cluster wide.
+---
+
+*Last Verified: 05 January 2024*
+
+# Objective
+
+We will set up a cluster where a user specifies as little YAML as possible in `Certificate` resources.
+This will be achieved by utilizing Kyverno to apply custom "default" values to the `Certificate` fields, that are not specified by a user.
+
+There are some benefits to having defaults:
+
+- `Certificate` consumers minimize their YAML resources.
+- `Certificate` consumers retain flexibility to override fields when needed.
+- Cluster operators can decide what the default should be, rather than having to rely on built-in defaults from cert-manager.
+
+## Use cases
+
+By setting custom defaults across our cluster, we enable platform teams to tackle use cases such as:
+
+- **To ensure that `CertificateRequest` resources get cleaned up.**
+
+ Use a `ClusterPolicy` to set a custom default value for the `Certificate.Spec.RevisionHistoryLimit` field.
+
+- **To help your users choose secure default key settings for their `Certificate` resources.**
+
+ Use a `ClusterPolicy` to set custom default values for the `Certificate.Spec.PrivateKey` fields.
+
+- **To default the `Issuer` for users within the cluster.**
+
+ Use a `ClusterPolicy` to set a custom default for the `Certificate.spec.issuerRef` fields.
+
+- **To set a default pattern for the naming of the `Secret` where the certificate will be populated.**
+
+ Use a `ClusterPolicy` to set a custom default value for the `spec.secretName` required field.
+
+- **Make application developers' lives easier by allowing them to create secure X.509 TLS certificates with the minimum of configuration.**
+
+ Use a `ClusterPolicy` to set all other required `Certificate.spec` fields.
+ Only a single identity specification field will be required, one of:
+ - `commonName` or `literalSubject`
+ - `dnsNames`
+ - `uris`
+ - `emailAddresses`
+ - `ipAddresses`
+ - `otherNames`
+
+## Process
+
+We will set up defaults for three different scenarios, getting slightly more advanced each time:
+
+1. Setting defaults for optional `Certificate` resource fields.
+2. Setting defaults for required `Certificate` resource fields.
+3. Setting defaults for `Certificate` resource fields, when using `Ingress` annotations to request certificates.
+
+# Setup
+
+## Prerequisites
+
+**đģ Software**
+
+1. [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl): The Kubernetes
+ command-line tool which allows you to configure Kubernetes clusters.
+1. [helm](https://helm.sh/): A package manager for Kubernetes.
+1. [kind](https://kind.sigs.k8s.io/) (**OPTIONAL**): For creating a local
+ Kubernetes environment that runs in Docker or other container runtimes.
+
+## Local Kubernetes Environment
+
+> â ī¸ This step can be skipped if you have another Kubernetes environment.
+
+1. Create a cluster environment using `kind` for this tutorial.
+
+ ```shell
+ kind create cluster --name defaults
+ ```
+
+ > ⲠIt should take less than one minute to create the cluster, depending on your machine.
+ >
+ > â ī¸ This cluster is only suitable for learning purposes. It is not suitable for production use.
+
+## Software Installation
+
+Once you have your cluster environment, install the required Kubernetes packages using `helm`.
+
+1. Set some environment variables for the helm chart versions:
+
+ ```shell
+ export CERT_MANAGER_CHART_VERSION="v1.14.0-alpha.0" \
+ KYVERNO_CHART_VERSION="3.1.1" \
+ INGRESS_NGINX_CHART_VERSION="4.9.0"
+ ```
+
+1. Install cert-manager
+
+ ```shell
+ helm upgrade --install cert-manager cert-manager \
+ --namespace cert-manager \
+ --version $CERT_MANAGER_CHART_VERSION \
+ --set installCRDs=true \
+ --set startupapicheck.enabled=false \
+ --create-namespace \
+ --repo https://charts.jetstack.io/
+ ```
+
+1. Install Kyverno
+
+ ```shell
+ helm upgrade --install kyverno kyverno \
+ --namespace kyverno-system \
+ --version $KYVERNO_CHART_VERSION \
+ --create-namespace \
+ --repo https://kyverno.github.io/kyverno/
+ ```
+
+1. Install ingress-nginx
+
+ ```shell
+ helm upgrade --install ingress-nginx ingress-nginx \
+ --namespace ingress-nginx \
+ --version $INGRESS_NGINX_CHART_VERSION \
+ --create-namespace \
+ --repo https://kubernetes.github.io/ingress-nginx
+ ```
+
+> For complete installation instructions, please refer to the following links:
+> - [cert-manager installation instructions](./../../../docs/installation/helm.md)
+> - [Kyverno installation instructions](https://kyverno.io/docs/installation/methods/#install-kyverno-using-helm)
+> - [ingress-nginx installation instructions](https://kubernetes.github.io/ingress-nginx/deploy/)
+
+# Setting Defaults
+
+The main tutorial starts here with some background, before tackling each of the three scenarios.
+
+## Required vs Non-required
+
+The `Certificate` resource has a `spec` section with a number of "required" fields.
+This means these fields must be present when you create a `Certificate` resource.
+There are also a number of other fields that are not required to be explicitly defined on each `Certificate` resource.
+This essentially means the value of one of these fields is either not required, or has defaults defined somewhere else.
+That somewhere else could be in the cert-manager code base, or indeed by the issuer that creates and returns the X.509 certificate.
+Let's explore how we can manipulate these values to be something custom and make the `Certificate` user's life easier.
+
+We will set up some `ClusterPolicy` resources and `Certificate` resources in this tutorial.
+We will make reference to a `ClusterIssuer` in the `Certificate` spec that doesn't exist, but for this tutorial the `ClusterIssuer` is not required as we will not actually be requesting certificates.
+That means anyone can follow this tutorial even without their own domain.
+
+> â ī¸ To make it easy to get started we are using cluster scoped `ClusterPolicy` resources.
+> You can scope your defaults to the namespace level through the use of `Policy` resources in the future, but that will not be covered in this tutorial.
+
+## 1 - Defaulting optional fields
+
+In this section we will create rules which set three fields for all `Certificate` resources automatically.
+None of the three fields here are required fields, but they might need to be set depending on platform and issuer preferences.
+These rules will:
+
+- Set a default value of: `revisionHistoryLimit: 2`.
+- Set a [default value of `Always` under `spec.privateKey.rotationPolicy`](../../usage/certificate.md#the-rotationpolicy-setting).
+- Set defaults for all `spec.privateKey` fields.
+
+> âšī¸ Note how these rules tackle the first two of our [uses cases](#use-cases).
+
+1. First take a look at the `ClusterPolicy`:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-0.yaml
+ ```
+ đ `cpol-mutate-certificates-0.yaml`
+
+1. Apply the policy to the cluster and check that it is ready:
+
+ ```shell
+ kubectl apply -f cpol-mutate-certificates-0.yaml
+ kubectl get cpol
+ ```
+
+ When the `ClusterPolicy` is ready the output should look like this:
+
+ ```log
+ NAME ADMISSION BACKGROUND VALIDATE ACTION READY AGE MESSAGE
+ mutate-certificates true true Audit True 0s Ready
+ ```
+
+1. Now inspect the "test-revision" `Certificate`:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-revision.yaml
+ ```
+ đ `cert-test-revision.yaml`
+
+ You can see that we have set the most minimal configuration currently possible, specifying only a DNS name for the certificate, where to save it (`secretName`) and the issuer to use to request the certificate (`issuerRef`).
+
+1. Use the following command to *dry-run apply* the certificate and then `diff` it against the original resource, to see how the defaults from our `ClusterPolicy` are applied:
+
+ ```shell
+ kubectl apply -f cert-test-revision.yaml --dry-run=server -o yaml | diff -uZ cert-test-revision.yaml -
+ ```
+
+ This command should return some output similar to this example:
+
+ ```yaml
+ --- cert-test-revision.yaml 2024-01-08 12:14:59.225074232 +0000
+ +++ - 2024-01-12 17:37:51.076593214 +0000
+ @@ -1,8 +1,14 @@
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ + annotations:
+ + kubectl.kubernetes.io/last-applied-configuration: |
+ + {"apiVersion":"cert-manager.io/v1","kind":"Certificate","metadata":{"annotations":{},"name":"test-revision","namespace":"default"},"spec":{"dnsNames":["example.com"],"issuerRef":{"group":"cert-manager.io","kind":"ClusterIssuer","name":"not-my-corp-issuer"},"secretName":"test-revision-cert"}}
+ + creationTimestamp: "2024-01-12T17:37:51Z"
+ + generation: 1
+ name: test-revision
+ namespace: default
+ + uid: 9f9a4f0a-4aa7-427d-ae4b-c1716fed8246
+ spec:
+ dnsNames:
+ - example.com
+ @@ -10,4 +16,10 @@
+ group: cert-manager.io
+ kind: ClusterIssuer
+ name: not-my-corp-issuer
+ + privateKey:
+ + algorithm: ECDSA
+ + encoding: PKCS1
+ + rotationPolicy: Always
+ + size: 521
+ + revisionHistoryLimit: 2
+ secretName: test-revision-cert
+ ```
+
+ We have successfully defaulted the `privateKey` and `revisionHistoryLimit` fields!
+
+1. Let's override all of these defaulted fields, to validate that we can still set what we want as an end user. To test this, let's use the "test-revision-override" `Certificate`:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml
+ ```
+ đ `cert-test-revision-override.yaml`
+
+ As before, *dry-run apply* and `diff` the output with the input file:
+
+ ```shell
+ kubectl apply -f cert-test-revision-override.yaml --dry-run=server -o yaml | diff -uZ cert-test-revision-override.yaml -
+ ```
+
+ Here you can see in the output there are no specification changes for the `Certificate` itself.
+ The `Certificate` already had all the fields defined that our `ClusterPolicy` rules would have affected.
+
+ ```yaml
+ --- cert-test-revision-override.yaml 2024-01-05 14:45:14.972562067 +0000
+ +++ - 2024-01-12 17:39:57.217028745 +0000
+ @@ -1,8 +1,14 @@
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ + annotations:
+ + kubectl.kubernetes.io/last-applied-configuration: |
+ + {"apiVersion":"cert-manager.io/v1","kind":"Certificate","metadata":{"annotations":{},"name":"test-revision-override","namespace":"default"},"spec":{"dnsNames":["example.com"],"issuerRef":{"group":"cert-manager.io","kind":"ClusterIssuer","name":"not-my-corp-issuer"},"privateKey":{"algorithm":"RSA","encoding":"PKCS8","rotationPolicy":"Never","size":4096},"revisionHistoryLimit":44,"secretName":"test-revision-override-cert"}}
+ + creationTimestamp: "2024-01-12T17:39:57Z"
+ + generation: 1
+ name: test-revision-override
+ namespace: default
+ + uid: 83a6ddbc-6903-479e-802d-e11149985338
+ spec:
+ dnsNames:
+ - example.com
+ ```
+
+## 2 - Defaulting required fields
+
+> â ī¸ This section requires cert-manager v1.14.x or newer to work properly out of the box.
+> See the [Appendix](#cert-manager-version-requirement) section for details.
+
+Now we can set a Kyverno `ClusterPolicy` to apply default values to any of the `Certificate` fields.
+This includes the *required* fields.
+In our example `ClusterPolicy` we will do two things:
+
+- Set the relevant `issuerRef` fields to default to use the "our-corp-issuer" `ClusterIssuer`.
+- Apply a default `secretName` that is the name of the `Certificate` object suffixed with "-cert".
+
+> âšī¸ Note how these rules are tackling the third and fourth [uses cases](#use-cases).
+
+1. Here is the `ClusterPolicy` resource to set both fields with defaults:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-1.yaml
+ ```
+ đ `cpol-mutate-certificates-1.yaml`
+
+ This `ClusterPolicy` is an extension of the policy we applied previously.
+
+1. Apply this policy:
+
+ ```shell
+ kubectl apply -f cpol-mutate-certificates-1.yaml
+ ```
+
+ You should see that our existing `ClusterPolicy` has been changed:
+
+ ```shell
+ clusterpolicy.kyverno.io/mutate-certificates configured
+ ```
+
+ Get the `ClusterPolicy` to validate it is "Ready":
+
+ ```shell
+ kubectl get cpol
+ ```
+
+ This command should return some output similar to this example:
+
+ ```shell
+ NAME ADMISSION BACKGROUND VALIDATE ACTION READY AGE MESSAGE
+ mutate-certificates true true Audit True 6m21s Ready
+ ```
+
+1. Look at the "test-minimal" `Certificate` designed to validate that all our rules within the policy are operative:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-minimal.yaml
+ ```
+ đ `cert-test-minimal.yaml`
+
+1. *Dry-run apply* and `diff` to validate all our defaults have applied to this minimal `Certificate`:
+
+ ```shell
+ kubectl apply -f cert-test-minimal.yaml --dry-run=server -o yaml | diff -uZ cert-test-minimal.yaml -
+ ```
+
+ This command should return some output similar to this example:
+
+ ```yaml
+ --- cert-test-minimal.yaml 2024-01-05 14:45:07.140668401 +0000
+ +++ - 2024-01-12 17:44:08.110290752 +0000
+ @@ -1,8 +1,25 @@
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ + annotations:
+ + kubectl.kubernetes.io/last-applied-configuration: |
+ + {"apiVersion":"cert-manager.io/v1","kind":"Certificate","metadata":{"annotations":{},"name":"test-minimal","namespace":"default"},"spec":{"dnsNames":["example.com"]}}
+ + creationTimestamp: "2024-01-12T17:44:08Z"
+ + generation: 1
+ name: test-minimal
+ namespace: default
+ + uid: 792d29c7-8cf3-4f3a-9f12-4fba396e0d6e
+ spec:
+ dnsNames:
+ - example.com
+ + issuerRef:
+ + group: cert-manager.io
+ + kind: ClusterIssuer
+ + name: our-corp-issuer
+ + privateKey:
+ + algorithm: ECDSA
+ + encoding: PKCS1
+ + rotationPolicy: Always
+ + size: 521
+ + revisionHistoryLimit: 2
+ + secretName: test-minimal-cert
+ ```
+
+ See how we have automatically populated the `spec.issuerRef` and `spec.secretName` field values.
+ This indicates the Kyverno `ClusterPolicy` has been applied to the supplied `Certificate` resource.
+
+1. To be absolutely sure we have not enforced any settings, let us explicitly set each property of the `Certificate` for which we have a default rule. We will use the "test-revision-override" `Certificate`:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml
+ ```
+ đ `cert-test-revision-override.yaml`
+
+1. *Dry-run apply* and `diff` this file:
+
+ ```shell
+ kubectl apply -f cert-test-revision-override.yaml --dry-run=server -o yaml | diff -uZ cert-test-revision-override.yaml -
+ ```
+
+ This command should return some output similar to this example:
+
+ ```yaml
+ --- cert-test-revision-override.yaml 2024-01-05 14:45:14.972562067 +0000
+ +++ - 2024-01-12 17:45:48.261997150 +0000
+ @@ -1,8 +1,14 @@
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ + annotations:
+ + kubectl.kubernetes.io/last-applied-configuration: |
+ + {"apiVersion":"cert-manager.io/v1","kind":"Certificate","metadata":{"annotations":{},"name":"test-revision-override","namespace":"default"},"spec":{"dnsNames":["example.com"],"issuerRef":{"group":"cert-manager.io","kind":"ClusterIssuer","name":"not-my-corp-issuer"},"privateKey":{"algorithm":"RSA","encoding":"PKCS8","rotationPolicy":"Never","size":4096},"revisionHistoryLimit":44,"secretName":"test-revision-override-cert"}}
+ + creationTimestamp: "2024-01-12T17:45:48Z"
+ + generation: 1
+ name: test-revision-override
+ namespace: default
+ + uid: d0ad7abe-c703-45f7-acf9-634b3a263cfa
+ spec:
+ dnsNames:
+ - example.com
+ ```
+
+ From this command you can see that none of the `Certificate` specification fields have been changed.
+ Only the metadata section has changed which tells us the policies have applied but not set any defaults because values were already provided.
+ This shows that you retain the flexibility to override the cluster defaults when needed.
+
+## 3 - Defaulting through Ingress Annotations
+
+Many cert-manager users don't create `Certificate` resources directly and instead use the [ingress-shim](https://cert-manager.io/docs/usage/ingress/) functionality.
+cert-manager creates `Certificate` resources based on the [supported annotations](https://cert-manager.io/docs/usage/ingress/#supported-annotations) and the `Ingress` specification.
+Let's see how we can still use `ClusterPolicy` to apply our defaults in this use case.
+
+1. This example `Ingress` resource has a `cert-manager.io/cluster-issuer` annotation which instructs cert-manager to create a `Certificate` with an `issuerRef` field pointing at a `ClusterIssuer` called `our-corp-issuer`:
+
+ ```yaml file=../../../../public/docs/tutorials/certificate-defaults/ingress.yaml
+ ```
+ đ `ingress.yaml`
+
+1. This annotation and the relevant `ingress.spec.tls` configuration are all we need so apply the resource:
+
+ ```shell
+ kubectl apply -f ingress.yaml
+ ```
+
+1. Now validate that the `Certificate` resource was automatically generated:
+
+ ```shell
+ kubectl get cert defaults-example-certificate-tls -o yaml
+ ```
+
+ This command should return some output similar to this example:
+
+ ```yaml
+ apiVersion: cert-manager.io/v1
+ kind: Certificate
+ metadata:
+ creationTimestamp: "2024-01-12T17:47:04Z"
+ generation: 1
+ name: defaults-example-certificate-tls
+ namespace: default
+ ownerReferences:
+ - apiVersion: networking.k8s.io/v1
+ blockOwnerDeletion: true
+ controller: true
+ kind: Ingress
+ name: defaults-example
+ uid: bea33a55-a9ed-4664-a56a-a679eb8272c3
+ resourceVersion: "584260"
+ uid: 43ced989-723b-4eac-bad0-f8bead6976df
+ spec:
+ dnsNames:
+ - app.example.com
+ issuerRef:
+ group: cert-manager.io
+ kind: ClusterIssuer
+ name: our-corp-issuer
+ privateKey:
+ algorithm: ECDSA
+ encoding: PKCS1
+ rotationPolicy: Always
+ size: 521
+ revisionHistoryLimit: 2
+ secretName: defaults-example-certificate-tls
+ usages:
+ - digital signature
+ - key encipherment
+ status:
+ conditions:
+ - lastTransitionTime: "2024-01-12T17:47:04Z"
+ message: Issuing certificate as Secret does not exist
+ observedGeneration: 1
+ reason: DoesNotExist
+ status: "True"
+ type: Issuing
+ - lastTransitionTime: "2024-01-12T17:47:04Z"
+ message: Issuing certificate as Secret does not exist
+ observedGeneration: 1
+ reason: DoesNotExist
+ status: "False"
+ type: Ready
+ nextPrivateKeySecretName: defaults-example-certificate-tls-nbjws
+ ```
+
+1. You can optionally validate that the "mutate-certificates" `ClusterPolicy` has been applied by viewing the logs of the Kyverno admission controller container.
+
+ ```shell
+ kubectl logs -n kyverno-system $(kubectl get pod -n kyverno-system -l app.kubernetes.io/component=admission-controller -o jsonpath='{.items[0].metadata.name}') -c kyverno --tail 3
+ ```
+
+ This command should return some output similar to this example:
+
+ ```log
+ I0112 17:47:04.425863 1 mutation.go:113] webhooks/resource/mutate "msg"="mutation rules from policy applied successfully" "clusterroles"=["cert-manager-controller-approve:cert-manager-io","cert-manager-controller-certificates","cert-manager-controller-certificatesigningrequests","cert-manager-controller-challenges","cert-manager-controller-clusterissuers","cert-manager-controller-ingress-shim","cert-manager-controller-issuers","cert-manager-controller-orders","system:basic-user","system:discovery","system:public-info-viewer","system:service-account-issuer-discovery"] "gvk"={"group":"cert-manager.io","version":"v1","kind":"Certificate"} "gvr"={"group":"cert-manager.io","version":"v1","resource":"certificates"} "kind"="Certificate" "name"="defaults-example-certificate-tls" "namespace"="default" "operation"="UPDATE" "policy"="mutate-certificates" "resource.gvk"={"Group":"cert-manager.io","Version":"v1","Kind":"Certificate"} "roles"=["kube-system:cert-manager:leaderelection"] "rules"=["set-revisionHistoryLimit","set-privateKey-rotationPolicy","set-privateKey-details"] "uid"="6f93bd8d-29ca-4eab-8e96-065ea82a1bf2" "user"={"username":"system:serviceaccount:cert-manager:cert-manager","uid":"21cbad67-9d2e-44ee-bb02-7fef9aa2e502","groups":["system:serviceaccounts","system:serviceaccounts:cert-manager","system:authenticated"],"extra":{"authentication.kubernetes.io/pod-name":["cert-manager-648cd49b44-z6g8s"],"authentication.kubernetes.io/pod-uid":["4bd741fa-a8ec-48a1-82d5-26c5b7acce5e"]}}
+ I0112 17:47:04.458402 1 mutation.go:113] webhooks/resource/mutate "msg"="mutation rules from policy applied successfully" "clusterroles"=["cert-manager-controller-approve:cert-manager-io","cert-manager-controller-certificates","cert-manager-controller-certificatesigningrequests","cert-manager-controller-challenges","cert-manager-controller-clusterissuers","cert-manager-controller-ingress-shim","cert-manager-controller-issuers","cert-manager-controller-orders","system:basic-user","system:discovery","system:public-info-viewer","system:service-account-issuer-discovery"] "gvk"={"group":"cert-manager.io","version":"v1","kind":"Certificate"} "gvr"={"group":"cert-manager.io","version":"v1","resource":"certificates"} "kind"="Certificate" "name"="defaults-example-certificate-tls" "namespace"="default" "operation"="UPDATE" "policy"="mutate-certificates" "resource.gvk"={"Group":"cert-manager.io","Version":"v1","Kind":"Certificate"} "roles"=["kube-system:cert-manager:leaderelection"] "rules"=["set-revisionHistoryLimit","set-privateKey-rotationPolicy","set-privateKey-details"] "uid"="ec61a3c9-df0a-4daf-8bc3-227dc80348a9" "user"={"username":"system:serviceaccount:cert-manager:cert-manager","uid":"21cbad67-9d2e-44ee-bb02-7fef9aa2e502","groups":["system:serviceaccounts","system:serviceaccounts:cert-manager","system:authenticated"],"extra":{"authentication.kubernetes.io/pod-name":["cert-manager-648cd49b44-z6g8s"],"authentication.kubernetes.io/pod-uid":["4bd741fa-a8ec-48a1-82d5-26c5b7acce5e"]}}
+ I0112 17:47:09.477776 1 mutation.go:113] webhooks/resource/mutate "msg"="mutation rules from policy applied successfully" "clusterroles"=["cert-manager-controller-approve:cert-manager-io","cert-manager-controller-certificates","cert-manager-controller-certificatesigningrequests","cert-manager-controller-challenges","cert-manager-controller-clusterissuers","cert-manager-controller-ingress-shim","cert-manager-controller-issuers","cert-manager-controller-orders","system:basic-user","system:discovery","system:public-info-viewer","system:service-account-issuer-discovery"] "gvk"={"group":"cert-manager.io","version":"v1","kind":"Certificate"} "gvr"={"group":"cert-manager.io","version":"v1","resource":"certificates"} "kind"="Certificate" "name"="defaults-example-certificate-tls" "namespace"="default" "operation"="UPDATE" "policy"="mutate-certificates" "resource.gvk"={"Group":"cert-manager.io","Version":"v1","Kind":"Certificate"} "roles"=["kube-system:cert-manager:leaderelection"] "rules"=["set-revisionHistoryLimit","set-privateKey-rotationPolicy","set-privateKey-details"] "uid"="c4384662-cb2a-49a0-8e83-e590942ec48d" "user"={"username":"system:serviceaccount:cert-manager:cert-manager","uid":"21cbad67-9d2e-44ee-bb02-7fef9aa2e502","groups":["system:serviceaccounts","system:serviceaccounts:cert-manager","system:authenticated"],"extra":{"authentication.kubernetes.io/pod-name":["cert-manager-648cd49b44-z6g8s"],"authentication.kubernetes.io/pod-uid":["4bd741fa-a8ec-48a1-82d5-26c5b7acce5e"]}}
+ ```
+
+ Taking the last line as an example you can pull out:
+
+ ```log
+ "kind"="Certificate" "name"="defaults-example-certificate-tls" "namespace"="default" "operation"="UPDATE" "policy"="mutate-certificates" "resource.gvk"={"Group":"cert-manager.io","Version":"v1","Kind":"Certificate"} "roles"=["kube-system:cert-manager:leaderelection"] "rules"=["set-revisionHistoryLimit","set-privateKey-rotationPolicy","set-privateKey-details"]
+ ```
+
+ See the `policy` key indicates that our policy has been applied.
+ In the `rules` section you can identify that three of our five rules have been applied to the generated "defaults-example-certificate-tls" `Certificate` resource.
+
+When using an `Ingress` resource, you always need to specify the `secretName` from which to load the certificate.
+No defaulting is required in this use case because this is a required part of the `Ingress` specification.
+
+The only additional YAML that a user is required to specify on the `Ingress` resource is the annotation:
+
+```yaml
+cert-manager.io/cluster-issuer: "our-corp-issuer"
+```
+
+This annotation serves as both the trigger for cert-manager to act upon this `Ingress` and also as the configuration value for the `Certificate.spec.issuerRef` fields.
+This single line replaces the need for the user to create a `Certificate` resource entirely.
+This results in a reduction of the total YAML required to secure the application behind this `Ingress`.
+
+# Summary
+
+This is a fairly simple example of how easy it can be to setup *defaults* for your cluster `Certificate` resources.
+We've shown how a `ClusterPolicy` doesn't have to "enforce" settings, rather it can be used to set and extend the default options.
+`Certificate` users can reduce their YAML, whilst maintaining the flexibility to override any value when needed.
+
+We have shown how a simple `ClusterPolicy` with only 5 rules can change the user experience creating `Certificate` resources from:
+
+```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml
+```
+đ `cert-test-revision-override.yaml`
+
+To instead only need to specify the configuration important to them, for example:
+
+```yaml file=../../../../public/docs/tutorials/certificate-defaults/cert-test-minimal.yaml
+```
+đ `cert-test-minimal.yaml`
+
+With this policy we achieved our objective and have enabled users to submit minimal `Certifiate` resources.
+This completes our fifth [use case](#use-cases), with only a single field contained within the specification, the `dnsNames` entry.
+Every other specified field was automatically defaulted using Kyverno with `ClusterPolicy` which would typically be setup by a platform administrator.
+
+# Cleanup
+
+If you created the kind cluster for this tutorial you can simply run:
+
+```shell
+kind delete cluster --name defaults
+```
+
+Otherwise to remove all resources deployed in this tutorial:
+
+```shell
+# Assuming you are running from this directly or saved all the files to yamls/
+kubectl delete -f ingress.yaml
+kubectl delete -f cpol-mutate-certificates-1.yaml
+helm uninstall kyverno -n kyverno-system
+helm uninstall cert-manager -n cert-manager
+helm uninstall ingress-nginx -n ingress-nginx
+```
+
+# Appendix
+
+## cert-manager version requirement
+
+The behavior of cert-manager's mutating webhook has been changed from v1.14.x onward.
+For a more complete explanation and details of the change please refer to [PR #6311](https://github.com/cert-manager/cert-manager/pull/6311).
+Instructions for a manual fix can be found [in this comment on PR #6311](https://github.com/cert-manager/cert-manager/pull/6311#issuecomment-1889517418).
+
+## Presets Feature Request
+
+For further background reading around setting "defaults" or "presets", you can refer to [issue 2239](ttps://github.com/cert-manager/cert-manager/issues/2239).
+This tutorial came out of an investigation of that issue.
+
+The cert-manager team reasoned that the requested solution could be achieved with the use of other, more generic open-source policy tools.
+Kyverno is just one example and similar can be achieved with [Gatekeeper](https://github.com/open-policy-agent/gatekeeper) as an alternative tool.
diff --git a/public/docs/tutorials/certificate-defaults/cert-test-minimal.yaml b/public/docs/tutorials/certificate-defaults/cert-test-minimal.yaml
new file mode 100644
index 00000000000..813e070a71a
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/cert-test-minimal.yaml
@@ -0,0 +1,8 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: test-minimal
+ namespace: default
+spec:
+ dnsNames:
+ - example.com
diff --git a/public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml b/public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml
new file mode 100644
index 00000000000..f901491306a
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/cert-test-revision-override.yaml
@@ -0,0 +1,19 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: test-revision-override
+ namespace: default
+spec:
+ dnsNames:
+ - example.com
+ issuerRef:
+ group: cert-manager.io
+ kind: ClusterIssuer
+ name: not-my-corp-issuer
+ privateKey:
+ algorithm: RSA
+ encoding: PKCS8
+ rotationPolicy: Never
+ size: 4096
+ revisionHistoryLimit: 44
+ secretName: test-revision-override-cert
diff --git a/public/docs/tutorials/certificate-defaults/cert-test-revision.yaml b/public/docs/tutorials/certificate-defaults/cert-test-revision.yaml
new file mode 100644
index 00000000000..f1f7169169f
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/cert-test-revision.yaml
@@ -0,0 +1,13 @@
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: test-revision
+ namespace: default
+spec:
+ dnsNames:
+ - example.com
+ issuerRef:
+ group: cert-manager.io
+ kind: ClusterIssuer
+ name: not-my-corp-issuer
+ secretName: test-revision-cert
diff --git a/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-0.yaml b/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-0.yaml
new file mode 100644
index 00000000000..82e107082d8
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-0.yaml
@@ -0,0 +1,45 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+ name: mutate-certificates
+spec:
+ failurePolicy: Fail
+ rules:
+ # Set a sane default for the history field if not already present
+ - name: set-revisionHistoryLimit
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ # +(...) This is the clever syntax for if not already set
+ +(revisionHistoryLimit): 2
+ # Set rotation to always if not already set
+ - name: set-privateKey-rotationPolicy
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ privateKey:
+ +(rotationPolicy): Always
+ # Set private key details for algorithm and size
+ - name: set-privateKey-details
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ privateKey:
+ +(algorithm): ECDSA
+ +(size): 521
+ +(encoding): PKCS1
diff --git a/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-1.yaml b/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-1.yaml
new file mode 100644
index 00000000000..9ad220a7f4d
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/cpol-mutate-certificates-1.yaml
@@ -0,0 +1,72 @@
+apiVersion: kyverno.io/v1
+kind: ClusterPolicy
+metadata:
+ name: mutate-certificates
+spec:
+ failurePolicy: Fail
+ rules:
+ # Set a sane default for the history field if not already present
+ - name: set-revisionHistoryLimit
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ # +(...) This is the clever syntax for if not already set
+ +(revisionHistoryLimit): 2
+ # Set rotation to always if not already set
+ - name: set-privateKey-rotationPolicy
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ privateKey:
+ +(rotationPolicy): Always
+ # Set private key details for algorithm and size
+ - name: set-privateKey-details
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ privateKey:
+ +(algorithm): ECDSA
+ +(size): 521
+ +(encoding): PKCS1
+ # Set a secretName when one is not provided
+ - name: set-default-secret-name
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ # You can read more about this syntax in the Kyverno documentation:
+ # https://kyverno.io/docs/writing-policies/variables/#variables-from-admission-review-requests
+ +(secretName): "{{request.object.metadata.name}}-cert"
+ # Set a default for issuerRef fields
+ - name: set-default-issuer-ref
+ match:
+ any:
+ - resources:
+ kinds:
+ - Certificate
+ mutate:
+ patchStrategicMerge:
+ spec:
+ +(issuerRef):
+ name: our-corp-issuer
+ kind: ClusterIssuer
+ group: cert-manager.io
diff --git a/public/docs/tutorials/certificate-defaults/ingress.yaml b/public/docs/tutorials/certificate-defaults/ingress.yaml
new file mode 100644
index 00000000000..d1284808992
--- /dev/null
+++ b/public/docs/tutorials/certificate-defaults/ingress.yaml
@@ -0,0 +1,24 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ annotations:
+ cert-manager.io/cluster-issuer: "our-corp-issuer"
+ name: defaults-example
+ namespace: default
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: app.example.com
+ http:
+ paths:
+ - backend:
+ service:
+ name: app
+ port:
+ number: 80
+ path: /
+ pathType: ImplementationSpecific
+ tls:
+ - hosts:
+ - app.example.com
+ secretName: defaults-example-certificate-tls