diff --git a/charts/server/templates/registrar/httproute.yaml b/charts/server/templates/registrar/httproute.yaml new file mode 100644 index 000000000..bf404fc39 --- /dev/null +++ b/charts/server/templates/registrar/httproute.yaml @@ -0,0 +1,38 @@ +{{- $ingress := mergeOverwrite (deepCopy .Values.global.ingress) .Values.common.ingress .Values.registrar.ingress }} +{{- if and .Values.registrar.enabled (eq $ingress.routingType "httproute") -}} +{{- + $host := ternary + $ingress.baseDomain + (printf "%s.%s" (tpl $ingress.subdomain .) $ingress.baseDomain) + $ingress.subdomainAsPathPrefix +}} +{{- + $pathPrefix := ternary + (tpl $ingress.subdomain . | printf "/%s") + "" + $ingress.subdomainAsPathPrefix +}} +{{- $ingressTLSRequired := and $ingress.tls.enabled (not $ingress.tls.terminatedAtProxy) }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "zenith.componentname" (list . "registrar") }} + labels: {{ include "zenith.componentLabels" (list . "registrar") | nindent 4 }} +spec: + parentRefs: + - name: {{ required "global.ingress.gateway.name is required when routingType is httproute" $ingress.gateway.name }} + namespace: {{ required "global.ingress.gateway.namespace is required when routingType is httproute" $ingress.gateway.namespace }} + hostnames: + - {{ default $host $ingress.host | quote }} + rules: + - matches: + - path: + type: PathPrefix + value: {{ printf "%s/associate" $pathPrefix | quote }} + timeouts: + request: {{ $ingress.timeouts.request }} + backendRequest: {{ $ingress.timeouts.backendRequest }} + backendRefs: + - name: {{ include "zenith.componentname" (list . "registrar") }} + port: 80 +{{- end }} diff --git a/charts/server/templates/registrar/ingress.yaml b/charts/server/templates/registrar/ingress.yaml index 09af353e5..4fdad8afc 100644 --- a/charts/server/templates/registrar/ingress.yaml +++ b/charts/server/templates/registrar/ingress.yaml @@ -1,4 +1,5 @@ -{{- if .Values.registrar.enabled -}} +{{- $ingress := mergeOverwrite (deepCopy .Values.global.ingress) .Values.common.ingress .Values.registrar.ingress }} +{{- if and .Values.registrar.enabled (eq $ingress.routingType "ingress") -}} {{- $global := deepCopy .Values.global.ingress }} {{- $common := deepCopy .Values.common.ingress }} {{- $registrar := deepCopy .Values.registrar.ingress }} diff --git a/charts/server/values.yaml b/charts/server/values.yaml index 74938eb2f..7489569b2 100644 --- a/charts/server/values.yaml +++ b/charts/server/values.yaml @@ -8,7 +8,22 @@ global: baseDomain: # Indicates whether the subdomain should be used as a path prefix subdomainAsPathPrefix: false - # The ingress class to use + # The routing type to use for the registrar ingress resource. + # 'ingress' uses a standard Kubernetes Ingress (for nginx-ingress-controller). + # 'httproute' uses a Gateway API HTTPRoute (for Traefik with kubernetesGateway + # provider or any Gateway API compatible controller). + routingType: ingress + # Gateway API configuration (used when routingType is 'httproute') + gateway: + # The name of the Gateway resource to attach HTTPRoutes to + name: "" + # The namespace of the Gateway resource + namespace: "" + # Default timeouts for Gateway API routes (used when routingType is 'httproute') + timeouts: + request: 600s + backendRequest: 600s + # The ingress class to use (only used when routingType is 'ingress') className: nginx # The common annotations for all ingress resources annotations: {} diff --git a/sync/chart/templates/ingress-oidc.yaml b/sync/chart/templates/ingress-oidc.yaml index 14c179296..5e2d9d174 100644 --- a/sync/chart/templates/ingress-oidc.yaml +++ b/sync/chart/templates/ingress-oidc.yaml @@ -4,6 +4,7 @@ .Values.ingress.enabled .Values.oidc.enabled (not .Values.oidc.ingress.enabled) + (eq .Values.ingress.routingType "ingress") }} {{- $ingressTLSRequired := and .Values.global.secure (not .Values.ingress.tls.terminatedAtProxy) }} apiVersion: networking.k8s.io/v1 diff --git a/sync/chart/templates/ingress.yaml b/sync/chart/templates/ingress.yaml index 4ef90f2e2..8bcde19ef 100644 --- a/sync/chart/templates/ingress.yaml +++ b/sync/chart/templates/ingress.yaml @@ -2,6 +2,7 @@ if and (mustRegexMatch "^https?$" .Values.protocol) .Values.ingress.enabled + (eq .Values.ingress.routingType "ingress") }} apiVersion: networking.k8s.io/v1 kind: Ingress diff --git a/sync/chart/templates/ingressroute-oidc.yaml b/sync/chart/templates/ingressroute-oidc.yaml new file mode 100644 index 000000000..8695f8da6 --- /dev/null +++ b/sync/chart/templates/ingressroute-oidc.yaml @@ -0,0 +1,49 @@ +{{- + if and + (mustRegexMatch "^https?$" .Values.protocol) + .Values.ingress.enabled + .Values.oidc.enabled + (eq .Values.ingress.routingType "ingressroute") +}} +{{- $host := include "zenith-service.ingress.host" . }} +{{- $prefix := tpl (index .Values.oidc.extraArgs "proxy-prefix") . }} +{{- $secure := and .Values.global.secure (not .Values.ingress.tls.terminatedAtProxy) }} +{{- $oidcServiceName := printf "%s-oidc" .Release.Name }} +--- +# ForwardAuth middleware pointing to the oauth2-proxy sidecar +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: {{ .Release.Name }}-oidc-auth + labels: {{ include "zenith-service.labels" . | nindent 4 }} +spec: + forwardAuth: + address: >- + http://{{ $oidcServiceName }}.{{ .Release.Namespace }}.svc.cluster.local{{ $prefix }}/auth + {{- with .Values.oidc.alphaConfig.configData.injectResponseHeaders }} + authResponseHeaders: + {{- range . }} + - {{ .name }} + {{- end }} + {{- end }} +--- +# IngressRoute for the oauth2-proxy endpoint (no auth middleware — it IS the auth provider) +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }}-oidc + labels: {{ include "zenith-service.labels" . | nindent 4 }} +spec: + entryPoints: + - {{ if .Values.global.secure }}websecure{{ else }}web{{ end }} + routes: + - match: Host(`{{ $host }}`) && PathPrefix(`{{ $prefix }}`) + kind: Rule + services: + - name: {{ $oidcServiceName }} + port: http + {{- if $secure }} + tls: + secretName: {{ include "zenith-service.ingress.tls.secretName" . }} + {{- end }} +{{- end }} diff --git a/sync/chart/templates/ingressroute.yaml b/sync/chart/templates/ingressroute.yaml new file mode 100644 index 000000000..05fc2e63d --- /dev/null +++ b/sync/chart/templates/ingressroute.yaml @@ -0,0 +1,32 @@ +{{- + if and + (mustRegexMatch "^https?$" .Values.protocol) + .Values.ingress.enabled + (eq .Values.ingress.routingType "ingressroute") +}} +{{- $host := include "zenith-service.ingress.host" . }} +{{- $path := include "zenith-service.ingress.pathPrefix" . }} +{{- $secure := and .Values.global.secure (not .Values.ingress.tls.terminatedAtProxy) }} +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: {{ .Release.Name }} + labels: {{ include "zenith-service.labels" . | nindent 4 }} +spec: + entryPoints: + - {{ if .Values.global.secure }}websecure{{ else }}web{{ end }} + routes: + - match: Host(`{{ $host }}`) && PathPrefix(`{{ $path }}`) + kind: Rule + {{- if .Values.oidc.enabled }} + middlewares: + - name: {{ .Release.Name }}-oidc-auth + {{- end }} + services: + - name: {{ .Release.Name }} + port: dynamic + {{- if $secure }} + tls: + secretName: {{ include "zenith-service.ingress.tls.secretName" . }} + {{- end }} +{{- end }} diff --git a/sync/chart/values.yaml b/sync/chart/values.yaml index 7c351ee52..7ce59c8e0 100644 --- a/sync/chart/values.yaml +++ b/sync/chart/values.yaml @@ -26,7 +26,11 @@ readTimeout: ingress: # Indicates if the ingress should be enabled or not enabled: true - # The ingress class name to use + # The routing type to use + # 'ingress' uses a standard Kubernetes Ingress (for nginx-ingress-controller) + # 'ingressroute' uses a Traefik IngressRoute CRD (for Traefik with kubernetesCRD provider) + routingType: ingress + # The ingress class name to use (only used when routingType is 'ingress') className: nginx # The annotations to use with the ingress annotations: diff --git a/sync/zenith/sync/config.py b/sync/zenith/sync/config.py index 2e343ba4a..219a08d97 100644 --- a/sync/zenith/sync/config.py +++ b/sync/zenith/sync/config.py @@ -160,6 +160,10 @@ class IngressConfig(Section): subdomain_as_path_prefix: bool = False #: Annotations to add to all ingress resources annotations: dict[str, str] = Field(default_factory=dict) + #: The routing type to use for exposing services. + #: Use 'ingress' for standard Kubernetes Ingress (nginx-ingress-controller) + #: or 'ingressroute' for Traefik IngressRoute CRD (kubernetesCRD provider). + routing_type: str = "ingress" #: The TLS configuration tls: TLSConfig = Field(default_factory=TLSConfig) #: The OIDC configuration diff --git a/sync/zenith/sync/processor/helm.py b/sync/zenith/sync/processor/helm.py index 7be8df60f..cd1a0191d 100644 --- a/sync/zenith/sync/processor/helm.py +++ b/sync/zenith/sync/processor/helm.py @@ -185,6 +185,7 @@ def _get_service_values(self, service: model.Service) -> dict[str, typing.Any]: "protocol": service.config.get("backend-protocol", "http"), "ingress": { "annotations": self.config.ingress.annotations, + "routingType": self.config.ingress.routing_type, }, } read_timeout = service.config.get("read-timeout")