diff --git a/charts/countly-web-ui-canary/templates/certificate.yaml b/charts/countly-web-ui-canary/templates/certificate.yaml new file mode 100644 index 0000000..e296052 --- /dev/null +++ b/charts/countly-web-ui-canary/templates/certificate.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.ingress.mode "acmeMergeable" -}} +{{- $fullname := include "canary.fullname" . -}} +{{- $hostname := include "canary.hostname" . -}} +{{- $issuer := .Values.ingress.tls.issuerRef -}} +{{- if not $issuer.name -}} +{{- fail "ingress.tls.issuerRef.name is required when ingress.mode=acmeMergeable" -}} +{{- end -}} +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: {{ $fullname }}-tls + namespace: {{ .Release.Namespace }} + labels: + {{- include "canary.labels" . | nindent 4 }} + annotations: + cert-manager.io/issue-temporary-certificate: "true" + argocd.argoproj.io/sync-wave: "0" +spec: + secretName: {{ $fullname }}-tls + dnsNames: + - {{ $hostname | quote }} + issuerRef: + kind: {{ $issuer.kind | default "ClusterIssuer" }} + name: {{ $issuer.name | quote }} +{{- end }} diff --git a/charts/countly-web-ui-canary/templates/ingress-master.yaml b/charts/countly-web-ui-canary/templates/ingress-master.yaml new file mode 100644 index 0000000..295e952 --- /dev/null +++ b/charts/countly-web-ui-canary/templates/ingress-master.yaml @@ -0,0 +1,26 @@ +{{- if eq .Values.ingress.mode "acmeMergeable" -}} +{{- $fullname := include "canary.fullname" . -}} +{{- $hostname := include "canary.hostname" . -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullname }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "canary.labels" . | nindent 4 }} + annotations: + nginx.org/mergeable-ingress-type: master + # ssl-redirect default flips to true when a tls: block is set; keep + # plain HTTP open for the ACME challenge on issuance and renewal. + nginx.org/ssl-redirect: "false" + argocd.argoproj.io/sync-wave: "1" + {{- include "canary.ingressAnnotations" . | nindent 4 }} +spec: + ingressClassName: {{ .Values.ingress.className }} + tls: + - hosts: + - {{ $hostname | quote }} + secretName: {{ $fullname }}-tls + rules: + - host: {{ $hostname | quote }} +{{- end }} diff --git a/charts/countly-web-ui-canary/templates/ingress-minion.yaml b/charts/countly-web-ui-canary/templates/ingress-minion.yaml new file mode 100644 index 0000000..b209e12 --- /dev/null +++ b/charts/countly-web-ui-canary/templates/ingress-minion.yaml @@ -0,0 +1,64 @@ +{{- if eq .Values.ingress.mode "acmeMergeable" -}} +{{- $fullname := include "canary.fullname" . -}} +{{- $hostname := include "canary.hostname" . -}} +{{- $stable := .Values.backend.release -}} +{{- $apiPort := .Values.backend.ports.api | int -}} +{{- $ingestorPort := .Values.backend.ports.ingestor | int -}} +{{- $jobserverPort := .Values.backend.ports.jobserver | int -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullname }}-app + namespace: {{ .Release.Namespace }} + labels: + {{- include "canary.labels" . | nindent 4 }} + annotations: + nginx.org/mergeable-ingress-type: minion + {{- if .Values.ingress.tls.proxyHideHSTS }} + # Strip HSTS leaking from the stable backend so a misconfigured canary + # can't pin a browser to expecting valid TLS for .v2.count.ly + # after the canary is torn down. + nginx.org/proxy-hide-headers: "Strict-Transport-Security" + {{- end }} + argocd.argoproj.io/sync-wave: "2" + {{- include "canary.ingressAnnotations" . | nindent 4 }} +spec: + ingressClassName: {{ .Values.ingress.className }} + rules: + - host: {{ $hostname | quote }} + http: + paths: + - path: /i/bulk + pathType: Exact + backend: { service: { name: {{ printf "%s-ingestor" $stable }}, port: { number: {{ $ingestorPort }} } } } + - path: /i/feedback/input + pathType: Exact + backend: { service: { name: {{ printf "%s-ingestor" $stable }}, port: { number: {{ $ingestorPort }} } } } + - path: /i/feedback/inputs + pathType: Exact + backend: { service: { name: {{ printf "%s-ingestor" $stable }}, port: { number: {{ $ingestorPort }} } } } + - path: /i + pathType: Exact + backend: { service: { name: {{ printf "%s-ingestor" $stable }}, port: { number: {{ $ingestorPort }} } } } + - path: /i/ + pathType: Prefix + backend: { service: { name: {{ printf "%s-api" $stable }}, port: { number: {{ $apiPort }} } } } + - path: /o + pathType: Exact + backend: { service: { name: {{ printf "%s-api" $stable }}, port: { number: {{ $apiPort }} } } } + - path: /o/ + pathType: Prefix + backend: { service: { name: {{ printf "%s-api" $stable }}, port: { number: {{ $apiPort }} } } } + - path: /api + pathType: Prefix + backend: { service: { name: {{ printf "%s-api" $stable }}, port: { number: {{ $apiPort }} } } } + - path: /v2 + pathType: Prefix + backend: { service: { name: {{ printf "%s-api" $stable }}, port: { number: {{ $apiPort }} } } } + - path: /jobs + pathType: Prefix + backend: { service: { name: {{ printf "%s-jobserver" $stable }}, port: { number: {{ $jobserverPort }} } } } + - path: / + pathType: Prefix + backend: { service: { name: {{ $fullname }}, port: { number: 80 } } } +{{- end }} diff --git a/charts/countly-web-ui-canary/templates/ingress.yaml b/charts/countly-web-ui-canary/templates/ingress.yaml index 164b991..17300e1 100644 --- a/charts/countly-web-ui-canary/templates/ingress.yaml +++ b/charts/countly-web-ui-canary/templates/ingress.yaml @@ -1,3 +1,4 @@ +{{- if eq .Values.ingress.mode "sharedSecret" -}} {{- $fullname := include "canary.fullname" . -}} {{- $stable := .Values.backend.release -}} {{- $apiPort := .Values.backend.ports.api | int -}} @@ -59,3 +60,4 @@ spec: - path: / pathType: Prefix backend: { service: { name: {{ $fullname }}, port: { number: 80 } } } +{{- end }} diff --git a/charts/countly-web-ui-canary/values.yaml b/charts/countly-web-ui-canary/values.yaml index 082c3d1..34f6b41 100644 --- a/charts/countly-web-ui-canary/values.yaml +++ b/charts/countly-web-ui-canary/values.yaml @@ -25,10 +25,31 @@ backend: ingress: className: nginx annotations: {} - # TLS off by default — chart stays generic. Cluster-specific override - # comes via ApplicationSet helm.values (see canary-web-ui ApplicationSet). - tls: {} - # secretName: # set per cluster + + # Two TLS strategies, mutually exclusive: + # sharedSecret — point at a pre-existing wildcard Secret (Flavor B). + # Single Ingress, references tls.secretName. + # acmeMergeable — per-canary LE cert via cert-manager + F5 mergeable + # Ingress (master + minion + Certificate). + # Default keeps the cluster on its current TLS path so a chart-only + # change is always a no-op until the AppSet flips this. + mode: sharedSecret + + tls: + # sharedSecret mode — name of the pre-existing wildcard Secret in the + # canary's namespace. Required when mode=sharedSecret. + secretName: "" + + # acmeMergeable mode — ClusterIssuer (or Issuer) cert-manager uses to + # issue the per-canary Certificate. Required when mode=acmeMergeable. + issuerRef: + kind: ClusterIssuer + name: "" + + # When true, the app minion strips Strict-Transport-Security on egress + # so HSTS leaking from the stable backend can't pin browsers to the + # canary host. Leave on unless you have a specific reason. + proxyHideHSTS: true resources: requests: