From e49440844f3dd1e5f25b19bf10773ee2a703664a Mon Sep 17 00:00:00 2001 From: James Mellors Date: Thu, 1 May 2025 22:23:22 +0100 Subject: [PATCH] setup of gateways --- argo/apps/istio-ingress-manager.yaml | 20 +++ argo/apps/kube-prometheus-stack.yaml | 2 + argo/apps/kustomization.yaml | 3 +- argo/apps/monitoring.yaml | 2 + helm/istio-ingress-manager/.helmignore | 23 +++ helm/istio-ingress-manager/Chart.yaml | 8 + .../templates/statefulset.yaml | 149 ++++++++++++++++++ helm/istio-ingress-manager/values.yaml | 86 ++++++++++ 8 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 argo/apps/istio-ingress-manager.yaml create mode 100644 helm/istio-ingress-manager/.helmignore create mode 100644 helm/istio-ingress-manager/Chart.yaml create mode 100644 helm/istio-ingress-manager/templates/statefulset.yaml create mode 100644 helm/istio-ingress-manager/values.yaml diff --git a/argo/apps/istio-ingress-manager.yaml b/argo/apps/istio-ingress-manager.yaml new file mode 100644 index 0000000..d19711a --- /dev/null +++ b/argo/apps/istio-ingress-manager.yaml @@ -0,0 +1,20 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: istio-ingress-manager + namespace: argocd +spec: + project: default # Or your specific Argo CD project + source: + repoURL: https://git.james-mellors.com/mello/Monitoring.git # Sealed Secrets chart repository + targetRevision: main # Specify the desired chart version (Check for the latest stable version!) + path: helm/istio-ingress-manager + + destination: + server: https://kubernetes.default.svc + namespace: monitoring + syncPolicy: + automated: # Optional: Enable automatic sync + prune: true + selfHeal: true + \ No newline at end of file diff --git a/argo/apps/kube-prometheus-stack.yaml b/argo/apps/kube-prometheus-stack.yaml index cda3e34..073a1a6 100644 --- a/argo/apps/kube-prometheus-stack.yaml +++ b/argo/apps/kube-prometheus-stack.yaml @@ -15,6 +15,8 @@ spec: server: https://kubernetes.default.svc namespace: monitoring syncPolicy: + syncOptions: + - ServerSideApply=true automated: prune: true selfHeal: true diff --git a/argo/apps/kustomization.yaml b/argo/apps/kustomization.yaml index 7688a03..730103f 100644 --- a/argo/apps/kustomization.yaml +++ b/argo/apps/kustomization.yaml @@ -1,4 +1,5 @@ resources: - uptime-karma.yaml - ntfy.yaml - - kube-prometheus-stack.yaml \ No newline at end of file + - kube-prometheus-stack.yaml + - istio-ingress-manager.yaml \ No newline at end of file diff --git a/argo/apps/monitoring.yaml b/argo/apps/monitoring.yaml index b9912b8..b372daf 100644 --- a/argo/apps/monitoring.yaml +++ b/argo/apps/monitoring.yaml @@ -15,6 +15,8 @@ spec: server: https://kubernetes.default.svc namespace: argocd syncPolicy: + syncOptions: + - ServerSideApply=true automated: prune: true selfHeal: true \ No newline at end of file diff --git a/helm/istio-ingress-manager/.helmignore b/helm/istio-ingress-manager/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/istio-ingress-manager/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/istio-ingress-manager/Chart.yaml b/helm/istio-ingress-manager/Chart.yaml new file mode 100644 index 0000000..61de844 --- /dev/null +++ b/helm/istio-ingress-manager/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: istio-ingress-manager +description: A Helm chart to manage Istio Gateways, VirtualServices, and Certificates for various applications +type: application +# Update version as you make changes +version: 0.1.0 +# appVersion can be set but isn't strictly tied to an app here +appVersion: "1.0" \ No newline at end of file diff --git a/helm/istio-ingress-manager/templates/statefulset.yaml b/helm/istio-ingress-manager/templates/statefulset.yaml new file mode 100644 index 0000000..a764e7f --- /dev/null +++ b/helm/istio-ingress-manager/templates/statefulset.yaml @@ -0,0 +1,149 @@ +{{- /* Loop through each application defined in values.yaml */}} +{{- range .Values.applications }} +{{- if .enabled }} +{{- /* Define variables for easier access within the loop */}} +{{- $appName := .name }} +{{- $namespace := .namespace }} +{{- $serviceName := .serviceName }} +{{- $servicePort := .servicePort }} +{{- $gatewayName := printf "%s-gateway" $appName }} +{{- /* Determine Certificate namespace: Use override if set, otherwise default to app namespace */}} +{{- $certNamespace := .certManager.createSecretInNamespace | default $namespace }} +{{- /* Determine Issuer details: Use app specific if set, otherwise use global */}} +{{- /* Note: Handling globals requires more complex logic or a helper template */}} +{{- /* For simplicity here, we assume issuerName/Kind are defined per-app if not using simple globals */}} +{{- $issuerName := .certManager.issuerName }} {{/* Simplified: Assumes set per app */}} +{{- $issuerKind := .certManager.issuerKind }} {{/* Simplified: Assumes set per app */}} +{{- /* Determine Gateway Selector: Use app specific if set, otherwise use global */}} +{{- /* Simplified: Assumes default or set per app */}} +{{- $gatewaySelector := .istio.gatewaySelector | default (dict "istio" "ingressgateway") }} + +{{- /* --- Validation --- */}} +{{- if not $namespace }}{{- fail (printf "ERROR: Application '%s' requires 'namespace' field." $appName) }}{{- end }} +{{- if not $serviceName }}{{- fail (printf "ERROR: Application '%s' requires 'serviceName' field." $appName) }}{{- end }} +{{- if not $servicePort }}{{- fail (printf "ERROR: Application '%s' requires 'servicePort' field." $appName) }}{{- end }} +{{- if not .hosts }}{{- fail (printf "ERROR: Application '%s' requires 'hosts' field." $appName) }}{{- end }} +{{- if .tls.enabled }} + {{- if not .tls.secretName }}{{- fail (printf "ERROR: Application '%s' has tls.enabled=true but requires 'tls.secretName'." $appName) }}{{- end }} + {{- if not .tls.hosts }}{{- fail (printf "ERROR: Application '%s' has tls.enabled=true but requires 'tls.hosts'." $appName) }}{{- end }} + {{- /* Validation for Cert-Manager fields only if TLS is enabled */}} + {{- if not $issuerName }}{{- fail (printf "ERROR: Application '%s' requires 'certManager.issuerName' when tls.enabled=true." $appName) }}{{- end }} + {{- if not $issuerKind }}{{- fail (printf "ERROR: Application '%s' requires 'certManager.issuerKind' when tls.enabled=true." $appName) }}{{- end }} +{{- end }} + +--- +# --- Istio Gateway for {{ $appName }} --- +apiVersion: networking.istio.io/v1beta1 +kind: Gateway +metadata: + name: {{ $gatewayName }} + namespace: {{ $namespace }} # Gateway lives in the app's namespace + labels: + app.kubernetes.io/name: {{ $appName }}-gateway + app.kubernetes.io/managed-by: {{ $.Release.Service }} + app.kubernetes.io/instance: {{ $.Release.Name }} +spec: + selector: {{ $gatewaySelector | toYaml | nindent 4 }} + servers: + # HTTP Server entry (Port 80) + - port: + number: 80 + name: http-{{ $appName }} + protocol: HTTP + hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + {{- if and .tls.enabled .istio.httpRedirect }} + tls: + httpsRedirect: true + {{- end }} + + {{- /* HTTPS Server entry (Port 443) - Only if TLS is enabled */}} + {{- if .tls.enabled }} + - port: + number: 443 + name: https-{{ $appName }} + protocol: HTTPS + hosts: + {{- range .tls.hosts }} + - {{ . | quote }} + {{- end }} + tls: + mode: SIMPLE + # Credential name refers to the Secret name (expected in $certNamespace by Istio) + credentialName: {{ .tls.secretName }} + {{- end }} + +--- +# --- Istio VirtualService for {{ $appName }} --- +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: {{ printf "%s-vs" $appName }} + namespace: {{ $namespace }} # VirtualService lives in the app's namespace + labels: + app.kubernetes.io/name: {{ $appName }}-vs + app.kubernetes.io/managed-by: {{ $.Release.Service }} + app.kubernetes.io/instance: {{ $.Release.Name }} +spec: + gateways: + - {{ $gatewayName }} # Link to the Gateway created above + hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + http: + {{- /* Assuming one path block per app for simplicity, extend if needed */}} + {{- range .paths }} + - match: + - uri: + {{- if eq .pathType "Prefix" }} + prefix: {{ .path }} + {{- else if eq .pathType "Exact" }} + exact: {{ .path }} + {{- else }} + prefix: {{ .path }} # Default to Prefix + {{- end }} + route: + - destination: + host: {{ $serviceName }} + port: + number: {{ $servicePort }} + {{- end }} + +{{- /* --- Cert-Manager Certificate (only if TLS enabled) --- */}} +{{- if .tls.enabled }} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + # Use the secret name for the Certificate resource name for consistency + name: {{ .tls.secretName }} + # Create Certificate (and Secret) in specified namespace (app namespace or override) + namespace: {{ $certNamespace }} + labels: + app.kubernetes.io/name: {{ $appName }}-certificate + app.kubernetes.io/managed-by: {{ $.Release.Service }} + app.kubernetes.io/instance: {{ $.Release.Name }} + {{- if ne $certNamespace $namespace }} + # Optional label indicating which app namespace this cert is FOR + app.kubernetes.io/for-namespace: {{ $namespace }} + {{- end }} +spec: + secretName: {{ .tls.secretName }} # Secret name used by Gateway's credentialName + issuerRef: + name: {{ $issuerName }} + kind: {{ $issuerKind }} + dnsNames: + {{- range .tls.hosts }} + - {{ . | quote }} + {{- end }} + {{- $firstHost := first .tls.hosts -}} + {{- if $firstHost }} + commonName: {{ $firstHost | quote }} + {{- end }} +{{- end }} {{- /* End if .tls.enabled for Certificate */}} + +{{- end }} {{- /* End if .enabled */}} +{{- end }} {{- /* End range .Values.applications */}} \ No newline at end of file diff --git a/helm/istio-ingress-manager/values.yaml b/helm/istio-ingress-manager/values.yaml new file mode 100644 index 0000000..1c38e31 --- /dev/null +++ b/helm/istio-ingress-manager/values.yaml @@ -0,0 +1,86 @@ +# Global defaults (Optional - can be overridden per application) +# global: +# istio: +# gatewaySelector: +# istio: ingressgateway # Default selector for Istio ingress pods +# certManager: +# issuerName: "letsencrypt-prod" +# issuerKind: ClusterIssuer +# createSecretInNamespace: "" # Default: empty means create in app namespace + +# List of applications to configure ingress for +applications: + # --- Example for Grafana --- + - name: grafana + # Set to true to create resources for this application + enabled: true + # Namespace where the Grafana SERVICE lives (and where Gateway/VS will be created) + namespace: monitoring + # Name of the Grafana Kubernetes Service + serviceName: kube-prometheus-stack-grafana # Adjust if different + # Port the Grafana Service listens on + servicePort: 80 # The K8s service port, often 80, targets Grafana's internal port (3000) + # Hostname(s) for accessing Grafana + hosts: + - grafana.james-mellors.com + # Path configuration for routing + paths: + - path: / + pathType: Prefix # Prefix or Exact + + # TLS specific configuration + tls: + # Set to true if this application uses TLS (required for certificate) + enabled: true + # Name of the Kubernetes Secret to store/read the TLS certificate + secretName: grafana-tls + # List of hosts the certificate should cover (usually matches 'hosts' above) + hosts: + - grafana.james-mellors.com + + # Istio specific overrides for this application (Optional) + istio: + # Override the global gateway selector if needed + # gatewaySelector: + # istio: my-custom-gateway + # Enable HTTP->HTTPS redirect for this app's Gateway + httpRedirect: true + + # Cert-Manager specific overrides for this application (Optional) + certManager: + # Override global issuer if needed + # issuerName: "my-internal-ca" + # issuerKind: Issuer + # Override where the Secret is created (e.g., "istio-system" or keep empty for app namespace) + createSecretInNamespace: "" # Default: uses 'namespace' field above + + # --- Example for NTFY --- + # - name: ntfy + # enabled: true + # namespace: default # Or wherever your NTFY service lives + # serviceName: ntfy-server # Adjust if different + # servicePort: 80 # The port the K8s service listens on + # hosts: + # - ntfy.yourdomain.com + # paths: + # - path: / + # pathType: Prefix + # tls: + # enabled: true + # secretName: ntfy-tls + # hosts: + # - ntfy.yourdomain.com + # istio: + # httpRedirect: true + # certManager: + # # Using global defaults defined above (if any) or specify here + # # issuerName: "letsencrypt-prod" + # # issuerKind: ClusterIssuer + # createSecretInNamespace: "" # Default: uses 'namespace' field above + + # --- Add more applications here --- + # - name: my-other-app + # enabled: false # Disabled example + # namespace: apps + # serviceName: my-other-app-svc + # ... etc ... \ No newline at end of file