Networking
Once the cluster was up and running, the next step was making it accessible. The goal was to expose services over HTTPS using self-signed certificates, all under a custom internal TLD. This meant setting up ingress, configuring DNS to route traffic appropriately, and making sure certs were handled cleanly without relying on any external CAs.
Ingress Design Overview
To expose internal services over HTTPS, I needed external IPs, routing, and a certificate infrastructure I could control end-to-end. MetalLB provided a pool of IPs for services of type LoadBalancer, making the cluster reachable on the local network. Istio handled ingress traffic, routing requests to internal services based on hostnames. For certificates, I used cert-manager in combination with step-ca, a lightweight, self-hosted certificate authority. The istio-csr component connected cert-manager and Istio, enabling automatic certificate provisioning for the ingress gateway with minimal manual configuration.
Issuing and Trusting the Certificates
To enable HTTPS ingress with self-signed certificates, I used step-ca as an in-cluster CA, integrated with cert-manager and istio-csr. step-ca is registered as an Issuer, and when Istio needs a cert, istio-csr generates a CSR that cert-manager sends to step-ca. The signed cert is stored in a Kubernetes Secret, which istio-csr uses to configure the ingress gateway. This allows secure ingress without relying on any external CA.
To make workloads trust these certs, I used trust-manager to distribute the step-ca root cert and kyverno to enforce its presence. trust-manager projects a Bundle into pods, and a kyverno policy ensures it's mounted where needed. This setup lets services verify TLS connections signed by the internal CA, creating a consistent, Kubernetes-native trust boundary.
Below is a diagram showing this flow:
Configuring External Ingress
To expose services outside the cluster, I used MetalLB with a pair of IPAddressPool and L2Advertisement resources to allocate external IPs from a defined range provided by the cloud provider. These IPs were used by Istio's ingress gateway, which was configured using the Kubernetes Gateway API rather than the older LoadBalancer Service model. For consistent name resolution, I configured a wildcard DNS entry in CoreDNS that pointed all *.service.taloslab domains to the relevant address pool. This setup gave me HTTPS ingress with self-signed certificates and let me route traffic cleanly across different service environments.
After trusting the certificates locally, I could visit domains on my kubernetes cluster in the browser and use HTTPS!


Closing Thoughts
Getting ingress up and running with HTTPS on a self-contained cluster took some careful plumbing, but the result is a flexible setup that relies only on internal components. Using step-ca, cert-manager, and istio-csr keeps certificate management self-hosted and automated, while trust-manager and kyverno extend that trust throughout the cluster. If I were to expand this further, I'd likely look into automating the trust propagation and tightening policy controls across environments—but for now, this foundation is solid, self-sufficient, and does exactly what I need.
Backlinks