Configuring Approver Policy Enterprise¶
The default cert-manager installation includes an approve-all policy approver. This approver adds an Approved
condition to all the certificate requests that use the built-in cert-manager issuers.
The open-source cert-manager approver-policy project provides an replacement for this approve-all approver. It supports configuring simple policy rules using certificate request policy resources.
The Approver Policy Enterprise approver is the enterprise version of the open-source approver and includes two additional approver plugins. The Venafi plugin can be used to make policy decisions based on the policies defined in your Venafi Control Plane. The Rego plugin adds support for writing custom complex rules using the Rego language.
Certificate request policy resources¶
Multiple certificate request policy resources can be applicable for a single certificate requests. If the certificate requests doesn't conform to at least one of these applicable policies, its approve condition is set to Denied
and no certificate is issued based on the request. If the certificate request conforms to all matching policies, the approve condition is set to Approved
and issuance can continue. However, if there are no policies that match the certificate request, the approver does not set any approve condition. This is so another approver can still set the approve condition, allowing multiple concurrent approvers, each approving only the certificate request that it is responsible for.
A certificate request policy has four sections; allowed
, constraints
, selector
, and plugins
. For more information, see the configuration section of the approver-policy documentation.
Here is a basic certificate request policy:
apiVersion: policy.cert-manager.io/v1alpha1
kind: CertificateRequestPolicy
metadata:
name: my-first-policy
spec:
allowed:
# The commonName field MUST be present and MUST have this value.
commonName:
value: "hello.world"
required: true
# The dnsNames field is optional, but if present the values MUST all match one of these.
dnsNames:
values: ["*.hello.world", "hello.world"]
required: false
constraints:
# The privateKey must be an RSA key of at least 4096 bits.
privateKey:
algorithm: RSA
minSize: 4096
selector:
# Only applies to a CertificateRequest which has the following issuerRef
issuerRef:
name: "my-issuer"
kind: "Issuer"
group: "cert-manager.io"
This policy states that certificate request resources that use the my-issuer
Issuer:
- MUST have
commonName: hello.world
. - MAY also include the
domainNames
field and if present the values must behello.world
(or a sub-domain of that). - MUST use an RSA private key with size at least 4096 bit.
Binding the certificate request policy to certificate request resources¶
Certificate request policy resources are only used for the CertificateRequest resources that match its CertificateRequestPolicy.Selector
section and that are bound via RBAC bindings.
Configuring certificate request policy's selector section¶
The CertificateRequestPolicy.Selector
section can be set to {}
, or contain a IssuerRef
field and/ or a Namespace
field.
A certificate request policy will only be used to approver/ deny a certificate request if both of these fields match the request's configuration, following the rules described here. Furthermore, the certificate request has to be bound to the certificate request policy via RBAC bindings.
Creating RBAC bindings¶
Every certificate request contains the following unchanging identity fields:
username
: the name of the user that created the certificate request (a user account or a Service Account).groups
: the group membership of the user at the moment when it was created.
These identity fields are managed by cert-manager and can't be set by a user or Service Account. The following is an example of a certificate request which was created by the kubernetes-admin
user:
apiGroup: cert-manager.io/v1
Kind: CertificateRequest
metadata:
name: my-request
namespace: default
spec:
...
username: kubernetes-admin
groups:
- system:masters
- system:authenticated
Next is an example of a certificate request which was created by cert-manager for a certificate resource. Notice that the username is that of cert-manager Service Account:
apiVersion: cert-manager.io/v1
kind: CertificateRequest
metadata:
name: my-request-q9nw4
namespace: default
spec:
...
username: system:serviceaccount:cert-manager:cert-manager
groups:
- system:serviceaccounts
- system:serviceaccounts:cert-manager
- system:authenticated
uid: bae0a607-0ef3-496d-bd57-99b5cae28188
...
You use Role
and RoleBinding
resources, and their Cluster equivalents (ClusterRole
s, ClusterRoleBinding
s) to bind policies to users and ServiceAccounts.
All policy Role
s and ClusterRole
s share the same fields except for the name of the Certificate Request Policy the role is targeting, namely:
# These fields do not change.
- apiGroups: ["policy.cert-manager.io"]
resources: ["certificaterequestpolicies"]
verbs: ["use"]
The names of the certificate request policy resources for the Role are defined in the resourceNames
field.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cert-manager-policy:my-first-policy
rules:
- apiGroups: ["policy.cert-manager.io"]
resources: ["certificaterequestpolicies"]
verbs: ["use"]
resourceNames: ["my-first-policy", "add-more-policies-if-you-like"]
In the following example, the policy is bound to the system:authenticated
group. The request therefore matches all users or Service Accounts who create certificate requests.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cert-manager-policy:my-fist-policy
rules:
- apiGroups: ["policy.cert-manager.io"]
resources: ["certificaterequestpolicies"]
verbs: ["use"]
# Name of the CertificateRequestPolicies to be used.
resourceNames: ["my-first-policy"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cert-manager-policy:my-first-policy
roleRef:
# ClusterRole or Role _must_ be bound to a user for the policy to be considered.
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cert-manager-policy:my-first-policy
subjects:
# The users who should be bound to the policies defined.
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
In the case of users creating certificates, the actor who is making the actual certificate request resources is the cert-manager Service Account. For these cases we need to bind to the Service Account instead:
- kind: ServiceAcount
name: cert-manager
namspace: venafi
Note
Change the namespace value if cert-manager has been installed in a different namespace. For example, cert-manager
or openshift-operators
.
Testing bindings¶
You can check whether a user, group, or ServiceAccount is able to use a certificate request policy by using the following kubectl
command.
Tip
Keep this command handy to check your policy is set up as you would expect as you go.
kubectl auth can-i -n $NAMESPACE --as $USER use certificaterequestpolicies/$POLICY_NAME
In this case, you can expect this command to always return OK since the policy is bound to the cluster scope to all authenticated identities.
kubectl auth can-i -n sandbox --as alice use certificaterequestpolicies/my-first-policy
No binding vs denied certificate requests¶
If a certificate request doesn't match any certificate request policy resource, its approved condition remains unset. This is different from a situation in which one or more certificate request policy resources are applicable for a certificate request but one of them denies the request. In the second case an approval condition is still added, but its status is Denied
.
Creating certificates¶
You now have a basic policy in place, so you can observe certificates being approved and denied. To test this, create certificate request resources using local certificate template files via the cmctl CLI tool.
# cert-deny.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: denied-certificate
spec:
commonName: world.hello
dnsNames:
- "world.hello"
- "example.world.hello"
privateKey:
algorithm: RSA
size: 2048
issuerRef:
name: my-issuer
kind: Issuer
group: cert-manager.io
cmctl create certificaterequest denied-certificate --from-certificate-file cert-deny.yaml
Use the following kubectl describe
command to see why the certificate request was denied.
kubectl describe certificaterequest denied-certificate
...
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Denied 14s policy.cert-manager.io No policy approved this request: [my-first-policy: [spec.allowed.commonName.value: Invalid value: "world.hello": hello.world, spec.allowed.dnsNames.values: Invalid value: []string{"world.hello", "example.world.hello"}: *.hello.world, hello.world]]
The following example is for a certificate request which passes the policy created earlier:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: approved-certificate
spec:
commonName: hello.world
dnsNames:
- "hello.world"
- "example.hello.world"
privateKey:
algorithm: RSA
size: 4096
issuerRef:
name: my-issuer
kind: Issuer
group: cert-manager.io
cmctl create certificaterequest approved-certificate --from-certificate-file cert-approve.yaml
kubectl describe certificaterequest approved-certificate
...
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Approved 5s (x13 over 26s) policy.cert-manager.io Approved by CertificateRequestPolicy: "my-first-policy"