This guide will walk through steps to setup Kong Ingress Controller with cert-manager to automate certificate management using Let's Encrypt. Any ACME-based CA can be used in-place of Let's Encrypt as well.
You will need the following:
- Kubernetes cluster that can provision an IP address that is routable from the Internet. If you don't have one, you can use GKE or any managed k8s cloud offering.
- A domain name for which you control the DNS records.
This is necessary so that
Let's Encrypt can verify the ownership of the domain and issue a certificate.
In the current guide, we use
yolo42.com
, please replace this with a domain you control.
This tutorial was written using Google Kubernetes Engine.
Execute the following to install the Ingress Controller:
$ kubectl create -f https://bit.ly/k4k8s
namespace/kong created
customresourcedefinition.apiextensions.k8s.io/kongplugins.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongconsumers.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongcredentials.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongingresses.configuration.konghq.com created
serviceaccount/kong-serviceaccount created
clusterrole.rbac.authorization.k8s.io/kong-ingress-clusterrole created
clusterrolebinding.rbac.authorization.k8s.io/kong-ingress-clusterrole-nisa-binding created
configmap/kong-server-blocks created
service/kong-proxy created
service/kong-validation-webhook created
deployment.extensions/kong created
Please follow cert-manager's documentation on how to install cert-manager onto your cluster.
Once installed, verify all the components are running using:
kubectl get all -n cert-manager
NAME READY STATUS RESTARTS AGE
pod/cert-manager-86478c5ff-mkhb9 1/1 Running 0 23m
pod/cert-manager-cainjector-65dbccb8b6-6dnjl 1/1 Running 0 23m
pod/cert-manager-webhook-78f9d55fdf-5wcnp 1/1 Running 0 23m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cert-manager-webhook ClusterIP 10.63.240.251 <none> 443/TCP 23m
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/cert-manager 1 1 1 1 23m
deployment.apps/cert-manager-cainjector 1 1 1 1 23m
deployment.apps/cert-manager-webhook 1 1 1 1 23m
NAME DESIRED CURRENT READY AGE
replicaset.apps/cert-manager-86478c5ff 1 1 1 23m
replicaset.apps/cert-manager-cainjector-65dbccb8b6 1 1 1 23m
replicaset.apps/cert-manager-webhook-78f9d55fdf 1 1 1 23m
Any HTTP-based application can be used, for the purpose of the demo, install the following echo server:
$ kubectl apply -f https://bit.ly/echo-service
service/echo created
deployment.apps/echo created
Get the IP address of the load balancer for Kong:
$ kubectl get service -n kong kong-proxy
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kong-proxy LoadBalancer 10.63.250.199 35.233.170.67 80:31929/TCP,443:31408/TCP 58d
To get only the IP address:
$ kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}" service -n kong kong-proxy
35.233.170.67
Please note that the IP address in your case will be different.
Next, setup a DNS records to resolve proxy.yolo42.com
to the
above IP address:
$ dig +short proxy.yolo42.com
35.233.170.67
Next, setup a CNAME DNS record to resolve demo.yolo42.com
to
proxy.yolo42.com
.
$ dig +short demo.yolo2.com
proxy.yolo42.com.
35.233.170.67
Setup an Ingress rule to expose the application:
$ echo "
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-yolo42-com
annotations:
kubernetes.io/ingress.class: kong
spec:
rules:
- host: demo.yolo42.com
http:
paths:
- path: /
backend:
serviceName: echo
servicePort: 80
" | kubectl apply -f -
ingress.extensions/demo-yolo42-com created
Note: The apiVersion of the Ingress uses networking.k8s.io/v1beta1 instead of the deprecated Ingress in extensions/v1beta1. The networking.k8s.io/v1 for Ingress may also be available, depending on the Kubernetes version used.
Access your application:
$ curl -I demo.yolo42.com
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Connection: keep-alive
Date: Fri, 21 Jun 2019 21:14:45 GMT
Server: echoserver
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 1
Via: kong/1.1.2
First, setup a ClusterIssuer for cert-manager
$ echo "apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
namespace: cert-manager
spec:
acme:
email: [email protected] #please change this
privateKeySecretRef:
name: letsencrypt-prod
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- http01:
ingress:
class: kong" | kubectl apply -f -
clusterissuer.cert-manager.io/letsencrypt-prod configured
Things to note:
- If you run into issues configuring this,
be sure that the group (
cert-manager.io
) and version (v1
) match those in the output ofkubectl describe crd clusterissuer
. This directs cert-manager which CA authority to use to issue the certificate. - The apiVersion for the cert-manager has been updated in later versions of the cert-manager to v1. Make sure to check the version installed in order to determine which version you should use.
- From version 0.10.0 of the Kong kubernetes-ingress-controller, Ingresses requires the ingress class annotation by default. For the cert-manager to be able to create a working Ingress to support the ACME DNS Challenge, the example adds a "class: kong" for the http01 ingress configuration (compared to previous "ingress: {}" configuration).
Next, update your Ingress resource to provision a certificate and then use it:
$ echo '
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-yolo42-com
annotations:
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: letsencrypt-prod
kubernetes.io/ingress.class: kong
spec:
tls:
- secretName: demo-yolo42-com
hosts:
- demo.yolo42.com
rules:
- host: demo.yolo42.com
http:
paths:
- path: /
backend:
serviceName: echo
servicePort: 80
' | kubectl apply -f -
ingress.extensions/demo-yolo42-com configured
Things to note here:
- The annotation
kubernetes.io/tls-acme
is set totrue
, informing cert-manager that it should provision a certificate for hosts in this Ingress using ACME protocol. certmanager.k8s.io/cluster-issuer
is set toletsencrypt-prod
, directing cert-manager to use Let's Encrypt's production server to provision a TLS certificate.tls
section of the Ingress directs Kong Ingress Controller to use the secretdemo-yolo42-com
to encrypt the traffic fordemo.yolo42.com
. This secret will be created by cert-manager.
Once you update the Ingress resource, cert-manager will start provisioning the certificate and in sometime the certificate will be available for use.
You can track the progress of certificate issuance:
$ kubectl describe certificate demo-example-com
Name: demo-example-com
Namespace: default
Labels: <none>
Annotations: <none>
API Version: certmanager.k8s.io/v1
Kind: Certificate
Metadata:
Creation Timestamp: 2019-06-21T20:41:54Z
Generation: 1
Owner References:
API Version: extensions/v1beta1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: demo-example-com
UID: 261d15d3-9464-11e9-9965-42010a8a01ad
Resource Version: 19561898
Self Link: /apis/certmanager.k8s.io/v1/namespaces/default/certificates/demo-example-com
UID: 014d3f1d-9465-11e9-9965-42010a8a01ad
Spec:
Acme:
Config:
Domains:
demo.yolo42.com
Http 01:
Dns Names:
demo.yolo42.com
Issuer Ref:
Kind: ClusterIssuer
Name: letsencrypt-prod
Secret Name: demo-example-com
Status:
Conditions:
Last Transition Time: 2019-06-21T20:42:20Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2019-09-19T19:42:19Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 53m cert-manager Generated new private key
Normal GenerateSelfSigned 53m cert-manager Generated temporary self signed certificate
Normal OrderCreated 53m cert-manager Created Order resource "demo-example-com-3811625818"
Normal OrderComplete 53m cert-manager Order "demo-example-com-3811625818" completed successfully
Normal CertIssued 53m cert-manager Certificate issued successfully
Once all is in place, you can use HTTPS:
$ curl -v https://demo.yolo42.com
* Rebuilt URL to: https://demo.yolo42.com/
* Trying 35.233.170.67...
* TCP_NODELAY set
* Connected to demo.yolo42.com (35.233.170.67) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=demo.yolo42.com
* start date: Jun 21 19:42:19 2019 GMT
* expire date: Sep 19 19:42:19 2019 GMT
* subjectAltName: host "demo.yolo42.com" matched cert's "demo.yolo42.com"
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: demo.yolo42.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Date: Fri, 21 Jun 2019 21:37:43 GMT
< Server: echoserver
< X-Kong-Upstream-Latency: 1
< X-Kong-Proxy-Latency: 1
< Via: kong/1.1.2
<
Hostname: echo-d778ffcd8-52ddj
Pod Information:
node name: gke-harry-k8s-dev-default-pool-bb23a167-9w4t
pod name: echo-d778ffcd8-52ddj
pod namespace: default
pod IP:10.60.2.246
Server values:
server_version=nginx: 1.12.2 - lua: 10010
Request Information:
client_address=10.60.2.239
method=GET
real path=/
query=
request_version=1.1
request_scheme=http
request_uri=http://demo.yolo42.com:8080/
Request Headers:
accept=*/*
connection=keep-alive
host=demo.yolo42.com
user-agent=curl/7.54.0
x-forwarded-for=10.138.0.6
x-forwarded-host=demo.yolo42.com
x-forwarded-port=8443
x-forwarded-proto=https
x-real-ip=10.138.0.6
Request Body:
-no body in request-
Et voilà ! You've secured your API with HTTPS with Kong Ingress Controller and cert-manager.