Skip to main content

Overview

The exchange infrastructure uses cert-manager to automatically provision and manage TLS/SSL certificates from Let’s Encrypt. This ensures secure HTTPS connections for all external-facing services without manual certificate management.

Prerequisites

Before setting up TLS certificates, ensure you have:
  1. Helm installed for deploying cert-manager
  2. NGINX Ingress Controller deployed in your cluster
  3. DNS properly configured pointing your domain to the cluster’s ingress IP
  4. cert-manager installed via Helm

Installation

Install cert-manager

Deploy cert-manager using Helm:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set installCRDs=true
Refer to the official cert-manager installation guide for detailed instructions.

Configuration

ClusterIssuer Setup

A ClusterIssuer is a cluster-wide resource that issues certificates using the ACME protocol with Let’s Encrypt. Create issuer.yml:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
Key Configuration:
  • email: Email for Let’s Encrypt expiration notices
  • server: Let’s Encrypt production ACME endpoint
  • privateKeySecretRef: Kubernetes Secret storing the ACME account private key
  • solvers: HTTP-01 challenge using NGINX ingress controller
Apply the ClusterIssuer:
kubectl apply -f issuer.yml

Verify ClusterIssuer

Check the ClusterIssuer status:
kubectl get clusterissuer
kubectl describe clusterissuer letsencrypt-prod

Certificate Provisioning

Automatic Certificate with Ingress

The recommended approach is to request certificates directly in your Ingress resource using annotations:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - exchange.jogeshwar.xyz
      secretName: exchange-tls
  rules:
    - host: exchange.jogeshwar.xyz
      http:
        paths:
          - path: /backend(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: exchange-router-service
                port:
                  number: 80
          - path: /ws
            pathType: ImplementationSpecific
            backend:
              service:
                name: exchange-ws-stream-service
                port:
                  number: 80
Important annotations:
  • cert-manager.io/cluster-issuer: letsencrypt-prod: Tells cert-manager to use the ClusterIssuer
  • tls.secretName: exchange-tls: The Secret where the certificate will be stored

Manual Certificate Resource (Optional)

For more control, you can create a standalone Certificate resource:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: exchange-cert
  namespace: default
spec:
  secretName: exchange-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  commonName: exchange.jogeshwar.xyz
  dnsNames:
    - exchange.jogeshwar.xyz
Apply the certificate:
kubectl apply -f certificate.yml
When defining certificates in Ingress annotations, you don’t need a separate Certificate resource. The Ingress annotation method is simpler and recommended for most use cases.

Certificate Validation

HTTP-01 Challenge

The ClusterIssuer uses the HTTP-01 challenge method to prove domain ownership:
  1. cert-manager creates a temporary Ingress route
  2. Let’s Encrypt makes an HTTP request to http://your-domain/.well-known/acme-challenge/<token>
  3. cert-manager responds with the validation token
  4. Let’s Encrypt verifies the response and issues the certificate
Requirements:
  • Port 80 must be accessible from the internet
  • DNS must be properly configured
  • NGINX Ingress Controller must be running

Monitoring and Verification

Check Certificate Status

# List all certificates
kubectl get certificate

# Describe specific certificate
kubectl describe certificate exchange-cert

Inspect TLS Secret

View the generated certificate secret:
# Get secret details
kubectl get secret exchange-tls -n default -o yaml

# Decode and inspect certificate
kubectl get secret exchange-tls -n default \
  -o jsonpath='{.data.tls\.crt}' | base64 --decode | \
  openssl x509 -text -noout

Certificate Renewal

cert-manager automatically renews certificates before they expire (typically 30 days before expiration). Monitor renewal status:
kubectl describe certificate exchange-cert
Look for events indicating renewal attempts.

Troubleshooting

Certificate Not Issuing

  1. Check ClusterIssuer status:
    kubectl describe clusterissuer letsencrypt-prod
    
  2. Check Certificate events:
    kubectl describe certificate exchange-cert
    
  3. Check CertificateRequest:
    kubectl get certificaterequest
    kubectl describe certificaterequest <request-name>
    
  4. Check cert-manager logs:
    kubectl logs -n cert-manager deploy/cert-manager
    

Common Issues

DNS not configured:
  • Ensure your domain points to the ingress IP
  • Verify with nslookup your-domain.com
Port 80 not accessible:
  • Check firewall rules
  • Verify LoadBalancer service is running
Wrong Ingress class:
  • Ensure ingress.class: nginx matches your ingress controller

Cleaning Up Failed Certificates

If a certificate fails to issue, clean up and retry:
# Delete certificate resource
kubectl delete certificate exchange-cert

# Delete associated secret
kubectl delete secret exchange-tls

# Reapply configuration
kubectl apply -f certificate.yml

Production Considerations

Rate Limits

Let’s Encrypt has rate limits:
  • 50 certificates per registered domain per week
  • 5 duplicate certificates per week
Use Let’s Encrypt staging environment for testing:
server: https://acme-staging-v02.api.letsencrypt.org/directory

Wildcard Certificates

For wildcard certificates (e.g., *.example.com), use DNS-01 challenge instead of HTTP-01:
solvers:
- dns01:
    cloudDNS:
      project: your-gcp-project
      serviceAccountSecretRef:
        name: clouddns-dns01-solver
        key: key.json

Certificate Backup

Backup TLS secrets for disaster recovery:
kubectl get secret exchange-tls -n default -o yaml > exchange-tls-backup.yaml

Resources

Next Steps

Sealed Secrets

Learn about secure secret management

Best Practices

Security hardening guidelines

Build docs developers (and LLMs) love