{{- /* The public TLS edge (ADR-0035). Rendered only when `public.domain` is set; with it empty the stack is reached on the NodePorts below and nothing here exists. Caddy rather than an ingress controller: the four portals already run caddy:2-alpine, so this adds no dependency, and it does ACME itself — no cert-manager, no CRDs, no Ingress objects for five hostnames that never change. It proxies to the ClusterIP services, so the browser-facing NodePorts are not involved in a public deployment. The public IP lives on the Fedora host, which forwards 80/443 to the two NodePorts below. That forward is dumb L4 — no TLS, no routing — see the runbook. */}} {{- if .Values.public.domain }} {{- $pub := .Values.public }} --- apiVersion: v1 kind: ConfigMap metadata: name: caddy-edge-config labels: {{- include "big.labels" (dict "root" $ "name" "caddy-edge") | nindent 4 }} data: Caddyfile: | { {{- with $pub.email }} email {{ . }} {{- end }} } {{- range $sub, $target := $pub.routes }} {{ $sub }}.{{ $pub.domain }} { reverse_proxy {{ $target }} } {{- end }} --- apiVersion: apps/v1 kind: Deployment metadata: name: caddy-edge labels: {{- include "big.labels" (dict "root" $ "name" "caddy-edge") | nindent 4 }} spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app.kubernetes.io/name: caddy-edge app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: annotations: # A ConfigMap mounted with subPath never updates in place, so a changed # Caddyfile has to roll the pod. checksum/caddyfile: {{ printf "%s|%v|%v" $pub.domain $pub.email $pub.routes | sha256sum }} labels: {{- include "big.labels" (dict "root" $ "name" "caddy-edge") | nindent 8 }} spec: containers: - name: caddy-edge image: {{ $pub.image }} ports: - name: http containerPort: 80 - name: https containerPort: 443 # TCP, not HTTP: a GET with no matching Host gets a 404 from Caddy, which # would fail an httpGet probe for a perfectly healthy edge. readinessProbe: tcpSocket: { port: 443 } volumeMounts: - name: config mountPath: /etc/caddy/Caddyfile subPath: Caddyfile readOnly: true - name: data mountPath: /data - name: run mountPath: /config volumes: - name: config configMap: name: caddy-edge-config - name: run emptyDir: {} - name: data {{- if .Values.persistence.storageClass }} persistentVolumeClaim: claimName: caddy-edge-data {{- else }} # Certificates live here. On an emptyDir every pod restart asks Let's # Encrypt again, and its duplicate-certificate limit is five per week — # set persistence.storageClass for anything that stays up. emptyDir: {} {{- end }} --- apiVersion: v1 kind: Service metadata: name: caddy-edge labels: {{- include "big.labels" (dict "root" $ "name" "caddy-edge") | nindent 4 }} spec: type: NodePort selector: app.kubernetes.io/name: caddy-edge app.kubernetes.io/instance: {{ .Release.Name }} ports: - name: http port: 80 targetPort: 80 nodePort: {{ $pub.nodePorts.http }} - name: https port: 443 targetPort: 443 nodePort: {{ $pub.nodePorts.https }} {{- if .Values.persistence.storageClass }} --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: caddy-edge-data labels: {{- include "big.labels" (dict "root" $ "name" "caddy-edge") | nindent 4 }} # Keep the certificates when the release is uninstalled — re-issuing them on # every reinstall is what burns the rate limit. annotations: helm.sh/resource-policy: keep spec: accessModes: [ReadWriteOnce] storageClassName: {{ .Values.persistence.storageClass }} resources: requests: storage: 128Mi {{- end }} {{- end }}