Setting up SSL using cert-manager through HTTP-01 challenge

In this page we will see how can we create SSL certificate using cert-manager through HTTP-01 challenge

What are HTTP-01 and DNS-01 challenge ?

The DNS-01 and HTTP-01 challenges are two methods used in the ACME (Automated Certificate Management Environment) protocol for validating domain ownership when requesting SSL/TLS certificates, typically for securing websites.

DNS-01 challenge involves creating a DNS TXT record with a specific value provided by the certificate authority (CA) to prove domain ownership. This method is usually preferred when automating certificate issuance because it doesn't require a publicly accessible web server but does require control over the domain's DNS records.

On the other hand, HTTP-01 challenge requires hosting a specific file with a token provided by the CA on the web server associated with the domain. The CA performs an HTTP request to verify that the token exists at a specific URL, confirming domain ownership. This method is suitable for domains with web servers that are publicly accessible.

Why HTTP-01 ?

It is sometimes not possible to pass the authentication keys of the DNS server to cert-manager to perform DNS-01 challenge. For cases like these a web server can be hosted in the kubernetes cluster through which HTTP-01 challenge can be completed and certificate can be generated.

Generating Certificate Cert-manager and Istio.

  1. Make sure cert-manager is installed in the cluster. For this head over to the Integrations tab, click on the three dots on your cluster card.

  2. Click Manage Applications and install cert-manager.

  3. Export these variables

    export DOMAIN="" # enter your domain name here, can't we wildcard as it is not supported for HTTP-01 challenge
    export EMAIL=""
    
  4. Create the Issuer with http01 challenge. Issuer should be created in istio-system namespace only.

    k apply -f -<<EOF
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: letsencrypt-prod-cluster
      namespace: istio-system
    spec:
      acme:
        email: $EMAIL
        server: https://acme-v02.api.letsencrypt.org/directory
        privateKeySecretRef:
          name: letsencrypt-prod-cluster
        solvers:
        - http01:
            ingress:
              class: istio
    EOF
    
  5. Create the certificate. Certificate should be created in istio-system namespace only.

    k apply -f -<<EOF
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: letsencrypt-prod-cert
      namespace: istio-system
    spec:
      secretName: letsencrypt-prod-secret
      duration: 2160h # 90d
      renewBefore: 360h # 15d
      isCA: false
      privateKey:
        algorithm: RSA
        encoding: PKCS1
        size: 2048
      usages:
        - server auth
        - client auth
      dnsNames:
        - "$DOMAIN"
      issuerRef:
        name: letsencrypt-prod-cluster
    EOF
    
  6. The certificate will be in False state

    kubectl get certificates.cert-manager.io -n istio-system
    NAME                    READY   SECRET                   AGE
    letsencrypt-prod-cert   False   letsencrypt-prod-secret   12m
    
  7. This will create a pod, service and an ingress in istio-system namespace.

    kubectl get pods,svc,ing -n istio-system -l acme.cert-manager.io/http01-solver=true
    
  8. We need to copy the service name and the port number from this ingress

    SERVICE_NAME=$(kubectl get ing -n istio-system -l  acme.cert-manager.io/http01-solver=true  -ojsonpath='{.items[0].spec.rules[0].http.paths[0].backend.service.name}')
    PORT_NUMBER=$(kubectl get ing -n istio-system -l  acme.cert-manager.io/http01-solver=true  -ojsonpath='{.items[0].spec.rules[0].http.paths[0].backend.service.port.number}')
    
  9. We need to create a VirtualService object with the ingress details

    k apply -f -<<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: letsencrypt-prod-vs
      namespace: istio-system
    spec:
      hosts:
      - "$DOMAIN"
      gateways:
      - istio-system/tfy-wildcard
      http:
      - match:
        - uri:
            prefix: /
        rewrite:
          uri: /
        route:
        - destination:
            host: SERVICE_NAME
            port:
              number: $PORT_NUMBER
    EOF
    
  10. Now we will edit tfy-istio-ingress in Deployments tab under Helm section.

    1. Make suretfy-istio-ingress is running.

    2. Edit tfy-istio-ingress to check if httpsRedirect is set to false

    3. Edit the HTTPS section of the gateway to credentialName to have the value of spec.secretName of the certificate we created above. Keep the protocol for tls[1] to HTTPS

      tfyGateway:
        name: tfy-wildcard
        spec:
          servers:
            - tls:
                httpsRedirect: false
              port:
                name: http-tfy-wildcard
                number: 80
                protocol: HTTP
              hosts:
                - $DOMAIN
            - tls:
                mode: SIMPLE
                credentialName: letsencrypt-prod-secret
              port:
                name: https-tfy-wildcard
                number: 443
                protocol: HTTPS
              hosts:
                - $DOMAIN
          selector:
            istio: tfy-istio-ingress
      
      
  11. Once this is done we need to create a DNS record in the DNS server to allow $DOMAIN to point to the load balancer created by the cloud provider. To get the IP or the hostname, run the below command

    kubectl get svc tfy-istio-ingress -n istio-system
    NAME                TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                                      AGE
    tfy-istio-ingress   LoadBalancer   10.0.58.231   <YOUR-IP-HERE>   15021:30547/TCP,80:32123/TCP,443:30997/TCP   11h
    
  12. Once this is done we will wait for the certificate to get in the ready state.

  13. Once the certificate gets in the ready state, we can edit the httpsRedirect: true