Adding SSL to Local KinD Clusters: Comprehensive Guide
This guide provides detailed instructions for implementing SSL/TLS in your local KinD (Kubernetes in Docker) clusters for secure HTTPS development and testing.
Prerequisites
- A running KinD cluster
- kubectl configured to use your cluster
- Helm v3 installed
- Basic understanding of Kubernetes networking and certificates
1. Setting Up Ingress Controller
First, we need to install an ingress controller to manage external access to services:
# Install NGINX ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/kind/deploy.yaml
# Wait for the controller to be ready
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120s2. Installing cert-manager
cert-manager is a Kubernetes add-on to automate the management and issuance of TLS certificates from various issuing sources.
Step 1: Add the Jetstack Helm repository
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install cert-manager with CRDs
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.0/cert-manager.crds.yaml
#kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.0/cert-manager.yaml
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.17.0
# --set crds.enabled=true
# Verify the installation
kubectl get pods -n cert-manager3. Creating a Self-Signed Certificate
Step 1: Create a self-signed issuer
# Create a file named self-signed-issuer.yaml
cat > self-signed-issuer.yaml << EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
EOF
# Apply the configuration
kubectl apply -f self-signed-issuer.yaml
# Verify the issuer is ready
kubectl get clusterissuer selfsigned-issuer -o jsonpath='{.status.conditions[0].status}'Step 2: Installing cert-manager
cert-manager is a Kubernetes addon that automates certificate management:
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install cert-manager with CRDs
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.1/cert-manager.crds.yaml
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.13.1
# Verify the installation
kubectl get pods -n cert-managerStep 3: Creating Certificate Issuers
Self-Signed Certificate Issuer For development purposes, a self-signed issuer is sufficient:
# Create a file named self-signed-issuer.yaml
cat > self-signed-issuer.yaml << EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
EOF
# Apply the configuration
kubectl apply -f self-signed-issuer.yaml
# Verify the issuer is ready
kubectl get clusterissuer selfsigned-issuer -o jsonpath='{.status.conditions[0].status}'Let's Encrypt Staging (Optional) For testing with Let's Encrypt without rate limits:
# Create a file named letsencrypt-staging.yaml
cat > letsencrypt-staging.yaml << EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
email: kelly.carinola@gmail.com
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
class: nginx
EOF
# Apply the configuration
kubectl apply -f letsencrypt-staging.yamlLet's Encrypt Production (Optional)
For production certificates (be cautious of rate limits):
# Create a file named letsencrypt-prod.yaml
cat > letsencrypt-prod.yaml << EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: kelly.carinola@gmail.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx
EOF
# Apply the configuration
kubectl apply -f letsencrypt-prod.yamlStep 4: Create a certificate resource
# Create a file named certificate.yaml
cat > certificate.yaml << EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: local-tls
namespace: default
spec:
secretName: local-tls-cert
dnsNames:
- app.local
- api.local
- "*.app.local"
isCA: false
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
EOF
# Apply the configuration
kubectl apply -f certificate.yaml
# Verify the certificate is ready
kubectl get certificate -n defaultStep 5: Configuring Ingress with SSL
Create an ingress resource that uses the certificate:
# First, deploy a sample application
kubectl create deployment demo --image=nginx --port=80
kubectl expose deployment demo --port=80
# Create a file named secure-ingress.yaml
cat > secure-ingress.yaml << EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: secure-ingress
annotations:
spec.ingressClassName: nginx
cert-manager.io/cluster-issuer: selfsigned-issuer
spec:
tls:
- hosts:
- app.local
secretName: local-tls-cert
rules:
- host: app.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo
port:
number: 80
EOF
# Apply the configuration
kubectl apply -f secure-ingress.yaml
# Verify the ingress is created
kubectl get ingressStep 6: Setting Up Local DNS
Configure your local machine to resolve the hostnames:
# Add entries to /etc/hosts
sudo sh -c 'echo "127.0.0.1 app.local api.local" >> /etc/hosts'Step 7: Testing the Setup
# Using curl with insecure flag (for self-signed certs)
curl -k https://app.local
# Or using wget
wget --no-check-certificate https://app.localStep 8: Trusting Certificates Locally
Export the Certificate
kubectl get secret local-tls-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > local-cert.pemAdd to System Trust Store Linux (Debian/Ubuntu)
sudo cp local-cert.pem /usr/local/share/ca-certificates/local-cert.crt
sudo update-ca-certificatesmacOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain local-cert.pemWindows
Import-Certificate -FilePath local-cert.pem -CertStoreLocation Cert:\LocalMachine\RootStep 9: Advanced Configurations
Wildcard Certificates
For covering multiple subdomains:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
namespace: default
spec:
secretName: wildcard-tls-cert
dnsNames:
- "*.example.local"
isCA: false
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuerUsing Certificates Across Namespaces
To use a certificate in multiple namespaces, create a Certificate resource in each namespace and reference the same Secret:
# Create certificate in a central namespace
kubectl create namespace cert-central
# Create the certificate
cat > shared-cert.yaml << EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: shared-tls
namespace: cert-central
spec:
secretName: shared-tls-cert
dnsNames:
- "*.example.local"
isCA: false
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
EOF
kubectl apply -f shared-cert.yaml10. Troubleshooting
Certificate Not Being Issued Check cert-manager logs:
kubectl logs -n cert-manager $(kubectl get pods -n cert-manager -l app=cert-manager -o name) -fCheck the status of the certificate:
kubectl describe certificate local-tls-cert -n defaultIngress Controller Issues Check the logs of the ingress controller:
kubectl logs -n ingress-nginx $(kubectl get pods -n ingress-nginx -l app.kubernetes.io/component=controller -o name) -fHow to fix pending on EXTERNAL-IP
Apply MetalLB manifest
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
Wait for MetalLB to be ready
# Apply MetalLB manifest
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
# Wait for MetalLB to be ready
kubectl wait --namespace metallb-system \
--for=condition=ready pod \
--selector=app=metallb \
--timeout=90s
# Create IP address pool (using Docker's default network range)
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 172.18.0.200-172.18.0.250
EOF
# Create L2Advertisement
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
spec:
ipAddressPools:
- first-pool
EOFConclusion
You now have a fully functional SSL setup in your local KinD cluster. This enables secure HTTPS development and testing without needing to deploy to a cloud environment.
For production environments, consider using Let's Encrypt with appropriate DNS or HTTP challenges instead of self-signed certificates.