---
title: "Kubernetes cert-manager"
description: "Learn how to automatically provision and manage TLS certificates in Kubernetes using Infisical PKI"
---
## Concept
The Infisical PKI provides [ACME (Automated Certificate Management Environment)](https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment) integration for various of ACME clients to issue and renew certificates automatically.
Under Kubernetes environment, [cert-manager](https://cert-manager.io) is the most popular issuer to issue certificates via ACME.
The issuer is perfect for getting X.509 certificates for ingresses and other Kubernetes resources and capable of automatically renewing certificates as needed.
As part of the workflow, you install `cert-manager` and configure resources to represent the connection details to your Infisical PKI and the certificates you wish to issue.
Each issued certificate and corresponding private key is made available in a Kubernetes secret.
We recommend reading the [cert-manager documentation](https://cert-manager.io/docs/) for a fuller understanding of all the moving parts.
For the ACME part, please reference to the [ACME](https://cert-manager.io/docs/configuration/acme/) section.
## Workflow
A typical workflow for using the Infisical PKI Issuer to issue certificates for your Kubernetes resources consists of the following steps:
1. Creating a certificate profile with ACME as the enrollment method in Infisical.
2. Installing `cert-manager` into your Kubernetes cluster.
3. Creating a Kubernetes secret to store the EAB (External Account Binding) credentials of the ACME certificate profile.
4. Creating an `Issuer` or `ClusterIssuer` resource in your Kubernetes cluster to connect to the Infisical PKI certificate profile you wish to use.
5. Creating a `Certificate` resource in your Kubernetes cluster to represent a certificate you wish to issue. As part of this step, you specify the Kubernetes `Secret` to create and store the issued certificate and private key.
6. Consuming the issued certificate across your Kubernetes resources from the specified Kubernetes `Secret`.
## Guide
In the following steps, we explore how to install the Infisical PKI Issuer using [kubectl](https://github.com/kubernetes/kubectl) and use it to obtain certificates for your Kubernetes resources.
Follow the instructions [here](/documentation/platform/pki/enrollment-methods/acme) to create a certificate profile with ACME as the enrollment method.
By the end of this step, you should have a **ACME Directory URL**, **EAB KID** and **EAB Secret** on hand as part of the credentials for the Infisical PKI ACME server to authenticate with Infisical; this will be useful in steps 4 and 5.
Currently, the Infisical PKI ACME server only supports authenticating with Infisical via the dedicated EAB credentials generated for each certificate profile as the authentication method.
We're planning to add support for [Kubernetes Auth](/documentation/platform/identities/kubernetes-auth) in the near future.
Install `cert-manager` into your Kubernetes cluster by following the instructions [here](https://cert-manager.io/docs/installation/) or by running the following command:
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.3/cert-manager.yaml
```
Start by creating a Kubernetes `Secret` containing the **EAB Secret** from step 1. As mentioned previously, this will be used by the Infisical PKI issuer to authenticate with Infisical.
```bash
kubectl create secret generic infisical-acme-eab-secret \
--namespace \
--from-literal=eabSecret=
```
```yaml acme-eab-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: infisical-acme-eab-secret
namespace:
data:
eabSecret:
```
```bash
kubectl apply -f acme-eab-secret.yaml
```
Next, create the cert-manager Issuer or ClusterIssuer by filling out `acme_server_url`, `your_email`, `acme_eab_kid`, and applying the following configuration file for the `Issuer` resource.
This configuration file specifies the connection details to your Infisical PKI CA to be used for issuing certificates.
```yaml infisical-issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: issuer-infisical
spec:
acme:
# the URL of your Infisical certificate profile with
# ACME enrollment method from step 1
server:
# your email address, any email could work.
# currently we just ignore the value
email:
externalAccountBinding:
keyID: # the EAB secret value from step 1
keySecretRef: # reference to the Secret created in step 3
name: "issuer-infisical-client-secret"
key: "clientSecret"
privateKeySecretRef:
name: issuer-infisical-account-key
solvers:
- http01:
ingress:
# this doesn't need to be nginx, you can use any
# ingressClassName available in your Kubernetes cluster
ingressClassName: nginx
```
```
kubectl apply -f infisical-issuer.yaml
```
You can check that the issuer was created successfully by running the following command:
```bash
kubectl get issuers.cert-manager.io -n -o wide
```
```bash
NAME AGE
issuer-infisical 21h
```
An `Issuer` is a namespaced resource, and it is not possible to issue certificates from an `Issuer` in a different namespace.
This means you will need to create an `Issuer` in each namespace you wish to obtain `Certificates` in.
If you want to create a single `Issuer` that can be consumed in multiple namespaces, you should consider creating a `ClusterIssuer` resource. This is almost identical to the `Issuer` resource, however is non-namespaced so it can be used to issue `Certificates` across all namespaces.
You can read more about the `Issuer` and `ClusterIssuer` resources [here](https://cert-manager.io/docs/configuration/).
Also, currently Infisical ACME server only supports HTTP-01 and requires all the certificate orders passing the challenge before issuing certificates.
We will allow users to opt-out challenge in the near future and also provide support DNS-01 as well.
Finally, create a `Certificate` by applying the following configuration file.
This configuration file specifies the details of the (end-entity/leaf) certificate to be issued.
```yaml certificate-issuer.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate-by-issuer
namespace:
spec:
# the common name for the certificate
commonName: certificate-by-issuer.example.com
# the name of the Kubernetes Secret to create and store
# the certificate and private key in
secretName: certificate-by-issuer
issuerRef:
name: issuer-infisical
group: issuers.cert-manager.io
kind: Issuer
# the algorithm and key size to use
privateKey:
algorithm: ECDSA
size: 256
# the ttl for the certificate
duration: 48h
# the time before the certificate expiry that the certificate should be automatically renewed
renewBefore: 12h
```
The above sample configuration file specifies a certificate to be issued with the common name `certificate-by-issuer.example.com` and ECDSA private key using the P-256 curve, valid for 48 hours; the certificate will be automatically renewed by `cert-manager` 12 hours before expiry.
The certificate is issued by the issuer `issuer-infisical` created in the previous step and the resulting certificate and private key will be stored in a secret named `certificate-by-issuer`.
Note that the full list of the fields supported on the `Certificate` resource can be found in the API reference documentation [here](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec).
You can check that the certificate was created successfully by running the following command:
```bash
kubectl get certificates -n -o wide
```
```bash
NAME READY SECRET ISSUER STATUS AGE
certificate-by-issuer True certificate-by-issuer issuer-infisical Certificate is up to date and has not expired 20h
```
Since the actual certificate and private key are stored in a Kubernetes secret, we can check that the secret was created successfully by running the following command:
```bash
kubectl get secret certificate-by-issuer -n
```
```bash
NAME TYPE DATA AGE
certificate-by-issuer kubernetes.io/tls 2 26h
```
We can `describe` the secret to get more information about it:
```bash
kubectl describe secret certificate-by-issuer -n default
```
```bash
Name: certificate-by-issuer
Namespace: default
Labels: controller.cert-manager.io/fao=true
Annotations: cert-manager.io/alt-names:
cert-manager.io/certificate-name: certificate-by-issuer
cert-manager.io/common-name: certificate-by-issuer.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group: infisical-issuer.infisical.com
cert-manager.io/issuer-kind: Issuer
cert-manager.io/issuer-name: issuer-infisical
cert-manager.io/uri-sans:
Type: kubernetes.io/tls
Data
====
ca.crt: 1306 bytes
tls.crt: 2380 bytes
tls.key: 227 bytes
```
Here, `ca.crt` is the Root CA certificate, `tls.crt` is the requested certificate followed by the certificate chain, and `tls.key` is the private key for the certificate.
We can decode the certificate and print it out using `openssl`:
```bash
kubectl get secret certificate-by-issuer -n default -o jsonpath='{.data.tls\.crt}' | base64 --decode | openssl x509 -text -noout
```
In any case, the certificate is ready to be used as Kubernetes Secret by your Kubernetes resources.
## FAQ
The full list of the fields supported on the `Certificate` resource can be found in the API reference documentation [here](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec).
Currently, not all fields are supported by the Infisical PKI Issuer.
Yes. `cert-manager` will automatically renew certificates according to the `renewBefore` threshold of expiry as
specified in the corresponding `Certificate` resource.
You can read more about the `renewBefore` field [here](https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.CertificateSpec).
If you see log messages similar to:
```
"CertificateRequest has not been approved yet. Ignoring.","controller":"certificaterequest","controllerGroup":"cert-manager.io","controllerKind":"CertificateRequest","CertificateRequest":{"name":"skynet-infisical-rta-rsa2048-1","namespace":"infisical-system"},"namespace":"infisical-system","name":"skynet-infisical-rta-rsa2048-1","reconcileID":"bfb7cad9-d867-45b5-b3a3-0139e731b7a6"}
```
This indicates that the `CertificateRequest` has been created, but `cert-manager` has not yet approved it. This typically occurs because a necessary approver policy is missing. Refer to the documentation above to create an approver policy.