diff --git a/charts/dataingestion-service/templates/traefik-ingress.yaml b/charts/dataingestion-service/templates/traefik-ingress.yaml new file mode 100644 index 000000000..1738e6cf1 --- /dev/null +++ b/charts/dataingestion-service/templates/traefik-ingress.yaml @@ -0,0 +1,143 @@ +{{/* +Traefik Ingress - Data Ingestion and Reporting Service routes +Replaces: k8s-manifests/traefik-ingress-dataingestion.yaml +Only rendered when traefikIngress.enabled is true. +Includes body-size-limit middleware. +*/}} +{{- if .Values.traefikIngress.enabled }} +{{- $fullName := include "dataingestion-service.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }}-traefik-ingress + labels: + {{- include "dataingestion-service.labels" . | nindent 4 }} + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-production" + traefik.ingress.kubernetes.io/router.middlewares: {{ .Release.Namespace }}-body-size-limit@kubernetescrd +spec: + ingressClassName: traefik + tls: + - secretName: {{ .Values.ingressHost }} + hosts: + - {{ .Values.ingressHost }} + rules: + - host: {{ .Values.ingressHost }} + http: + paths: + - path: "/ingestion/" + pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- if eq .Values.reportingService.enabled "true" }} + - path: "/reporting/person-svc/" + pathType: Prefix + backend: + service: + name: person-reporting-service + port: + number: {{ .Values.reportingService.personReportingServicePort }} + - path: "/reporting/organization-svc/" + pathType: Prefix + backend: + service: + name: organization-reporting-service + port: + number: {{ .Values.reportingService.organizationReportingServicePort }} + - path: "/reporting/investigation-svc/" + pathType: Prefix + backend: + service: + name: investigation-reporting-service + port: + number: {{ .Values.reportingService.investigationReportingServicePort }} + - path: "/reporting/observation-svc/" + pathType: Prefix + backend: + service: + name: observation-reporting-service + port: + number: {{ .Values.reportingService.observationReportingServicePort }} + - path: "/reporting/post-processing-svc/" + pathType: Prefix + backend: + service: + name: post-processing-reporting-service + port: + number: {{ .Values.reportingService.postProcessingReportingServicePort }} + - path: "/reporting/ldfdata-svc/" + pathType: Prefix + backend: + service: + name: ldfdata-reporting-service + port: + number: {{ .Values.reportingService.ldfdataReportingServicePort }} + {{- end }} + {{- if eq .Values.dataprocessingService.enabled "true" }} + - path: "/rti/" + pathType: Prefix + backend: + service: + name: data-processing-service + port: + number: {{ .Values.dataprocessingService.port }} + {{- end }} + {{- if eq .Values.nndService.enabled "true" }} + - path: "/extraction/" + pathType: Prefix + backend: + service: + name: nnd-service + port: + number: {{ .Values.nndService.port }} + {{- end }} + {{- if eq .Values.srtedataService.enabled "true" }} + - path: "/data/" + pathType: Prefix + backend: + service: + name: srte-data-service + port: + number: {{ .Values.srtedataService.port }} + {{- end }} + {{- if eq .Values.dataExtraction.enabled "true" }} + - path: "/data-extraction/" + pathType: Prefix + backend: + service: + name: data-extraction-service + port: + number: {{ .Values.dataExtraction.port }} + {{- end }} + {{- if eq .Values.caseNotification.enabled "true" }} + - path: "/case-notification/" + pathType: Prefix + backend: + service: + name: case-notification-service + port: + number: {{ .Values.caseNotification.port }} + {{- end }} + {{- if eq .Values.xmlHl7Parser.enabled "true" }} + - path: "/hl7-parser/" + pathType: Prefix + backend: + service: + name: xml-hl7-parser-service + port: + number: {{ .Values.xmlHl7Parser.port }} + {{- end }} + {{- if eq .Values.compare.enabled "true" }} + - path: "/comparison/" + pathType: Prefix + backend: + service: + name: data-compare-api-service + port: + number: {{ .Values.compare.port }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/dataingestion-service/values.yaml b/charts/dataingestion-service/values.yaml index 02ecf5fa3..2d0154f3e 100644 --- a/charts/dataingestion-service/values.yaml +++ b/charts/dataingestion-service/values.yaml @@ -69,13 +69,16 @@ compare: port: 8085 ingress: - enabled: true + enabled: false className: "nginx" tls: - secretName: "data.EXAMPLE_DOMAIN" hosts: - "data.EXAMPLE_DOMAIN" +traefikIngress: + enabled: true + resources: {} autoscaling: diff --git a/charts/modernization-api/templates/traefik-ingress.yaml b/charts/modernization-api/templates/traefik-ingress.yaml new file mode 100644 index 000000000..ed02055eb --- /dev/null +++ b/charts/modernization-api/templates/traefik-ingress.yaml @@ -0,0 +1,71 @@ +{{/* +Traefik Ingress - Main application routes (KeyCloak + NBS Gateway) +Replaces: k8s-manifests/traefik-ingress-main.yaml +Only rendered when traefikIngress.enabled is true. +Only one ingress type should be enabled: nginx (ingress), istio (istioGatewayIngress), or traefik (traefikIngress). +*/}} +{{- if .Values.traefikIngress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "modernization-api.fullname" . }}-traefik-ingress + labels: + {{- include "modernization-api.labels" . | nindent 4 }} + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-production" + traefik.ingress.kubernetes.io/router.middlewares: {{ .Release.Namespace }}-nbs-custom-headers@kubernetescrd,{{ .Release.Namespace }}-body-size-limit@kubernetescrd +spec: + ingressClassName: traefik + tls: + - secretName: {{ .Values.ingressHost }} + hosts: + - {{ .Values.ingressHost }} + rules: + - host: {{ .Values.ingressHost }} + http: + paths: + # KeyCloak auth routes + - path: "/auth/realms/nbs-users/" + pathType: Prefix + backend: + service: + name: keycloak + port: + name: http + - path: "/auth/robots.txt" + pathType: Exact + backend: + service: + name: keycloak + port: + name: http + - path: "/auth/js/" + pathType: Prefix + backend: + service: + name: keycloak + port: + name: http + - path: "/auth/resources/" + pathType: Prefix + backend: + service: + name: keycloak + port: + name: http + # NBS Gateway routes + - path: /favicon.ico + pathType: Prefix + backend: + service: + name: nbs-gateway-svc + port: + number: 8000 + - path: "/" + pathType: Prefix + backend: + service: + name: nbs-gateway-svc + port: + number: 8000 +{{- end }} \ No newline at end of file diff --git a/charts/modernization-api/templates/traefik-ingressroute-cached.yaml b/charts/modernization-api/templates/traefik-ingressroute-cached.yaml new file mode 100644 index 000000000..c2757d601 --- /dev/null +++ b/charts/modernization-api/templates/traefik-ingressroute-cached.yaml @@ -0,0 +1,29 @@ +{{/* +Traefik IngressRoute - Cached static assets with regex path matching +Replaces: k8s-manifests/traefik-ingressroute-cached.yaml +Uses IngressRoute CRD because regex path patterns cannot be expressed +with standard Kubernetes Ingress pathType (Prefix/Exact). +Only rendered when traefikIngress.enabled is true. +*/}} +{{- if .Values.traefikIngress.enabled }} +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ include "modernization-api.fullname" . }}-traefik-cached + labels: + {{- include "modernization-api.labels" . | nindent 4 }} +spec: + entryPoints: + - websecure + routes: + - match: "Host(`{{ .Values.ingressHost }}`) && PathRegexp(`^/(?!auth|ecr-viewer|orchestration).+\\.(jpg|svg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|ttf|woff|woff2)$`)" + kind: Rule + services: + - name: nbs-gateway-svc + port: 8000 + middlewares: + - name: nbs-cached-headers + namespace: {{ .Release.Namespace }} + tls: + secretName: {{ .Values.ingressHost }} +{{- end }} \ No newline at end of file diff --git a/charts/modernization-api/templates/traefik-middlewares.yaml b/charts/modernization-api/templates/traefik-middlewares.yaml new file mode 100644 index 000000000..f55a2a50d --- /dev/null +++ b/charts/modernization-api/templates/traefik-middlewares.yaml @@ -0,0 +1,63 @@ +{{/* +Traefik Middleware CRDs - Response headers, body size limits, cached headers +Replaces: + - k8s-manifests/traefik-middleware-headers.yaml + - k8s-manifests/traefik-middleware-body-limit.yaml + - k8s-manifests/traefik-middleware-cached-headers.yaml +Only rendered when traefikIngress.enabled is true. +*/}} +{{- if .Values.traefikIngress.enabled }} +# ============================================================================= +# Custom Response Headers (replaces NGINX configuration-snippet) +# Replaces: nginx.ingress.kubernetes.io/configuration-snippet: +# more_set_headers "X-Frame-Options: Allow"; +# more_set_headers "Cross-Origin-Opener-Policy: same-origin"; +# ============================================================================= +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: nbs-custom-headers + labels: + {{- include "modernization-api.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress +spec: + headers: + customResponseHeaders: + X-Frame-Options: "Allow" + Cross-Origin-Opener-Policy: "same-origin" +--- +# ============================================================================= +# Body Size Limit (replaces NGINX proxy-body-size: "100m") +# ============================================================================= +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: body-size-limit + labels: + {{- include "modernization-api.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress +spec: + buffering: + maxRequestBodyBytes: {{ int64 (.Values.traefikIngress.bodySizeLimit.maxRequestBodyBytes | default 104857600) }} + memRequestBodyBytes: 10485760 + maxResponseBodyBytes: 0 + memResponseBodyBytes: 1048576 +--- +# ============================================================================= +# Cached Asset Headers (adds Cache-Control for static assets) +# Replaces: NGINX configuration-snippet on main-ingress-resource-cached +# ============================================================================= +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: nbs-cached-headers + labels: + {{- include "modernization-api.labels" . | nindent 4 }} + app.kubernetes.io/component: ingress +spec: + headers: + customResponseHeaders: + X-Frame-Options: "Allow" + Cross-Origin-Opener-Policy: "same-origin" + Cache-Control: "max-age=1209600, immutable" +{{- end }} \ No newline at end of file diff --git a/charts/modernization-api/values.yaml b/charts/modernization-api/values.yaml index 3618d89e9..c6bf73710 100644 --- a/charts/modernization-api/values.yaml +++ b/charts/modernization-api/values.yaml @@ -41,7 +41,7 @@ pageBuilder: enabled: "false" ingress: - enabled: true + enabled: false className: "nginx" tls: - secretName: app.EXAMPLE_DOMAIN @@ -56,6 +56,13 @@ istioGatewayIngress: certificateName: "" certificateIssuerName: "letsencrypt-production" +traefikIngress: + enabled: true + bodySizeLimit: + # Max request body size in bytes (100MB — matches NGINX proxy-body-size: 100m) + maxRequestBodyBytes: 104857600 + + mTLS: enabled: true diff --git a/charts/traefik/README.md b/charts/traefik/README.md new file mode 100644 index 000000000..d0273485f --- /dev/null +++ b/charts/traefik/README.md @@ -0,0 +1,40 @@ +# Traefik Ingress Controller + +Traefik v3.x replaces the NGINX Ingress Controller for NBS7 Kubernetes deployments. + +[https://doc.traefik.io/traefik/migrate/nginx-to-traefik/] + +## Deployment + +### AWS (EKS) + +```bash +helm repo add traefik https://traefik.github.io/charts +helm repo update +helm install traefik-crds traefik/traefik-crds --namespace traefik --create-namespace +helm install traefik traefik/traefik --namespace traefik --values values.yaml --skip-crds +``` + +### Azure (AKS) + +```bash +helm repo add traefik https://traefik.github.io/charts +helm repo update +helm install traefik-crds traefik/traefik-crds --namespace traefik --create-namespace +helm install traefik traefik/traefik --namespace traefik --values values-azure.yaml --skip-crds --set service.spec.loadBalancerIP=XX.XX.XX.XX +``` + +## Files + +| File | Description | +|------|-------------| +| `values.yaml` | Helm values for AWS (EKS) with NLB | +| `values-azure.yaml` | Helm values for Azure (AKS) with internal load balancer | + +## Verification + +```bash +kubectl get pods -n traefik +kubectl get svc -n traefik +kubectl get ingressclass +``` \ No newline at end of file diff --git a/charts/traefik/values-azure.yaml b/charts/traefik/values-azure.yaml new file mode 100644 index 000000000..30e2014f7 --- /dev/null +++ b/charts/traefik/values-azure.yaml @@ -0,0 +1,120 @@ +# Traefik Ingress Controller - Azure (AKS) Configuration +# Replaces: charts/nginx-ingress/values-azure.yaml +# Chart: traefik/traefik (v3.6.x) + +deployment: + enabled: true + kind: Deployment + replicas: 1 + podAnnotations: + linkerd.io/inject: enabled + +ingressClass: + enabled: true + isDefaultClass: true + +providers: + kubernetesCRD: + enabled: true + allowCrossNamespace: false + allowEmptyServices: true + kubernetesIngress: + enabled: true + allowEmptyServices: true + publishedService: + enabled: true + +ports: + traefik: + port: 9000 + expose: + default: false + protocol: TCP + + web: + port: 8000 + expose: + default: true + exposedPort: 80 + protocol: TCP + forwardedHeaders: + insecure: true + transport: + respondingTimeouts: + readTimeout: "300s" + writeTimeout: "300s" + idleTimeout: "300s" + + websecure: + port: 8443 + expose: + default: true + exposedPort: 443 + protocol: TCP + http: + tls: {} + forwardedHeaders: + insecure: true + transport: + respondingTimeouts: + readTimeout: "300s" + writeTimeout: "300s" + idleTimeout: "300s" + + metrics: + port: 9100 + expose: + default: false + protocol: TCP + +service: + enabled: true + type: LoadBalancer + spec: + externalTrafficPolicy: "Local" + loadBalancerIP: XX.XX.XX.XX #Populate your private IP here OR by passing it to your helm command + annotations: + #For private IP and private cluster use the following: + service.beta.kubernetes.io/azure-load-balancer-internal: "true" + service.beta.kubernetes.io/azure-load-balancer-internal-health-probe-request-path: "/ping" + prometheus.io/scrape: "true" + prometheus.io/port: "9100" + prometheus.io/path: /metrics + +additionalArguments: + - "--serversTransport.forwardingTimeouts.dialTimeout=300s" + - "--serversTransport.forwardingTimeouts.responseHeaderTimeout=300s" + - "--serversTransport.forwardingTimeouts.idleConnTimeout=300s" + - "--entryPoints.websecure.http.tls" + +metrics: + prometheus: + entryPoint: metrics + addEntryPointsLabels: true + addRoutersLabels: true + addServicesLabels: true + +logs: + general: + level: INFO + access: + enabled: true + +ingressRoute: + dashboard: + enabled: true + entryPoints: ["traefik"] + +resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "500m" + memory: "512Mi" + +rbac: + enabled: true + +# Run the following helm command after updating the load balancer IP: +# helm upgrade --install traefik traefik/traefik --namespace traefik -f values-azure.yaml --set service.spec.loadBalancerIP=XX.XX.XX.XX \ No newline at end of file diff --git a/charts/traefik/values.yaml b/charts/traefik/values.yaml new file mode 100644 index 000000000..d1b77a944 --- /dev/null +++ b/charts/traefik/values.yaml @@ -0,0 +1,124 @@ +# Traefik Ingress Controller - AWS (EKS) Configuration +# Replaces: charts/nginx-ingress/values.yaml +# Chart: traefik/traefik (v3.6.x) + +deployment: + enabled: true + kind: Deployment + replicas: 1 + podAnnotations: + linkerd.io/inject: enabled + +ingressClass: + enabled: true + isDefaultClass: true + +providers: + kubernetesCRD: + enabled: true + allowCrossNamespace: false + allowEmptyServices: true + kubernetesIngress: + enabled: true + allowEmptyServices: true + publishedService: + enabled: true + +ports: + traefik: + port: 9000 + expose: + default: false + protocol: TCP + +# HTTP entrypoint + web: + port: 8000 + expose: + default: true + exposedPort: 80 + protocol: TCP + forwardedHeaders: + insecure: true + transport: + respondingTimeouts: + readTimeout: "300s" + writeTimeout: "300s" + idleTimeout: "300s" + +# HTTPS entrypoint + websecure: + port: 8443 + expose: + default: true + exposedPort: 443 + protocol: TCP + http: + tls: {} + forwardedHeaders: + insecure: true + transport: + respondingTimeouts: + readTimeout: "300s" + writeTimeout: "300s" + idleTimeout: "300s" + +# Metrics port for Prometheus scraping + metrics: + port: 9100 + expose: + default: false + protocol: TCP + +service: + enabled: true + type: LoadBalancer + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: nlb + prometheus.io/scrape: "true" + prometheus.io/port: "9100" + prometheus.io/path: /metrics + # Optional: Assign Elastic IPs to the NLB for static IP addresses + # Allocate EIPs in AWS first, then reference their allocation IDs: + # service.beta.kubernetes.io/aws-load-balancer-eip-allocations: eipalloc-xxxxxxxxx,eipalloc-yyyyyyyyy + # + # Optional: Restrict inbound traffic to specific CIDRs + # service.beta.kubernetes.io/aws-load-balancer-source-ranges: "XX.XX.XX.XX/24" + +additionalArguments: + - "--serversTransport.forwardingTimeouts.dialTimeout=300s" + - "--serversTransport.forwardingTimeouts.responseHeaderTimeout=300s" + - "--serversTransport.forwardingTimeouts.idleConnTimeout=300s" + - "--entryPoints.websecure.http.tls" + +metrics: + prometheus: + entryPoint: metrics + addEntryPointsLabels: true + addRoutersLabels: true + addServicesLabels: true + +logs: + general: + level: INFO + access: + enabled: true + +# DASHBOARD +# Traefik includes a built-in dashboard - useful for debugging routes +# Do NOT expose externally in production +ingressRoute: + dashboard: + enabled: true + entryPoints: ["traefik"] + +resources: + requests: + cpu: "100m" + memory: "128Mi" + limits: + cpu: "500m" + memory: "512Mi" + +rbac: + enabled: true \ No newline at end of file