Requirements
- K3s cluster or stand alone node
- Kubectl installed on your machine and configured to connect to your node / cluster
- A public domain within cloudflare
- Helm installed on your machine
Setting up
Ensure you have cert manager helm repo installed
1
| helm repo add jetstack https://charts.jetstack.io
|
Create namespace for cert manager
1
| kubectl create namespace cert-manager
|
You will want to apply the CRD (Custom resource definitions) for cert-manager otherwise this will not work
1
| kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yaml
|
Now install with helm
1
| helm install cert-manager jetstack/cert-manager --namespace cert-manager --values=values.yaml --version v1.9.1
|
For the values.yaml see the below YAML block
1
2
3
4
5
6
7
8
9
10
| installCRDs: false
replicaCount: 3
extraArgs:
- --dns01-recursive-nameservers=1.1.1.1:53,9.9.9.9:53
- --dns01-recursive-nameservers-only
podDnsPolicy: None
podDnsConfig:
nameservers:
- "1.1.1.1"
- "9.9.9.9"
|
We want to set the DNS servers to 1.1.1.1 and 9.9.9.9 so that cert manager can look up domains on the internet, this will prevent it matching your internal dns records. This is so the DNS-01 challenge does not fail
You will need to create an API token within your cloudflare account that can read and write to your domain.
Create and apply the secret for your API key
1
2
3
4
5
6
7
8
9
| ---
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-token-secret
namespace: cert-manager
type: Opaque
stringData:
cloudflare-token: #Token here
|
We will want to create a staging cert issuer first. this is so that we test the function in the first instance, if this repeatedly fails on the prod issuer, you can lock your account out.
1
| kubectl apply -f letsencrypt-staging.yaml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| ---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: Email@dmain
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- dns01:
cloudflare:
email: email@domain
apiTokenSecretRef:
name: cloudflare-token-secret
key: cloudflare-token
selector:
dnsZones:
- "your.domain.tld"
|
deploy a test staging cert into the default NS
1
| kubectl apply -f staging-example.yaml
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| ---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: staging-cert-name #Change to whatever you wish to call the staging cert
namespace: default
spec:
secretName: staging-cert-name #Change to whatever you wish to call the staging cert
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
commonName: "*.internal.domain.tld"
dnsNames:
- "*.internal.domain.tld"
|
Check if cert has been issued
1
| kubectl get certificate
|
Deploy a Demo Nginx service with the cert applied
Deployment of Nginx YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| ---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nginx
namespace: default #This is fine for demo purposes, use own namespace for prod deployments
labels:
app: nginx
spec:
replicas: 3 #change to how many replicas you wish to have
progressDeadlineSeconds: 600
revisionHistoryLimit: 2
strategy:
type: Recreate
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
|
Create the Service yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
| ---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
spec:
selector:
app: nginx
ports:
- name: http
targetPort: 80
port: 80
|
Create the ingress.yaml You will need to change the ingress class to match your setup. But I am using an ingress route as it provides more control.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| ---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nginx
namespace: default
annotations:
kubernetes.io/ingress.class: traefik-external #Note,you may not have ingress class if you not created one whith traefik.
spec:
entryPoints:
- websecure
routes:
- match: Host(`www.nginx.internal.domain.tld`)
kind: Rule
services:
- name: nginx
port: 80
- match: Host(`nginx.internal.domain.tld`)
kind: Rule
services:
- name: nginx
port: 80
middlewares:
- name: default-headers
tls:
secretName: #Name of staging cert
|