How to Install a Wildcard Let's Encrypt SSL Certificate on Vultr Kubernetes Engine
Introduction
Let's Encrypt is an automated, open certificate authority that offers free TLS/SSL certificates for public benefit. The service is provided by the Internet Security Research Group (ISRG). You can generate a wildcard SSL certificate to secure unlimited subdomains that belong to the same domain name. For example, a wildcard SSL certificate generated for example.com can secure www.example.com, app.example.com, test.example.com, and so on.
This article demonstrates the steps to install the required plugins, generate a wildcard SSL certificate and secure a sample deployment using the Nginx Ingress controller and the generated certificate.
Prerequisites
Before you begin, you should:
- Deploy a Vultr Kubernetes Engine cluster.
- Create a DNS zone for your domain name. This article uses example.com for demonstration.
- Deploy a Ubuntu 22.04 server to use as a management workstation. On this management workstation, you need to:
- Install Kubectl.
- Install Helm.
- Download your VKE configuration.
Install the ExternalDNS Add-On
The ExternalDNS add-on allows the Kubernetes cluster to manage the Vultr DNS zones and synchronize the exposed services/ingresses with the DNS records. You need to install this add-on as Let's Encrypt requires you to prove the full ownership of the domain name. In this case, the ACME client, cert-manager
verifies the ownership by adding a special DNS record in the domain name.
Create a new manifest file named 1-dns.yaml
.
# nano 1-dns.yaml
Add the following contents to the file.
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns:v0.12.2
args:
- --source=ingress # service also possible
- --provider=vultr
env:
- name: VULTR_API_KEY
value: "" # Populate with your API key.
The above manifest deploys the ExternalDNS add-on on the Kubernetes cluster. Be sure to populate the VULTR_API_KEY
value with your API key. You can locate the API key by navigating to the Vultr Customer Portal > Account > API.
Apply the manifest file.
# kubectl apply -f 01-dns.yaml
Install the Nginx Ingress Controller
The nginx-ingress
controller is a Kubernetes resource that provisions a load balancer and configures it to expose the ingress resources outside the cluster. It is a cluster-wise resource that you can use to expose multiple services using ingress resources.
Install the nginx-ingress
controller.
# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.5.1/deploy/static/provider/cloud/deploy.yaml
Verify the installation.
# kubectl get services/ingress-nginx-controller -n ingress-nginx
Install the cert-manager Plugin
The cert-manager
plugin adds the certificates and certificate issuers as resource types in the Kubernetes cluster. It simplifies the process of obtaining, renewing, and using the certificates.
Install the cert-manager
plugin.
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.1/cert-manager.yaml
Verify the installation.
# kubectl get pods --namespace cert-manager
Install the Vultr Webhook for the cert-manager Plugin
The Vultr Webhook allows using the ACME DNS01 solver by Vultr with the cert-manager
plugin to issue the Let's Encrypt certificates. You must perform this step after installing the cert-manager
plugin on the cluster.
Clone the GitHub repository.
# git clone https://github.com/vultr/cert-manager-webhook-vultr.git
Create a secret resource.
# kubectl create secret generic "vultr-credentials" --from-literal=apiKey=<VULTR API KEY> --namespace=cert-manager
The above command creates a secret resource for defining the Vultr credentials. Replace <VULTR API KEY>
with your API key.
Install the webhook.
# cd cert-manager-webhook-vultr
# helm install --namespace cert-manager cert-manager-webhook-vultr ./deploy/cert-manager-webhook-vultr
Create a new manifest file named 2-webhook.yaml
.
$ nano 2-webhook.yaml
Add the following contents to the file.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: "" # Populate with your email address.
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- dns01:
webhook:
groupName: acme.vultr.com
solverName: vultr
config:
apiKeySecretRef:
key: apiKey
name: vultr-credentials
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cert-manager-webhook-vultr:secret-reader
namespace: cert-manager
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["vultr-credentials"]
verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cert-manager-webhook-vultr:secret-reader
namespace: cert-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cert-manager-webhook-vultr:secret-reader
subjects:
- apiGroup: ""
kind: ServiceAccount
name: cert-manager-webhook-vultr
The above manifest creates the ClusterIssuer
resource for issuing Let's Encrypt certificates. It also defines the necessary role bindings for the webhook to function properly. Populate the spec.acme.email
field with your email address.
Apply the manifest file.
# kubectl apply -f 02-webhook.yaml
Verify the installation.
# kubectl get clusterissuer
Create a Wildcard Certificate
This section demonstrates the steps to create a Certificate
resource which requests the Let's Encrypt CA to issue a wildcard SSL certificate for *.example.com
and store it in a secret resource named wildcard-tls
.
Create a new manifest file named 3-certificate.yaml
.
# nano 3-certificate.yaml
Add the following contents to the file.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard
spec:
dnsNames:
- "*.example.com" # Populate with the domain name followed by an asterisk symbol.
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
secretName: wildcard-tls
The above manifest creates a Certificate
resource for *.example.com that you use in the later steps to secure a sample Nginx deployment.
Apply the manifest file.
# kubectl apply -f 3-certificate.yaml
Create an Nginx Deployment
You create a sample Deployment
resource in this section using the official Nginx image. You can swap this step with the deployment of your existing web application and change the name of the service in the next section.
Create a new manifest file named 4-nginx.yaml
.
# nano 4-nginx.yaml
Add the following contents to the file.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Service
apiVersion: v1
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- port: 80
The above manifest creates a Deployment
resource to run a sample Nginx container and a Service
resource to expose the connections inside the cluster.
Apply the manifest file.
# kubectl apply -f 4-nginx.yaml
Create an Ingress Resource
In the previous steps, you installed the nginx-ingress
controller to expose Kubernetes services outside the cluster. In this section, you create an ingress resource to expose the nginx-svc
service outside the cluster bound with the test.example.com
subdomain and secure it with the wildcard SSL certificate.
Create a new manifest file named 5-ingress.yaml
.
# nano 5-ingress.yaml
Add the following contents to the file.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
external-dns.alpha.kubernetes.io/hostname: "test.example.com" # Populate with the subdomain.
nginx.ingress.kubernetes.io/ssl-redirect: "false"
certmanager.k8s.io/issuer: "letsencrypt-prod"
certmanager.k8s.io/acme-challenge-type: dns01
certmanager.k8s.io/acme-dns01-provider: vultr
name: nginx-ingress
spec:
rules:
- host: "test.example.com" # Populate with the subdomain.
http:
paths:
- backend:
service:
name: nginx-svc
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- "test.example.com" # Populate with the subdomain.
secretName: wildcard-tls
The above manifest creates an Ingress
resource to expose the nginx-svc
outside the cluster and secure it with the wildcard certificate stored in the wildcard-tls
secret resource.
Apply the manifest file.
# kubectl apply -f 5-ingress.yaml
You can verify the deployment by opening test.example.com
on your web browser a few minutes after applying the manifest file.
Conclusion
This article demonstrated the steps to install the required plugins, generate a wildcard SSL certificate and secure a sample deployment using the Nginx Ingress controller and the generated certificate. You can follow these steps to generate a wildcard SSL certificate for your domain name and secure your Kubernetes deployments.