Cert-Manager Policy Approver in OpenShift
- - 9 min read
One of the most commonly deployed operators in OpenShift environments is the Cert-Manager Operator. It automates the management of TLS certificates for applications running within the cluster, including their issuance and renewal.
The tool supports a variety of certificate issuers by default, including ACME, Vault, and self-signed certificates. Whenever a certificate is needed, Cert-Manager will automatically create a CertificateRequest resource that contains the details of the certificate. This resource is then processed by the appropriate issuer to generate the actual TLS certificate. The approval process in this case is usually fully automated, meaning that the certificate is issued without any manual intervention.
But what if you want to have more control? What if certificate issuance must follow strict organizational policies, such as requiring a specifc country code or organization name? This is where the CertificateRequestPolicy resource, a resource provided by the Approver Policy, comes into play.
This article walks through configuring the Cert-Manager Approver Policy in OpenShift to enforce granular policies on certificate requests.
Prerequisites
Before you begin, ensure you have the following:
OpenShift 4.16 or higher with cluster-admin access
Cert-Manager Operator installed
The installation of Cert-Manager itself is discussed in the article: Managing Certificates using GitOps approach. |
The Cert-Manager Operator does not currently support the Approver Policy by default. You need to install the Approver Policy manually using a Helm Chart. There is a feature request to include the Approver Policy in the Cert-Manager Operator in the future. |
Adding Approver Policy Chart as a dependency
Source: Cert-Manager Deployment
The above Chart contains the necessary resources to deploy the Cert-Manager itself and Cert-Manager Approver Policy in OpenShift. To deploy the Approver Policy alongside Cert-Manager, add it as a dependency in your Chart.yaml:
[...]
- name: cert-manager-approver-policy
version: v0.19.0
repository: https://charts.jetstack.io
[...]
This is the official Helm Chart for the Cert-Manager Approver Policy tool provided by Jetstack. The version used in this example is v0.19.0, but you can use a newer version if available.
Configuration of the Helm Chart
The initial values.yaml file was extended to:
include the configuration for the Approver Policy Chart
the configuration for a CertificateRequestPolicy object
with some specific modifications to the CertManager resource itself
Disabling Cert-Manager’s Auto-Approver
The first step we need to do is to disable the auto-approver of the Cert-Manager. If this is not done, there will be a race condition between the auto-approver and the Approver Policy, which will lead to unexpected results. These changes are done by:
cert-manager:
certManager:
enable_patch: true (1)
unsupportedConfigOverrides:
controller:
args:
- '--controllers=*,-certificaterequests-approver' (2)
1 | This enables the patching of the CertManager resource, which tells the chart to overwrite (patch) the automatically generated CertManager resource with the custom configuration. |
2 | This disables the auto-approver Controller of the Cert-Manager. |
As the name suggests, this is currently an unsupported configuration, but it is necessary to disable the auto-approver for the Cert-Manager. In the future versions of the Cert-Manager, this might change, and the auto-approver might be supported out of the box. |
In my case, the full CertManager resource looks like this:
apiVersion: operator.openshift.io/v1alpha1
kind: CertManager
metadata:
annotations:
name: cluster
spec:
controllerConfig:
overrideArgs: (1)
- '--dns01-recursive-nameservers-only'
- '--dns01-recursive-nameservers=ns-362.awsdns-45.com:53,ns-930.awsdns-52.net:53'
logLevel: Normal
managementState: Managed
operatorLogLevel: Normal
unsupportedConfigOverrides: (2)
controller:
args:
- '--controllers=*,-certificaterequests-approver'
1 | Settings to support AWS Nameservers for DNS01 challenges. |
2 | This disables the auto-approver Controller of the Cert-Manager. |
Configuration of the Approver Policy
The second step is to configure the Approver Policy chart. This chart will deploy the necessary resources, most importantly a Deployment that will start the Pods which will process the CertificateRequestPolicy resources later on.
My configuration for that chart looks like this (some default values are omitted for brevity):
cert-manager-approver-policy:
crds: (1)
# This option decides if the CRDs should be installed
# as part of the Helm installation.
enabled: true
# This option makes it so that the "helm.sh/resource-policy": keep
# annotation is added to the CRD. This will prevent Helm from uninstalling
# the CRD when the Helm release is uninstalled.
# WARNING: when the CRDs are removed, all cert-manager-approver-policy custom resources
# (CertificateRequestPolicy) will be removed too by the garbage collector.
keep: true
# Number of replicas of approver-policy to run.
replicaCount: 1 (2)
image: (3)
# Target image repository.
repository: quay.io/jetstack/cert-manager-approver-policy
# Kubernetes imagePullPolicy on Deployment.
pullPolicy: IfNotPresent
tag: v0.19.0
app:
# List of signer names that approver-policy will be given permission to
# approve and deny. CertificateRequests referencing these signer names can be
# processed by approver-policy. Defaults to an empty array, allowing approval
# for all signers.
approveSignerNames: (4)
- 'issuers.cert-manager.io/*'
- 'clusterissuers.cert-manager.io/*'
1 | This enables the installation of the CRDs that are required for the Approver Policy. |
2 | The number of replicas of the Approver Policy Deployment. In most cases, one replica is enough. |
3 | The image configuration for the Approver Policy. The image is pulled from the Jetstack Quay.io repository. |
4 | The list of signer names that the Approver Policy will be allowed to approve. In this case, it is configured to allow all issuers and clusterissuers. |
The approveSignerNames are, if configured, an important setting, especially if you want to add custom (cluster)issuers. In such a case, you need to add the name of the custom issuer to this list. Otherwise the Approver Policy will not be able to approve the CertificateRequests for that issuer. |
Creating a CertificateRequestPolicy and Role(Binding)
The final step in our configuration is to define a CertificateRequestPolicy resource that will define the policy for the certificate requests. This resource will be processed by the Approver Policy and will determine if a certificate request is approved or denied based on the defined criteria.
The following example shows a CertificateRequestPolicy that will:
Allow certificate requests with any common name, DNS names, IP addresses, URIs, and email addresses.
Require DNS names to be set.
Require the subject to contain a specific organization (MyOrganization) and country code (AT).
Allow usages for server auth and client auth.
Set constraints for the certificate duration (1h-24h) and private key algorithm (RSA) and size (2048-4096).
Allow all issuers by using an empty selector.
role: cert-manager-policy:global-approver (1)
serviceAccount: cert-manager (2)
cert_manager_Namespace: cert-manager (3)
policies:
- name: my-approver-policy
enabled: true
allowed:
commonName:
required: false
value: "*"
validations: []
dnsNames: (4)
required: true
values:
- "*"
validations: []
ipAddresses:
required: false
values: ["*"]
validations: []
uris:
required: false
values:
- "*"
validations: []
emailAddresses:
required: false
values:
- "*"
validations: []
# isCA: false
subject:
organizations: (5)
required: true
values:
- "MyOrganization"
validations:
- rule: self.matches("MyOrganization")
message: Organization must be MyOrganization
countries:
required: true
values:
- AT
validations:
- rule: self.matches("AT")
message: Country code must be AT
usages:
- "server auth"
- "client auth"
constraints: (6)
minDuration: 1h
maxDuration: 24h
privateKey:
algorithm: RSA
minSize: 2048
maxSize: 4096
selector:
issuerRef: {} (7)
1 | The role that is used to approve the certificate requests. This role must be created in the OpenShift cluster and must have the necessary permissions to approve certificate requests. |
2 | The service account that is used by the Approver Policy to process the certificate requests. This service account must have the necessary permissions to access the CertificateRequest resources. |
3 | The namespace where the Cert-Manager is deployed. This is usually the cert-manager namespace, but you can change it if you have a different namespace. |
4 | The DNS names are required to be set for the certificate request. |
5 | The subject must contain the organization MyOrganization and the country code AT. |
6 | The constraints for the certificate request, such as the minimum and maximum duration, private key algorithm, and size. |
7 | The selector is empty, which means that the policy applies to all issuers. If you want to limit the policy to specific issuers, you can specify the issuerRef here. |
Rendered CertificateRequestPolicy and Role(Binding)
The above configuration will create a CertificateRequestPolicy resource that looks like this:
One important note is about the ClusterRole and ClusterRoleBinding that are created by the Helm Chart. The role looks like the following and is required to allow the Approver Policy to approve certificate requests. This small bit, puzzled me for a while:
rules:
- verbs:
- use
apiGroups:
- policy.cert-manager.io
resources:
- certificaterequestpolicies
resourceNames:
- my-approver-policy
With the above configuration we are good to go. The Helm Chart can be deployed to the OpenShift cluster (for example, using Argo CD), and the CertificateRequestPolicy will be created automatically.
A new Pod is running in the cert-manager namespace:
❯ oc get pods -n cert-manager | grep approver
NAME READY STATUS RESTARTS AGE
cert-manager-approver-policy-xxxxx 1/1 Running 0 XXm (1)
1 | The Pod cert-manager-approver-policy-xxxxx is the Pod that is responsible for processing the CertificateRequestPolicy resources. |
Testing the Policy
Test 1 - Valid Certificate Request
Now it is time to test the policy. We need to create a Certificate and monitor the output of our approval pod.
As a reminder, the policy we created requires the following:
The subject must contain the organization MyOrganization
The subject must contain the country code AT.
The keysize must be at least 2048 bits. (max 4096 bits)
The duration must be between 1 hour and 24 hours.
The usage must be server auth or client auth.
Let’s create this example Certificate in the myproject
namespace:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: test-certificate1
namespace: myproject
spec:
dnsNames:
- test1.apps.ocp.aws.ispworld.at
duration: 24h
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
privateKey:
algorithm: RSA
encoding: PKCS1
rotationPolicy: Always
secretName: test1
subject:
organizations:
- MyOrganization
countries:
- AT
usages:
- server auth
In the log of the Approver Policy Pod, we should see the following output:
time=2025-06-03T16:07:58.656Z level=DEBUG+3 msg="Approved by CertificateRequestPolicy: \"my-approver-policy\"" logger=controller-manager/events type=Normal object="{Kind:CertificateRequest Namespace:myproject Name:test-certificate1-1 [...]}" reason=Approved
This indicates that the CertificateRequest was approved by the Approver Policy. The policy was able to validate the subject, keysize, duration, and usage of the certificate request and approved it accordingly. The certificate has been created successfully in the myproject namespace, and the secret test1 contains the TLS certificate and private key.
❯ oc get secret test1 -n myproject -o yaml
apiVersion: v1
data:
tls.crt: ...
tls.key: ...
kind: Secret
metadata:
labels:
controller.cert-manager.io/fao: "true"
name: test1
namespace: myproject
type: kubernetes.io/tls
Test 2 - Invalid Certificate Request
That was easy, but what happens if we create a CertificateRequest that does not meet the policy requirements? Let’s try to create a Certificate without the required organization or a wrong country code:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: test-certificate2
namespace: myproject
spec:
dnsNames:
- test2.apps.ocp.aws.ispworld.at
duration: 24h
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
privateKey:
algorithm: RSA
encoding: PKCS1
rotationPolicy: Always
secretName: test2
subject: (1)
countries:
- XX
usages:
- server auth
1 | The subject does not contain the required organization MyOrganization and the country code is set to XX, which is not allowed by the policy. |
This will lead to the following error in the log of the Approver Policy Pod:
time=2025-06-03T16:16:02.233Z level=DEBUG+3 msg="No policy approved this request: [my-approver-policy: [spec.allowed.subject.organizations.required: Required value: true, spec.allowed.subject.countries.values: Invalid value: []string{\"XX\"}: AT, spec.allowed.subject.countries.validations[0]: Invalid value: \"XX\": Country code must be AT]]" logger=controller-manager/events type=Warning object="{Kind:CertificateRequest Namespace:myproject Name:test-certificate2-1 ..." reason=Denied
It complains that the subject does not meet the policy requirements and therefore the CertificateRequest was denied.
Ok we have a policy, but whats next?
The above example shows how the Cert-Manager Approver Policy can be configured and deployed, even if it is not yet supported by the Cert-Manager Operator. However, we only scratched the surface of what is possible with the Approver Policy. You can create more complex policies that include additional validations, such as checking the validity of the DNS names, IP addresses, or URIs. You can also create policies that require specific email addresses or organizational units in the subject.
You can even create fine-grained policies that apply to specific issuers or namespaces by using the selector
field in the CertificateRequestPolicy resource. This allows you to create policies that are tailored to your specific requirements and use cases.
The best references can be found here:
Official documentation: Cert-Manager Approver Policy
Example Policies: Cert-Manager Approver Policy Examples
Conclusion
The Cert-Manager Approver Policy is a powerful tool that allows you to implement custom policies for certificate requests in OpenShift. It provides a way to control the issuance of TLS certificates based on specific criteria, such as the subject, key size, duration, and usage of the certificate. While not yet officially supported by the Cert-Manager Operator, it can be easily integrated into your OpenShift environment using a Helm Chart. Future support is currently being discussed, and it is expected that the Approver Policy will be included in the Cert-Manager Operator in the future.
Copyright © 2020 - 2025 Toni Schmidbauer & Thomas Jungbauer