title: Manage Certificates With Cert Manager

This tutorial will detail how to manage secrets of ApisixTls using cert-manager.

Prerequisites

In this guide, we assume that your APISIX is installed with ssl enabled, which is not enabled by default in the Helm Chart. To enable it, you need to set gateway.tls.enabled=true during installation.

For example, you could install APISIX and APISIX ingress controller by running:

helm install apisix apisix/apisix --set gateway.type=NodePort --set ingress-controller.enabled=true --set gateway.tls.enabled=true --set ingress-controller.config.apisix.serviceNamespace=default

Assume that the SSL port is 9443.

Create Issuer

For testing purposes, we will use a simple CA issuer. All required files can be found here.

To create a CA issuer, use the following commands:

kubectl apply -f ./cert-manager/ca.yaml
kubectl apply -f ./cert-manager/issuer.yaml

If the cert-manager is working correctly, we should be able to see the Ready status by running:

kubectl get issuer

It should output:

NAME        READY   AGE
ca-issuer   True    50s

Create Certificate

Before creating ApisixTls, we should create a Certificate resource.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: demo-cert
spec:
  dnsNames:
    - local.httpbin.org
  issuerRef:
    kind: Issuer
    name: ca-issuer
  secretName: example-cert
  usages:
    - digital signature
    - key encipherment
  renewBefore: 0h55m0s
  duration: 1h0m0s

Note that we set the parameters duration and renewBefore. We want to test if the certificate rotation functionality is working well, so a shorter renewal time will help.

Like Issuer, we could see its readiness status by running:

kubectl get certificate

It should output:

NAME        READY   SECRET        AGE
demo-cert   True    example-cert  50s

Check the secrets by running:

kubectl get secret

It should output:

NAME          TYPE                DATA   AGE
example-cert  kubernetes.io/tls   3      2m20s

This means that our cert-manager is working properly.

Create Test Service

We use kennethreitz/httpbin as the service image.

Deploy it by running:

kubectl run httpbin --image kennethreitz/httpbin --expose --port 80

Route the Service

Create an ApisixRoute to route the service:

apiVersion: apisix.apache.org/v2beta1
kind: ApisixRoute
metadata:
  name: httpserver-route
spec:
  http:
    - name: httpbin
      match:
        hosts:
          - local.httpbin.org
        paths:
          - "/*"
      backend:
        serviceName: httpbin
        servicePort: 80

Run curl command in a APISIX pod to see if the routing configuration works.

kubectl -n <APISIX_NAMESPACE> exec -it <APISIX_POD_NAME> -- curl http://127.0.0.1:9080/ip -H 'Host: local.httpbin.org'

It should output:

{
  "origin": "127.0.0.1"
}

Secure the Route

Create an ApisixTls to secure the route, referring to the secret created by cert-manager:

apiVersion: apisix.apache.org/v1
kind: ApisixTls
metadata:
  name: example-tls
spec:
  hosts:
    - local.httpbin.org
  secret:
    name: example-cert # the secret created by cert-manager
    namespace: default # secret namespace

Run curl command in a APISIX pod to see if the Ingress and TLS configuration are working.

kubectl -n <APISIX_NAMESPACE> exec -it <APISIX_POD_NAME> -- curl --resolve 'local.httpbin.org:9443:127.0.0.1' "https://local.httpbin.org:9443/ip" -k

It should output:

{
  "origin": "127.0.0.1"
}

Test Certificate Rotation

To verify certificate rotation, we can add a verbose parameter -v to curl command:

kubectl -n <APISIX_NAMESPACE> exec -it <APISIX_POD_NAME> -- curl --resolve 'local.httpbin.org:9443:127.0.0.1' "https://local.httpbin.org:9443/ip" -k -v

The verbose option will show us the handshake log, which also contains the certificate information.

Example output:

* Added local.httpbin.org:9443:127.0.0.1 to DNS cache
* Hostname local.httpbin.org was found in DNS cache
*   Trying 127.0.0.1:9443...
* Connected to local.httpbin.org (127.0.0.1) port 9443 (#0)
...
...
* Server certificate:
*  subject: [NONE]
*  start date: Sep 16 00:14:55 2021 GMT
*  expire date: Sep 16 01:14:55 2021 GMT
*  issuer: C=CN; ST=Zhejiang; L=Hangzhou; O=APISIX-Test-CA_; OU=APISIX_CA_ROOT_; CN=APISIX.ROOT_; emailAddress=test@test.com

We could see the start date and expiration date of the server certificate.

Since the Certificate we defined requires the cert-manager to renew the cert every 5 minutes, we should be able to see the changes to the server certificate after 5 minutes.

* Added local.httpbin.org:9443:127.0.0.1 to DNS cache
* Hostname local.httpbin.org was found in DNS cache
*   Trying 127.0.0.1:9443...
* Connected to local.httpbin.org (127.0.0.1) port 9443 (#0)
...
...
* Server certificate:
*  subject: [NONE]
*  start date: Sep 16 00:19:55 2021 GMT
*  expire date: Sep 16 01:19:55 2021 GMT
*  issuer: C=CN; ST=Zhejiang; L=Hangzhou; O=APISIX-Test-CA_; OU=APISIX_CA_ROOT_; CN=APISIX.ROOT_; emailAddress=test@test.com

The certificate was rotated as expected.