EQTY LabDocs

Domain and TLS

Set up the NGINX ingress controller, DNS record, and cert-manager issuer the platform needs before the Helm install.

View as Markdown

Prepare the cluster entry point: an NGINX ingress controller, a DNS record for the platform domain, and a cert-manager issuer that requests Let’s Encrypt certificates for the platform’s ingresses.

Before You Begin

  • kubectl and helm access to the cluster, and the guardian namespace (kubectl create namespace guardian)
  • A clone of the deployment repository for the helper scripts
  • A domain you control, such as guardian.example.com, and access to its DNS zone
  • An email address for Let’s Encrypt expiry notices
  • Inbound port 80 from the internet to the ingress controller, which the Let’s Encrypt HTTP-01 challenge uses

Without public HTTP-01, such as in an air-gapped cluster or with a private CA, skip Steps 3 and 4: create the TLS secret named by each ingress’s tls[].secretName yourself with kubectl create secret tls (guardian-tls in Chart Values, <env>-tls-secret from guardctl), and remove the cert-manager.io/issuer annotations from values.yaml.

All four platform services share the one domain under different paths. Auth0 and Microsoft Entra ID need no extra domain. Keycloak can run on the same domain under /keycloak or on its own domain, such as keycloak.example.com; see Keycloak.

Step 1: Install the NGINX Ingress Controller

Skip this step if the cluster already runs ingress-nginx. From the deployment repository root, run scripts/nginx.sh:

bash
./scripts/nginx.sh   # installs into ingress-nginx; change with --namespace

The script also sets the Azure load balancer health probe path to /healthz. On AKS, set the service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path annotation to /healthz on the controller service if you install the controller another way.

Step 2: Create the DNS Record

Find the controller’s external address in the EXTERNAL-IP column:

bash
kubectl get svc -n ingress-nginx ingress-nginx-controller

Create a record for the platform domain that points at that address:

CloudEXTERNAL-IP showsRecord type
EKSA hostname, such as xxx.elb.amazonaws.comCNAME
AKSAn IP addressA
GKEAn IP addressA

If Keycloak runs on its own domain, add a second record of the same type for it.

Create the record now: the certificate request at install fails until the domain resolves to the controller. Confirm it resolves before you install:

bash
dig +short guardian.example.com

Step 3: Install cert-manager

Skip this step if the cluster already runs cert-manager. Run scripts/cert-issuer.sh, which installs cert-manager with its CRDs:

bash
./scripts/cert-issuer.sh   # installs into cert-manager; change with --namespace

Step 4: Create the Let’s Encrypt Issuer

The platform’s ingresses use a namespace-scoped Issuer named letsencrypt-prod in the release namespace:

bash
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod
  namespace: guardian
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <your-email>   # Let's Encrypt expiry notices
    privateKeySecretRef:
      name: letsencrypt-production
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
EOF

To share one issuer across namespaces, create a ClusterIssuer instead: set kind: ClusterIssuer and remove metadata.namespace. Then use the cert-manager.io/cluster-issuer annotation in Step 5 in place of cert-manager.io/issuer.

Step 5: Point the Ingresses at the Issuer

The ingress blocks in Chart Values already name the letsencrypt-prod Issuer and share one guardian-tls secret across all four services. If you renamed the Issuer or created a ClusterIssuer, match the annotation on each. cert-manager requests the certificate when the chart is installed and renews it before it expires.

If you enable the Gateway Stack ingress, set gateway-stack.ingress.className: nginx and add DNS records for its hosts that aren’t the platform domain. Add cert-manager.io/issuer: "letsencrypt-prod" under gateway-stack.ingress.annotations, or set gateway-stack.ingress.certManager.clusterIssuer to the ClusterIssuer name. The Gateway Stack names its own TLS secrets.

Verify

After the Helm Deployment, check the certificate in the release namespace. It is named after the secretName in your values (guardctl uses <env>-tls-secret):

bash
kubectl get certificate -n guardian
text
NAME           READY   SECRET         AGE
guardian-tls   True    guardian-tls   2m

Then open https://guardian.example.com and confirm the browser trusts the certificate.

Troubleshooting

If READY stays False, read the events in kubectl describe certificate guardian-tls -n guardian.

SymptomLikely causeResolution
Issuer letsencrypt-prod not foundIssuer in another namespace, or annotation kind mismatchCreate the Issuer in the release namespace, or match issuer vs cluster-issuer to the kind you created
HTTP-01 challenge pending or failingDNS not propagated, or port 80 not reachableConfirm dig returns the controller address and the controller accepts traffic on port 80
Let’s Encrypt rate limit errorToo many certificate requests for the domainWait for the limit to reset; test repeated installs against the Let’s Encrypt staging server
No Certificate resource existscert-manager not running, or ingress lacks the annotationCheck kubectl get pods -n cert-manager and the ingress annotations in values.yaml
  • Helm Deployment — installing the chart once the domain and issuer are ready
  • Chart Values — the full ingress block for each service
  • Keycloak — the Keycloak ingress host and path
  • Prerequisites — the other cluster and cloud resources to prepare