kuberneteskubernetes-ingresslets-encryptcert-manager

HTTP-01 Challenge Fails on Bare-Metal Kubernetes with cert-manager and Let’s Encrypt


I am running a bare-metal Kubernetes cluster (no cloud provider) with cert-manager and Let’s Encrypt to issue TLS certificates. I bought a domain from Namecheap and configured a basic Nginx ingress controller. However, my certificates are stuck in a pending/errored state.

Setup Kubernetes cluster: 1 control plane + 3 workers CI/CD: GitHub Runner with ArgoCD Ingress: Nginx LoadBalancer: MetalLB with a local IP assigned to the Nginx controller Domain: A record points to my public IP

Here is what my conf is (I have remove my domain and email info.):

K8s

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: private-test-deployment-api
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - 'my-domain'
      secretName: my-domain-tls
  rules:
    - host: 'my-domain'
      http:
        paths:
          - path: /grades
            pathType: Prefix
            backend:
              service:
                name: private-test-deployment-api
                port:
                  number: 80
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: 'my-email'
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx
            ingressTemplate:
              metadata:
                annotations:
                  nginx.ingress.kubernetes.io/ssl-redirect: "false"
                  nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
 0    ;;; PPPoer Connection !
      chain=srcnat action=masquerade out-interface=pppoe-out1 log=no log-prefix="" 

 2    ;;; Forward HTTP to worker0 ingress
      chain=dstnat action=dst-nat to-addresses=192.168.88.39 to-ports=80 protocol=tcp in-interface=pppoe-out1 dst-port=80 
      log=no log-prefix="" 

 3    ;;; Forward HTTPS to worker0 ingress
      chain=dstnat action=dst-nat to-addresses=192.168.88.39 to-ports=443 protocol=tcp in-interface=pppoe-out1 dst-port=443 
      log=no log-prefix=""
0    ;;; Allow HTTP to ingress
      chain=forward action=accept protocol=tcp dst-address=192.168.88.39 in-interface=pppoe-out1 dst-port=80 

 1    ;;; Allow HTTPS to ingress
      chain=forward action=accept protocol=tcp dst-address=192.168.88.39 in-interface=pppoe-out1 dst-port=443 

 2    ;;; Allow Access From Trusted IPs
      chain=input action=accept src-address-list=Trusted-IPs log=no log-prefix="" 

 3    ;;; Accept Established & Related Connections
      chain=input action=accept connection-state=established,related log=no log-prefix="" 

 4    ;;; Accept ICMP Request From LAN
      chain=input action=accept tcp-flags="" protocol=icmp in-interface=ether2 icmp-options=8:0-255 log=no log-prefix="" 

 5    ;;; Accept DNS-UDP
      chain=input action=accept protocol=udp in-interface=ether2 dst-port=53 log=no log-prefix="" 

 6    ;;; Accept DNS-TCP
      chain=input action=accept protocol=tcp in-interface=ether2 dst-port=53 log=no log-prefix="" 

 7    ;;; Drop Everything Else
      chain=input action=drop log=no log-prefix=""

Tests

kubectl -n default run -it --rm --image=curlimages/curl curltest -- curl -sv http://private-test-deployment-api.default/.well-known/acme-challenge/test

Returns: error: timed out waiting for the condition

curl -v http://test.icptokens.net/.well-known/acme-challenge/test

Returns: 404 (Not Found)

Observation and Question I suspect the issue is external HTTP traffic not reaching the solver pod, but I’m not sure if it’s my MikroTik NAT rules, firewall, or ingress configuration.

Has anyone successfully run HTTP-01 on a bare-metal cluster behind a MikroTik router? I would appreciate guidance on:

Thanks in advance for any advice!


Solution

  • I know that Namecheap is not a natively supported issuer for cert-manager but in your situation I would drop firewall/NAT rules in favor of webhook issuer.

    This method will allow you to generate certificate via placing txt record in your DNS provider and generate any certificate without need to expose your internal cluster.

    Example webhook for your DNS.