Search code examples
kubernetesnginx-ingresslets-encryptspring-cloud-gatewaycert-manager

How to configure spring-cloud-gateway with letsencrypt cluster-issuer on k8s?


I'm trying to configure a K8s Ingress for a spring-cloud-gateway instance deployed on Kubernetes, but can't figure out how to serve it with a valid SSL certificate.

Here is my ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-production
    acme.cert-manager.io/http01-edit-in-place: "true"
  name: demo-bff
  namespace: demo-bff
spec:
  rules:
  - host: bff.demo.c4-soft.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: bff-gateway
            port:
              number: 8080
  tls:
    - hosts:
        - bff.demo.c4-soft.com
      secretName: demo-bff-tls

The letsencrypt-production cluster-issuer is successfully used from another namespace to expose Keycloak.

I believe the issue is in my gateway configuration not correctly routing / authorizing the HTTP-01 challenge. Anyone knows how to configure spring-cloud-gateway with cert-manager, a cluster-issuer and letsencrypt?

What I tried so far for gateway config (in addition to standard OAuth2 client configuration):

spring:
  cloud:
    gateway:
      default-filters:
      - TokenRelay=
      - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
      - SaveSession
      routes:
      - id: letsencrypt
        uri: http://cert-manager-webhook
        predicates:
        - Path=/.well-known/acme-challenge/**

extract of kubectl get services --all-namespaces

cert-manager    service/cert-manager-webhook                 ClusterIP      10.3.8.243     <none>           443/TCP                      4d2h
default         service/kubernetes                           ClusterIP      10.3.0.1       <none>           443/TCP                      4d2h
demo-bff        service/bff-gateway                          ClusterIP      10.3.49.122    <none>           8080/TCP                     175m
ingress-nginx   service/ingress-nginx-controller             LoadBalancer   10.3.86.83     148.113.158.14   80:30664/TCP,443:31206/TCP   4d2h
ingress-nginx   service/ingress-nginx-controller-admission   ClusterIP      10.3.31.34     <none>           443/TCP                      4d2h
kube-system     service/kube-dns                             ClusterIP      10.3.0.10      <none>           53/UDP,53/TCP,9153/TCP       4d2h

Solution

  • I finally got it working by changing two things:

    • switch the cert-manager-webhook URI to https (the service is bound to port 443, as can be seen above)
    • ensure the /.well-known/acme-challenge/** path-matcher has permitAll() in exchanges authorization
    spring:
      cloud:
        gateway:
          default-filters:
          - TokenRelay=
          - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
          - SaveSession
          routes:
          - id: letsencrypt
            uri: https://cert-manager-webhook
            predicates:
            - Path=/.well-known/acme-challenge/**
    com:
      c4-soft:
        springaddons:
          security:
            client:
              security-matchers: /**
              permit-all:
              - /login/**
              - /oauth2/**
              - /
              - /login-options
              - "/me"
              - /ui/**
              - /actuator/health/readiness
              - /actuator/health/liveness
              - /.well-known/acme-challenge/**
    

    The com.c4-soft.springaddons.security properties are used by an alternate Spring Boot starter of mine (wrapping spring-boot-starter-oauth2-client). Those reported here are used in http.authorizeExchange(authorizeExchange -> authorizeExchange.pathMatchers(permitAll).permitAll()).