Gateway api support (#890)

* Gateway api support

Signed-off-by: Kevin Fox <[email protected]>

* Update readme

Signed-off-by: Kevin Fox <[email protected]>

* Fix gateway name. a gateway doesnt need to be named gateway

Signed-off-by: Kevin Fox <[email protected]>

* Fix naming issue

Signed-off-by: Kevin Fox <[email protected]>

* Incorperate feedback

Signed-off-by: Kevin Fox <[email protected]>

* Incorperate feedback

Signed-off-by: Kevin Fox <[email protected]>

---------

Signed-off-by: Kevin Fox <[email protected]>
This commit is contained in:
kfox1111
2026-07-30 19:38:43 +00:00
committed by GitHub
parent 2d92b81ba4
commit 3cfefb72ba
39 changed files with 1015 additions and 5 deletions
@@ -0,0 +1,25 @@
# Test-owned shared Gateway. Created outside the chart so its Envoy Gateway
# data-plane Service (and ClusterIP) exists before the chart is installed,
# letting the harness resolve the test hostnames to it in a single install pass.
# name/namespace match the chart's global reference defaults
# (global.spire.gatewayAPI.gateway.name=spire-gateway, namespace=release ns) so
# the chart's ListenerSets and routes attach to it.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: spire
namespace: spire-server
spec:
gatewayClassName: eg
allowedListeners:
namespaces:
from: All
listeners:
- name: base
protocol: TLS
port: 443
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: All
@@ -0,0 +1,27 @@
# EnvoyProxy forces the per-Gateway data-plane Service to type ClusterIP so the
# integration harness can capture a stable ClusterIP and map the test hostnames
# to it via hostAliases (kind has no LoadBalancer). The GatewayClass wires this
# proxy config in via parametersRef.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: clusterip-proxy
namespace: envoy-gateway-system
spec:
provider:
type: Kubernetes
kubernetes:
envoyService:
type: ClusterIP
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: clusterip-proxy
namespace: envoy-gateway-system
+1 -1
View File
@@ -12,6 +12,6 @@
{
"name": "envoy-gateway",
"registry": "docker.io/envoyproxy/gateway-helm",
"version": "v1.7.2"
"version": "v1.8.2"
}
]
@@ -0,0 +1,12 @@
{{- if .Values.fetchCA.gatewayAPI.enabled -}}
{{- $fullName := printf "%s-fetchca" (include "spiffe-step-ssh.fullname" .) -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.fetchCA.gatewayAPI
"name" $fullName
"namespace" .Release.Namespace
"svcName" $fullName
"port" .Values.fetchCA.service.port
"labels" (include "spiffe-step-ssh.labels" .)
"routeKind" "TLSRoute") }}
{{- end }}
@@ -0,0 +1,12 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $fullName := include "spiffe-step-ssh.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.gatewayAPI
"name" $fullName
"namespace" .Release.Namespace
"svcName" $fullName
"port" .Values.step.service.port
"labels" (include "spiffe-step-ssh.labels" .)
"routeKind" "TLSRoute") }}
{{- end }}
+30
View File
@@ -72,6 +72,21 @@ stepIngress:
# - hosts:
# - spiffe-step-ssh.example.org
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose the step endpoint via Gateway API (TLS passthrough)
enabled: false
## @param gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spiffe-step-ssh"
## @param gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
## @skip step
step:
service:
@@ -233,6 +248,21 @@ fetchCA:
# - hosts:
# - spiffe-step-ssh-fetchca.example.org
gatewayAPI:
## @param fetchCA.gatewayAPI.enabled Flag to expose the fetchCA endpoint via Gateway API (TLS passthrough)
enabled: false
## @param fetchCA.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spiffe-step-ssh-fetchca"
## @param fetchCA.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param fetchCA.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param fetchCA.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param fetchCA.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
## @param fetchCA.autoscaling.enabled Enable autoscaling
## @param fetchCA.autoscaling.minReplicas Minimum number of replicas to deploy
## @param fetchCA.autoscaling.maxReplicas Maximum number of replicas to deploy
+16
View File
@@ -102,6 +102,14 @@ A Helm chart to install the SPIRE Identity Exchange.
| `rest.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `rest.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` |
| `rest.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` |
| `rest.gatewayAPI.enabled` | Flag to expose the REST endpoint via Gateway API | `false` |
| `rest.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-rest` |
| `rest.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` |
| `rest.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `rest.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `rest.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `rest.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `rest.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` |
| `grpc.enabled` | Enable the grpc service | `false` |
| `grpc.service.type` | Service type | `ClusterIP` |
| `grpc.service.port` | port for the service | `443` |
@@ -115,6 +123,14 @@ A Helm chart to install the SPIRE Identity Exchange.
| `grpc.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `grpc.ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` |
| `grpc.ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` |
| `grpc.gatewayAPI.enabled` | Flag to expose the gRPC endpoint via Gateway API | `false` |
| `grpc.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-identity-exchange-grpc` |
| `grpc.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough. | `""` |
| `grpc.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `grpc.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `grpc.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `grpc.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `grpc.gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` |
| `tools.kubectl.image.registry` | The OCI registry to pull the image from | `registry.k8s.io` |
| `tools.kubectl.image.repository` | The repository within the registry | `kubectl` |
| `tools.kubectl.image.pullPolicy` | The image pull policy | `IfNotPresent` |
@@ -0,0 +1,13 @@
{{- if .Values.grpc.gatewayAPI.enabled -}}
{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.grpc.gatewayAPI) -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.grpc.gatewayAPI
"name" (printf "%s-grpc" (include "spire-identity-exchange.fullname" .))
"namespace" (include "spire-identity-exchange.namespace" .)
"svcName" (printf "%s-grpc" (include "spire-identity-exchange.fullname" .))
"port" .Values.grpc.service.port
"labels" (include "spire-identity-exchange.labels" .)
"routeKind" $routeKind
"backendTLS" (eq $routeKind "HTTPRoute")) }}
{{- end }}
@@ -0,0 +1,13 @@
{{- if .Values.rest.gatewayAPI.enabled -}}
{{- $routeKind := include "spire-lib.gateway-route-kind" (dict "gatewayAPI" .Values.rest.gatewayAPI) -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.rest.gatewayAPI
"name" (printf "%s-rest" (include "spire-identity-exchange.fullname" .))
"namespace" (include "spire-identity-exchange.namespace" .)
"svcName" (printf "%s-rest" (include "spire-identity-exchange.fullname" .))
"port" .Values.rest.service.port
"labels" (include "spire-identity-exchange.labels" .)
"routeKind" $routeKind
"backendTLS" (eq $routeKind "HTTPRoute")) }}
{{- end }}
@@ -284,6 +284,31 @@ rest:
# hosts:
# - spire-identity-exchange-rest.example.org
## Gateway API exposure for the REST endpoint. Independent of rest.ingress. The
## backend serves HTTPS, so a set tlsSecret => HTTPRoute + BackendTLSPolicy
## (reencrypt); blank tlsSecret => TLSRoute (SNI passthrough).
gatewayAPI:
## @param rest.gatewayAPI.enabled Flag to expose the REST endpoint via Gateway API
enabled: false
## @param rest.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spire-identity-exchange-rest"
## @param rest.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough.
tlsSecret: ""
## @param rest.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param rest.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param rest.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param rest.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
# BackendTLSPolicy (reencrypt) is emitted automatically for the terminated
# HTTPS backend when gatewayAPI.tlsSecret is set.
backendTLS:
## @param rest.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap.
caCertificateRefs: []
grpc:
## @param grpc.enabled Enable the grpc service
enabled: false
@@ -331,6 +356,31 @@ grpc:
# hosts:
# - spire-identiy-exchange-grpc.example.org
## Gateway API exposure for the gRPC endpoint. Independent of grpc.ingress. The
## backend serves HTTPS, so a set tlsSecret => HTTPRoute + BackendTLSPolicy
## (reencrypt); blank tlsSecret => TLSRoute (SNI passthrough).
gatewayAPI:
## @param grpc.gatewayAPI.enabled Flag to expose the gRPC endpoint via Gateway API
enabled: false
## @param grpc.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spire-identity-exchange-grpc"
## @param grpc.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough.
tlsSecret: ""
## @param grpc.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param grpc.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param grpc.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param grpc.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
# BackendTLSPolicy (reencrypt) is emitted automatically for the terminated
# HTTPS backend when gatewayAPI.tlsSecret is set.
backendTLS:
## @param grpc.gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap.
caCertificateRefs: []
tools:
kubectl:
## @param tools.kubectl.image.registry The OCI registry to pull the image from
+239
View File
@@ -366,3 +366,242 @@ Anything lower has an incompatible API.
{{- print "crt" }}
{{- end }}
{{- end }}
{{/* ---------------------------------------------------------------------------
* Gateway API helpers
*
* Gateway API support is entirely independent of Ingress. It is driven by a
* per-service `gatewayAPI:` values block (sibling of `ingress:`) with its own
* `enabled` flag, so a service can expose both an Ingress and Gateway API
* resources at the same time. The existing ingress helpers above are untouched.
*
* Only Standard-channel, v1 Gateway API resources are used
* (gateway.networking.k8s.io/v1): Gateway, HTTPRoute, TLSRoute, ListenerSet,
* BackendTLSPolicy. Requires Gateway API v1.5+ CRDs pre-installed.
* --------------------------------------------------------------------------- */}}
{{/* Shared Gateway object name (global reference). Input: dict {global} */}}
{{- define "spire-lib.gateway-name" -}}
{{- dig "spire" "gatewayAPI" "gateway" "name" "spire" .global -}}
{{- end }}
{{/* Shared Gateway namespace. Input: dict {global, root}. Defaults to the release namespace. */}}
{{- define "spire-lib.gateway-namespace" -}}
{{- $ns := dig "spire" "gatewayAPI" "gateway" "namespace" "" .global -}}
{{- if $ns -}}{{ $ns }}{{- else -}}{{ .root.Release.Namespace }}{{- end -}}
{{- end }}
{{/* Shared Gateway listener port (global reference, ListenerSets must match). Input: dict {global} */}}
{{- define "spire-lib.gateway-port" -}}
{{- dig "spire" "gatewayAPI" "gateway" "port" 443 .global -}}
{{- end }}
{{/* Resolve whether ListenerSet management is on for a service.
* Input: dict {gatewayAPI, global}. Per-service listenerSet.enabled (null=inherit)
* falls back to global.spire.gatewayAPI.manageListenerSets (default true).
* Returns the string "true" or "false".
*/}}
{{- define "spire-lib.gateway-manage-listenersets" -}}
{{- $ls := dig "listenerSet" "enabled" nil .gatewayAPI -}}
{{- if ne $ls nil -}}
{{- $ls -}}
{{- else -}}
{{- dig "spire" "gatewayAPI" "manageListenerSets" true .global -}}
{{- end -}}
{{- end }}
{{/* Default route kind from tlsSecret. Input: dict {gatewayAPI}. Empty tlsSecret => TLSRoute (passthrough). */}}
{{- define "spire-lib.gateway-route-kind" -}}
{{- if .gatewayAPI.tlsSecret -}}HTTPRoute{{- else -}}TLSRoute{{- end -}}
{{- end }}
{{/* parentRefs list items for a route.
* Input: dict {manageLS(bool), name, gatewayAPI, gwName, gwNS}
* - manageLS on: attach to the service's ListenerSet (kind ListenerSet, sectionName=name)
* - off + gatewayAPI.parentRefs set: use those verbatim
* - off + no override: attach directly to the shared Gateway
*/}}
{{- define "spire-lib.gateway-parentref" -}}
{{- if .manageLS }}
- group: gateway.networking.k8s.io
kind: ListenerSet
name: {{ .name | quote }}
sectionName: {{ .name | quote }}
{{- else if .gatewayAPI.parentRefs }}
{{ toYaml .gatewayAPI.parentRefs }}
{{- else }}
- group: gateway.networking.k8s.io
kind: Gateway
name: {{ .gwName | quote }}
namespace: {{ .gwNS | quote }}
{{- with .gatewayAPI.sectionName }}
sectionName: {{ . | quote }}
{{- end }}
{{- end }}
{{- end }}
{{/* Emit Gateway API resources for one service: a Route, plus (optionally) a
* ListenerSet and a BackendTLSPolicy. Direct analogue of spire-lib.ingress-spec.
* Input dict:
* root - chart root context (.)
* gatewayAPI - the per-service gatewayAPI values block
* name - base name for the emitted resources
* namespace - namespace for the emitted resources
* svcName - backend Service name
* port - backend Service port (number)
* labels - pre-rendered labels YAML (string)
* routeKind - "TLSRoute" or "HTTPRoute"
* backendTLS - bool; when true and HTTPRoute, emit a BackendTLSPolicy (reencrypt)
* path - HTTPRoute path prefix (default "/")
*/}}
{{- define "spire-lib.gateway-routes" -}}
{{- $g := .gatewayAPI -}}
{{- $global := .root.Values.global -}}
{{- $host := include "spire-lib.ingress-calculated-name" (dict "ingress" (dict "host" $g.host) "Values" .root.Values) | trim -}}
{{- $gwName := include "spire-lib.gateway-name" (dict "global" $global) -}}
{{- $gwNS := include "spire-lib.gateway-namespace" (dict "global" $global "root" .root) -}}
{{- $port := include "spire-lib.gateway-port" (dict "global" $global) -}}
{{- $manageLS := eq (include "spire-lib.gateway-manage-listenersets" (dict "gatewayAPI" $g "global" $global)) "true" -}}
{{- $terminate := eq .routeKind "HTTPRoute" -}}
{{- $path := default "/" .path -}}
{{- $parentRefs := include "spire-lib.gateway-parentref" (dict "manageLS" $manageLS "name" .name "gatewayAPI" $g "gwName" $gwName "gwNS" $gwNS) -}}
apiVersion: gateway.networking.k8s.io/v1
kind: {{ .routeKind }}
metadata:
name: {{ .name }}
namespace: {{ .namespace }}
labels:
{{- .labels | nindent 4 }}
{{- with $g.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
parentRefs:
{{- $parentRefs | trim | nindent 4 }}
hostnames:
- {{ $host | quote }}
rules:
{{- if $terminate }}
- matches:
- path:
type: PathPrefix
value: {{ $path | quote }}
backendRefs:
- name: {{ .svcName | quote }}
port: {{ .port }}
{{- else }}
- backendRefs:
- name: {{ .svcName | quote }}
port: {{ .port }}
{{- end }}
{{- if $manageLS }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: ListenerSet
metadata:
name: {{ .name }}
namespace: {{ .namespace }}
labels:
{{- .labels | nindent 4 }}
spec:
parentRef:
group: gateway.networking.k8s.io
kind: Gateway
name: {{ $gwName | quote }}
namespace: {{ $gwNS | quote }}
listeners:
- name: {{ .name }}
hostname: {{ $host | quote }}
port: {{ $port }}
{{- if $terminate }}
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: {{ $g.tlsSecret | quote }}
allowedRoutes:
namespaces:
from: Same
kinds:
- group: gateway.networking.k8s.io
kind: HTTPRoute
{{- else }}
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
kinds:
- group: gateway.networking.k8s.io
kind: TLSRoute
{{- end }}
{{- end }}
{{- if and $terminate .backendTLS }}
---
apiVersion: gateway.networking.k8s.io/v1
kind: BackendTLSPolicy
metadata:
name: {{ .name }}
namespace: {{ .namespace }}
labels:
{{- .labels | nindent 4 }}
spec:
targetRefs:
- group: ""
kind: Service
name: {{ .svcName | quote }}
validation:
hostname: {{ $host | quote }}
caCertificateRefs:
{{- if dig "backendTLS" "caCertificateRefs" (list) $g }}
{{- toYaml $g.backendTLS.caCertificateRefs | nindent 6 }}
{{- else }}
- group: ""
kind: ConfigMap
name: {{ include "spire-lib.bundle-configmap" .root | trim | quote }}
{{- end }}
{{- end }}
{{- end }}
{{/* The shared Gateway object. Rendered only by the umbrella chart.
* Input: dict {root, gatewayObject} where gatewayObject is the umbrella-local
* `gatewayAPI.gateway` values block. name/namespace/port come from the global
* reference; className and listener policy are local.
*/}}
{{- define "spire-lib.gateway-resource" -}}
{{- $global := .root.Values.global -}}
{{- $obj := .gatewayObject -}}
{{- $gwName := include "spire-lib.gateway-name" (dict "global" $global) -}}
{{- $gwNS := include "spire-lib.gateway-namespace" (dict "global" $global "root" .root) -}}
{{- $port := include "spire-lib.gateway-port" (dict "global" $global) -}}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: {{ $gwName }}
namespace: {{ $gwNS }}
{{- with $obj.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
gatewayClassName: {{ required "gatewayAPI.gateway.className is required to render the shared Gateway" $obj.className | quote }}
allowedListeners:
namespaces:
from: {{ default "All" $obj.allowedListenersNamespaces }}
listeners:
- name: base
protocol: TLS
port: {{ $port }}
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: {{ default "All" $obj.allowedRoutesNamespaces }}
{{- with $obj.extraListeners }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
+15
View File
@@ -311,12 +311,27 @@ Now you can interact with the Spire agent socket from your own application. The
| `global.spire.namespaces.server.labels` | Labels to apply to the Spire server Namespace. | `{}` |
| `global.spire.strictMode` | Check values, such as trustDomain, are overridden with a suitable value for production. | `false` |
| `global.spire.ingressControllerType` | Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""]. | `""` |
| `global.spire.gatewayAPI.manageListenerSets` | Default policy for whether services render a ListenerSet for their SNI listener. Each service may override via its gatewayAPI.listenerSet.enabled. | `true` |
| `global.spire.gatewayAPI.gateway.name` | Name of the shared Gateway object that routes and ListenerSets attach to | `spire` |
| `global.spire.gatewayAPI.gateway.namespace` | Namespace of the shared Gateway object. Defaults to the release namespace if blank. | `""` |
| `global.spire.gatewayAPI.gateway.port` | Port the shared Gateway listens on. ListenerSet listeners must match this. | `443` |
| `global.spire.tools.kubectl.tag` | Set to force the tag to use for all kubectl instances | `""` |
| `global.installAndUpgradeHooks.enabled` | Enable Helm hooks to autofix common install/upgrade issues (should be disabled when using `helm template`) | `true` |
| `global.installAndUpgradeHooks.resources` | Resource requests and limits for installAndUpgradeHooks | `{}` |
| `global.deleteHooks.enabled` | Enable Helm hooks to autofix common delete issues (should be disabled when using `helm template`) | `true` |
| `global.deleteHooks.resources` | Resource requests and limits for deleteHooks | `{}` |
### Gateway API parameters
| Name | Description | Value |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `gatewayAPI.gateway.enabled` | Render the shared Gateway object | `false` |
| `gatewayAPI.gateway.className` | gatewayClassName for the shared Gateway (e.g. "eg"). Required when enabled. | `""` |
| `gatewayAPI.gateway.annotations` | Annotations for the Gateway object | `{}` |
| `gatewayAPI.gateway.allowedListenersNamespaces` | From which namespaces ListenerSets may attach to the Gateway. One of All, Same, Selector. | `All` |
| `gatewayAPI.gateway.allowedRoutesNamespaces` | From which namespaces routes may attach directly to the base listener (used when ListenerSet management is off). One of All, Same, Selector. | `All` |
| `gatewayAPI.gateway.extraListeners` | Additional listeners to add to the Gateway | `[]` |
### Spire server parameters
| Name | Description | Value |
@@ -122,6 +122,14 @@ A Helm chart to install the SPIFFE OIDC discovery provider.
| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `ingress.hosts` | Host paths for ingress object. If emtpy, rules will be built based on the host var. | `[]` |
| `ingress.tls` | Secrets containining TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` |
| `gatewayAPI.enabled` | Flag to expose the OIDC discovery provider via Gateway API | `false` |
| `gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `oidc-discovery` |
| `gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination. Blank keeps passthrough when TLS is enabled. | `""` |
| `gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `gatewayAPI.backendTLS.caCertificateRefs` | ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap. | `[]` |
| `tests.hostAliases` | List of host aliases for testing | `[]` |
| `tests.tls.enabled` | Flag for enabling tls for tests | `false` |
| `tests.tls.customCA` | Custom CA value for tests | `""` |
@@ -0,0 +1,18 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $tlsEnabled := eq (include "spiffe-oidc-discovery-provider.tls-enabled" .) "true" -}}
{{- $hasSecret := ne (default "" .Values.gatewayAPI.tlsSecret) "" -}}
{{- $port := $tlsEnabled | ternary .Values.service.ports.https .Values.service.ports.http -}}
{{- $routeKind := (or (not $tlsEnabled) $hasSecret) | ternary "HTTPRoute" "TLSRoute" -}}
{{- $backendTLS := and $tlsEnabled $hasSecret -}}
{{- $fullName := include "spiffe-oidc-discovery-provider.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.gatewayAPI
"name" $fullName
"namespace" (include "spiffe-oidc-discovery-provider.namespace" .)
"svcName" $fullName
"port" $port
"labels" (include "spiffe-oidc-discovery-provider.labels" .)
"routeKind" $routeKind
"backendTLS" $backendTLS) }}
{{- end }}
@@ -9,6 +9,9 @@
{{- if gt (len .Values.ingress.hosts) 0 }}
{{- $host = (index .Values.ingress.hosts 0).host }}
{{- end }}
{{- if .Values.gatewayAPI.enabled }}
{{- $host = include "spire-lib.ingress-calculated-name" (dict "Values" .Values "ingress" (dict "host" .Values.gatewayAPI.host)) | trim }}
{{- end }}
apiVersion: v1
kind: Pod
metadata:
@@ -40,7 +43,7 @@ spec:
args: ['-s', '-f', '-k', '{{ $protocol }}://{{ include "spiffe-oidc-discovery-provider.fullname" . }}.{{ include "spiffe-oidc-discovery-provider.namespace" . }}.svc.{{ include "spire-lib.cluster-domain" . }}:{{ $port }}/.well-known/openid-configuration']
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 8 }}
{{- if .Values.ingress.enabled }}
{{- if or .Values.ingress.enabled .Values.gatewayAPI.enabled }}
- name: curl-ingress
image: {{ template "spire-lib.image" (dict "image" .Values.tests.bash.image "global" .Values.global) }}
command: ['curl']
@@ -345,6 +345,33 @@ ingress:
# hosts:
# - oidc-discovery.example.org
## Gateway API exposure for the OIDC discovery provider. Independent of `ingress`.
## Route kind follows the provider's TLS setting: TLS disabled => HTTPRoute (edge,
## plaintext backend); TLS enabled + tlsSecret => HTTPRoute + BackendTLSPolicy
## (reencrypt to the HTTPS backend); TLS enabled + no tlsSecret => TLSRoute
## (SNI passthrough).
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose the OIDC discovery provider via Gateway API
enabled: false
## @param gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "oidc-discovery"
## @param gatewayAPI.tlsSecret Secret with the TLS cert for edge termination. Blank keeps passthrough when TLS is enabled.
tlsSecret: ""
## @param gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
# BackendTLSPolicy (reencrypt) is emitted automatically when this service
# terminates TLS at the gateway while the backend serves HTTPS.
backendTLS:
## @param gatewayAPI.backendTLS.caCertificateRefs [array] ConfigMap refs holding the backend CA used to validate the re-encrypted connection. Defaults to the SPIRE bundle configmap.
caCertificateRefs: []
tests:
## @param tests.hostAliases [array] List of host aliases for testing
hostAliases: []
@@ -72,3 +72,9 @@ A Helm chart to deploy spike keepers
| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` |
| `gatewayAPI.enabled` | Flag to expose spike-keeper via Gateway API (TLS passthrough) | `false` |
| `gatewayAPI.host` | Host name for the routes. A `-<index>` suffix is added per replica. If no '.' in host, trustDomain is automatically appended. | `keeper` |
| `gatewayAPI.annotations` | Annotations for the routes (and their ListenerSets) | `{}` |
| `gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for each replica's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
@@ -0,0 +1,28 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $root := . -}}
{{- $g := .Values.gatewayAPI -}}
{{- $fullName := include "spike-keeper.fullname" . -}}
{{- $last := sub (.Values.replicas | int) 1 | int -}}
{{- range (seq 0 $last | toString | split " ") }}
{{- $i := . }}
{{- $host := $g.host }}
{{- if contains "." $host }}
{{- $hostParts := regexSplit "[.]" $host 2 }}
{{- $host = printf "%s-%s.%s" (index $hostParts 0) $i (index $hostParts 1) }}
{{- else }}
{{- $host = printf "%s-%s" $host $i }}
{{- end }}
{{- $gi := deepCopy $g }}
{{- $_ := set $gi "host" $host }}
---
{{ include "spire-lib.gateway-routes" (dict
"root" $root
"gatewayAPI" $gi
"name" (printf "%s-%s" $fullName $i)
"namespace" (include "spike-keeper.namespace" $root)
"svcName" (printf "%s-%s" $fullName $i)
"port" $root.Values.service.port
"labels" (include "spike-keeper.labels" $root)
"routeKind" "TLSRoute") }}
{{- end }}
{{- end }}
@@ -141,3 +141,21 @@ ingress:
## @param ingress.tls [array] Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars.
tls: []
## Gateway API exposure for spike-keeper. One route + ListenerSet per replica
## (host suffixed with the replica index, like the ingress). Independent of
## `ingress`. Empty tlsSecret => TLSRoute (SNI passthrough).
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose spike-keeper via Gateway API (TLS passthrough)
enabled: false
## @param gatewayAPI.host Host name for the routes. A `-<index>` suffix is added per replica. If no '.' in host, trustDomain is automatically appended.
host: "keeper"
## @param gatewayAPI.annotations [object] Annotations for the routes (and their ListenerSets)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for each replica's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
@@ -84,6 +84,12 @@ A Helm chart to deploy spike nexus
| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` |
| `gatewayAPI.enabled` | Flag to expose spike-nexus via Gateway API (TLS passthrough) | `false` |
| `gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `nexus` |
| `gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `persistence.type` | What type of volume to use for persistence. Valid options pvc (recommended), hostPath, emptyDir (testing only) | `pvc` |
| `persistence.size` | What size volume to use for persistence | `1Gi` |
| `persistence.accessMode` | What access mode to use for persistence. Valid options are ReadWriteOnce (recommended), ReadWriteOncePod, ReadWriteMany (not recommended) | `ReadWriteOnce` |
@@ -0,0 +1,12 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $fullName := include "spike-nexus.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.gatewayAPI
"name" $fullName
"namespace" (include "spike-nexus.namespace" .)
"svcName" $fullName
"port" .Values.service.port
"labels" (include "spike-nexus.labels" .)
"routeKind" "TLSRoute") }}
{{- end }}
@@ -182,6 +182,23 @@ ingress:
# hosts:
# - nexus.example.org
## Gateway API exposure for spike-nexus. Independent of `ingress`. Empty tlsSecret
## => TLSRoute (SNI passthrough); set tlsSecret => HTTPRoute (edge terminate).
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose spike-nexus via Gateway API (TLS passthrough)
enabled: false
## @param gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "nexus"
## @param gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
## @param persistence.type What type of volume to use for persistence. Valid options pvc (recommended), hostPath, emptyDir (testing only)
## @param persistence.size What size volume to use for persistence
## @param persistence.accessMode What access mode to use for persistence. Valid options are ReadWriteOnce (recommended), ReadWriteOncePod, ReadWriteMany (not recommended)
@@ -191,6 +191,12 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr
| `federation.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `federation.ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `federation.ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` |
| `federation.gatewayAPI.enabled` | Flag to expose the federation endpoint via Gateway API (TLS passthrough) | `false` |
| `federation.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-server-federation` |
| `federation.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `federation.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `federation.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `federation.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `caSubject.country` | Country for Spire server CA | `ARPA` |
| `caSubject.organization` | Organization for Spire server CA | `Example` |
| `caSubject.commonName` | Common Name for Spire server CA | `example.org` |
@@ -426,6 +432,12 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr
| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` |
| `gatewayAPI.enabled` | Flag to expose spire-server via Gateway API (TLS passthrough) | `false` |
| `gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `spire-server` |
| `gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `extraEnv` | Extra environment variables to add to the spire server | `[]` |
| `extraVolumes` | Extra volumes to be mounted | `[]` |
| `extraVolumeMounts` | Extra volume mounts | `[]` |
@@ -548,6 +560,13 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr
| `tornjak.ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `tornjak.ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `tornjak.ingress.tls` | Secrets containing TLS certs to enable https on ingress. If emtpy, rules will be built based on the host and tlsSecret vars. | `[]` |
| `tornjak.gatewayAPI.enabled` | Flag to expose the Tornjak backend via Gateway API | `false` |
| `tornjak.gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `tornjak-backend` |
| `tornjak.gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination (used when connectionType is http). Blank keeps passthrough. | `""` |
| `tornjak.gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `tornjak.gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `tornjak.gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `tornjak.gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `tornjak.startupProbe.failureThreshold` | Failure threshold count | `3` |
| `tornjak.startupProbe.initialDelaySeconds` | Initial delay seconds | `5` |
| `tornjak.startupProbe.periodSeconds` | Period seconds | `10` |
@@ -364,10 +364,15 @@ The code below determines what connection type should be used.
{{- define "spire-server.test.federation-ingress-args" }}
{{- $args := list }}
{{- $host := include "spire-lib.ingress-calculated-name" (dict "Values" .Values "ingress" .Values.federation.ingress) }}
{{- $host := "" }}
{{- if .host }}
{{- $host = .host }}
{{- else }}
{{- $host = include "spire-lib.ingress-calculated-name" (dict "Values" .Values "ingress" .Values.federation.ingress) }}
{{- if gt (len .Values.federation.ingress.tls) 0 }}
{{- $host = index (index (index .Values.federation.ingress.tls 0) "hosts") 0 }}
{{- end }}
{{- end }}
{{- if dig "tests" "tls" "enabled" false .Values }}
{{- if ne (len (dig "tests" "tls" "customCA" "" .Values)) 0 }}
{{- $args = append $args "--cacert" }}
@@ -0,0 +1,14 @@
{{- if .Values.federation.enabled -}}
{{- if .Values.federation.gatewayAPI.enabled -}}
{{- $fullName := include "spire-server.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.federation.gatewayAPI
"name" (printf "%s-federation" $fullName)
"namespace" (include "spire-server.namespace" .)
"svcName" $fullName
"port" .Values.federation.bundleEndpoint.port
"labels" (include "spire-server.labels" .)
"routeKind" "TLSRoute") }}
{{- end }}
{{- end }}
@@ -0,0 +1,12 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $fullName := include "spire-server.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.gatewayAPI
"name" $fullName
"namespace" (include "spire-server.namespace" .)
"svcName" $fullName
"port" .Values.service.port
"labels" (include "spire-server.labels" .)
"routeKind" "TLSRoute") }}
{{- end }}
@@ -56,6 +56,21 @@ spec:
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 8 }}
{{- end }}
{{- if .Values.federation.gatewayAPI.enabled }}
{{- $gwHost := include "spire-lib.ingress-calculated-name" (dict "Values" .Values "ingress" (dict "host" .Values.federation.gatewayAPI.host)) | trim }}
- name: federation-gateway
image: {{ template "spire-lib.image" (dict "image" .Values.tests.bash.image "global" .Values.global) }}
command: ['curl']
args:
{{ include "spire-server.test.federation-ingress-args" (dict "Values" $values "host" $gwHost) | nindent 8 }}
{{- if ne (len (dig "tests" "tls" "customCA" "" $values)) 0 }}
volumeMounts:
- name: custom-ca
mountPath: /ca
{{- end }}
securityContext:
{{- include "spire-lib.securitycontext" . | nindent 8 }}
{{- end }}
{{- if ne (len (dig "tests" "hostAliases" "" $values)) 0 }}
hostAliases:
{{- toYaml .Values.tests.hostAliases | nindent 4 }}
@@ -0,0 +1,15 @@
{{- if .Values.tornjak.gatewayAPI.enabled -}}
{{- $isHttp := eq (include "spire-tornjak.connectionType" .) "http" -}}
{{- $routeKind := $isHttp | ternary "HTTPRoute" "TLSRoute" -}}
{{- $port := $isHttp | ternary .Values.tornjak.service.ports.http .Values.tornjak.service.ports.https -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.tornjak.gatewayAPI
"name" (include "spire-tornjak.fullname" .)
"namespace" (include "spire-server.namespace" .)
"svcName" (include "spire-tornjak.servicename" .)
"port" $port
"labels" (include "spire-server.labels" .)
"routeKind" $routeKind
"backendTLS" false) }}
{{- end }}
@@ -343,6 +343,21 @@ federation:
# If Profile Type == https_web:
# secretName: spire-server-federation-tls
gatewayAPI:
## @param federation.gatewayAPI.enabled Flag to expose the federation endpoint via Gateway API (TLS passthrough)
enabled: false
## @param federation.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spire-server-federation"
## @param federation.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param federation.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param federation.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param federation.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
caSubject:
## @param caSubject.country Country for Spire server CA
country: ARPA
@@ -1056,6 +1071,21 @@ ingress:
# hosts:
# - spire-server.example.org
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose spire-server via Gateway API (TLS passthrough)
enabled: false
## @param gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "spire-server"
## @param gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
## @param extraEnv [array] Extra environment variables to add to the spire server
extraEnv: []
@@ -1358,6 +1388,25 @@ tornjak:
# hosts:
# - tornjak-backend.example.org
## Gateway API exposure for the Tornjak backend. Route kind follows the tornjak
## connectionType: non-http => TLSRoute (passthrough), http => HTTPRoute (edge).
gatewayAPI:
## @param tornjak.gatewayAPI.enabled Flag to expose the Tornjak backend via Gateway API
enabled: false
## @param tornjak.gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "tornjak-backend"
## @param tornjak.gatewayAPI.tlsSecret Secret with the TLS cert for edge termination (used when connectionType is http). Blank keeps passthrough.
tlsSecret: ""
## @param tornjak.gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param tornjak.gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param tornjak.gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param tornjak.gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
## @param tornjak.startupProbe.failureThreshold Failure threshold count
## @param tornjak.startupProbe.initialDelaySeconds Initial delay seconds
## @param tornjak.startupProbe.periodSeconds Period seconds
@@ -98,6 +98,13 @@ port forwarding. See the chart NOTES output for more details.
| `ingress.tlsSecret` | Secret that has the certs. If blank will use default certs. Used with host var. | `""` |
| `ingress.hosts` | Host paths for ingress object. If empty, rules will be built based on the host var. | `[]` |
| `ingress.tls` | Secrets containing TLS certs to enable https on ingress. If empty, rules will be built based on the host and tlsSecret vars. | `[]` |
| `gatewayAPI.enabled` | Flag to expose the Tornjak frontend via Gateway API | `false` |
| `gatewayAPI.host` | Host name for the route. If no '.' in host, trustDomain is automatically appended. | `tornjak-frontend` |
| `gatewayAPI.tlsSecret` | Secret with the TLS cert for edge termination | `""` |
| `gatewayAPI.annotations` | Annotations for the route (and its ListenerSet) | `{}` |
| `gatewayAPI.listenerSet.enabled` | Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets. | `nil` |
| `gatewayAPI.parentRefs` | parentRefs used when ListenerSet management is disabled (direct attach) | `[]` |
| `gatewayAPI.sectionName` | Listener sectionName override when attaching directly to a Gateway | `""` |
| `tests.bash.image.registry` | The OCI registry to pull the image from | `cgr.dev` |
| `tests.bash.image.repository` | The repository within the registry | `chainguard/bash` |
| `tests.bash.image.pullPolicy` | The image pull policy | `IfNotPresent` |
@@ -0,0 +1,13 @@
{{- if .Values.gatewayAPI.enabled -}}
{{- $fullName := include "tornjak-frontend.fullname" . -}}
{{- include "spire-lib.gateway-routes" (dict
"root" .
"gatewayAPI" .Values.gatewayAPI
"name" $fullName
"namespace" (include "tornjak-frontend.namespace" .)
"svcName" $fullName
"port" .Values.service.port
"labels" (include "tornjak-frontend.labels" .)
"routeKind" "HTTPRoute"
"backendTLS" false) }}
{{- end }}
@@ -151,6 +151,26 @@ ingress:
# hosts:
# - tornjak-frontend.example.org
## Gateway API exposure for the Tornjak frontend UI. Independent of `ingress`.
## The frontend serves plaintext HTTP, so this is always an HTTPRoute with edge
## TLS termination at the Gateway; set tlsSecret to supply the listener cert.
gatewayAPI:
## @param gatewayAPI.enabled Flag to expose the Tornjak frontend via Gateway API
enabled: false
## @param gatewayAPI.host Host name for the route. If no '.' in host, trustDomain is automatically appended.
host: "tornjak-frontend"
## @param gatewayAPI.tlsSecret Secret with the TLS cert for edge termination
tlsSecret: ""
## @param gatewayAPI.annotations [object] Annotations for the route (and its ListenerSet)
annotations: {}
listenerSet:
## @param gatewayAPI.listenerSet.enabled Manage a ListenerSet for this service's SNI listener. Null inherits global.spire.gatewayAPI.manageListenerSets.
enabled: null
## @param gatewayAPI.parentRefs [array] parentRefs used when ListenerSet management is disabled (direct attach)
parentRefs: []
## @param gatewayAPI.sectionName Listener sectionName override when attaching directly to a Gateway
sectionName: ""
tests:
bash:
## @param tests.bash.image.registry The OCI registry to pull the image from
+3
View File
@@ -0,0 +1,3 @@
{{- if .Values.gatewayAPI.gateway.enabled }}
{{- include "spire-lib.gateway-resource" (dict "root" . "gatewayObject" .Values.gatewayAPI.gateway) }}
{{- end }}
+37
View File
@@ -84,6 +84,22 @@ global:
## @param global.spire.ingressControllerType Specify what type of ingress controller you're using to add the necessary annotations accordingly. If blank, autodetection is attempted. If other, no annotations will be added. Must be one of [ingress-nginx, openshift, other, ""].
ingressControllerType: ""
## Gateway API reference settings shared across charts. Only the values needed
## by other charts to attach their routes/ListenerSets to the shared Gateway
## live here; the Gateway object itself is configured under the top-level
## `gatewayAPI.gateway` block of this (umbrella) chart. Gateway API support is
## independent of ingress and can be enabled alongside it.
gatewayAPI:
## @param global.spire.gatewayAPI.manageListenerSets Default policy for whether services render a ListenerSet for their SNI listener. Each service may override via its gatewayAPI.listenerSet.enabled.
manageListenerSets: true
gateway:
## @param global.spire.gatewayAPI.gateway.name Name of the shared Gateway object that routes and ListenerSets attach to
name: spire
## @param global.spire.gatewayAPI.gateway.namespace Namespace of the shared Gateway object. Defaults to the release namespace if blank.
namespace: ""
## @param global.spire.gatewayAPI.gateway.port Port the shared Gateway listens on. ListenerSet listeners must match this.
port: 443
tools:
kubectl:
## @param global.spire.tools.kubectl.tag Set to force the tag to use for all kubectl instances
@@ -129,6 +145,27 @@ global:
# namespace: "kube-prometheus-system"
# labels: {}
## @section Gateway API parameters
## Configuration for the optional shared Gateway object managed by this umbrella
## chart. The Gateway is rendered here (rather than in a subchart) so it exists
## even when spire-server is disabled. name/namespace/port come from
## global.spire.gatewayAPI.gateway; className and listener policy are local.
##
gatewayAPI:
gateway:
## @param gatewayAPI.gateway.enabled Render the shared Gateway object
enabled: false
## @param gatewayAPI.gateway.className gatewayClassName for the shared Gateway (e.g. "eg"). Required when enabled.
className: ""
## @param gatewayAPI.gateway.annotations [object] Annotations for the Gateway object
annotations: {}
## @param gatewayAPI.gateway.allowedListenersNamespaces From which namespaces ListenerSets may attach to the Gateway. One of All, Same, Selector.
allowedListenersNamespaces: All
## @param gatewayAPI.gateway.allowedRoutesNamespaces From which namespaces routes may attach directly to the base listener (used when ListenerSet management is off). One of All, Same, Selector.
allowedRoutesNamespaces: All
## @param gatewayAPI.gateway.extraListeners [array] Additional listeners to add to the Gateway
extraListeners: []
## subcharts
## @section Spire server parameters
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env bash
set -xe
# Gateway API integration test. Mirrors tests/integration/production but exposes
# services through the Kubernetes Gateway API (Envoy Gateway) instead of
# ingress-nginx:
# - spire-server and the federation endpoint use TLS passthrough (TLSRoute)
# - the OIDC discovery provider uses HTTPS termination at the edge (HTTPRoute)
# Both share port 443 and are disambiguated by SNI via per-service ListenerSets.
#
# The shared Gateway object is created by this harness (see gateway-eg.yaml)
# rather than by the chart, so its data-plane Service ClusterIP is known before
# the chart is installed and the whole thing installs in a single pass.
#
# The released chart has no Gateway API support, so the production `-u`
# upgrade-from-release path is intentionally omitted here.
SCRIPT="$(readlink -f "$0")"
SCRIPTPATH="$(dirname "${SCRIPT}")"
TESTDIR="${SCRIPTPATH}/../../../.github/tests"
DEPS="${TESTDIR}/dependencies"
# shellcheck source=/dev/null
source "${SCRIPTPATH}/../../../.github/scripts/parse-versions.sh"
# shellcheck source=/dev/null
source "${TESTDIR}/common.sh"
"${SCRIPTPATH}/../../../.github/scripts/prepare-local-chart-deps.sh"
CLEANUP=1
for i in "$@"; do
case $i in
-c)
CLEANUP=0
shift # past argument=value
;;
esac
done
teardown() {
print_helm_releases
print_spire_workload_status spire-server spire-system
if [[ "$1" -ne 0 ]]; then
get_namespace_details spire-server spire-system
fi
if [ "${CLEANUP}" -eq 1 ]; then
helm uninstall --namespace spire-server spire 2>/dev/null || true
kubectl delete ns spire-server 2>/dev/null || true
kubectl delete ns spire-system 2>/dev/null || true
helm uninstall --namespace cert-manager cert-manager 2>/dev/null || true
kubectl delete ns cert-manager 2>/dev/null || true
kubectl delete -f "${DEPS}/gateway-eg.yaml" 2>/dev/null || true
kubectl delete -f "${DEPS}/gatewayclass-eg.yaml" 2>/dev/null || true
helm uninstall --namespace envoy-gateway-system eg 2>/dev/null || true
kubectl delete ns envoy-gateway-system 2>/dev/null || true
# Leave the Gateway API CRDs installed; removing them is unnecessary and
# would disrupt anything else on a shared cluster.
fi
}
trap 'EC=$? && trap - SIGTERM && teardown $EC' SIGINT SIGTERM EXIT
kubectl create namespace spire-system 2>/dev/null || true
kubectl label namespace spire-system pod-security.kubernetes.io/enforce=privileged || true
kubectl create namespace spire-server 2>/dev/null || true
kubectl label namespace spire-server pod-security.kubernetes.io/enforce=restricted || true
helm upgrade --install --create-namespace cert-manager cert-manager \
--version "$VERSION_CERT_MANAGER" --repo "$HELM_REPO_CERT_MANAGER" \
--namespace cert-manager \
--set installCRDs=true \
--wait
kubectl apply -f "${DEPS}/testcert.yaml" -n spire-server
# Install the Envoy Gateway control plane. Its gateway-crds-helm dependency also
# installs the Gateway API CRDs (bundled v1.5.1), including the standard v1
# ListenerSet, TLSRoute and BackendTLSPolicy we rely on — so no separate CRD
# apply is needed. Pin the standard channel explicitly.
helm upgrade --install --create-namespace eg "$HELM_REGISTRY_ENVOY_GATEWAY" \
--version "$VERSION_ENVOY_GATEWAY" \
--namespace envoy-gateway-system \
--set gateway-crds-helm.crds.gatewayAPI.channel=standard \
--wait
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available
# GatewayClass (eg) + an EnvoyProxy that forces the data-plane Service to
# ClusterIP so the hostAliases DNS trick works on kind, then the shared Gateway
# object itself. Creating the Gateway here (not in the chart) makes Envoy Gateway
# provision the data-plane Service up front so we can capture its ClusterIP.
kubectl apply -f "${DEPS}/gatewayclass-eg.yaml"
kubectl apply -f "${DEPS}/gateway-eg.yaml"
# Capture the Envoy Gateway data-plane Service ClusterIP. Envoy Gateway labels
# the Service with the owning Gateway (spire in spire-server).
echo "Waiting for the Envoy Gateway data-plane Service to be provisioned..."
ip=""
for _ in $(seq 1 60); do
ip=$(kubectl -n envoy-gateway-system get svc \
-l "gateway.envoyproxy.io/owning-gateway-namespace=spire-server,gateway.envoyproxy.io/owning-gateway-name=spire" \
-o jsonpath='{.items[0].spec.clusterIP}' 2>/dev/null || true)
if [[ -n "$ip" && "$ip" != "None" ]]; then
break
fi
sleep 5
done
if [[ -z "$ip" || "$ip" == "None" ]]; then
echo "Failed to obtain Envoy Gateway data-plane Service ClusterIP"
exit 1
fi
echo "$ip" spire-server.production.other oidc-discovery.production.other spire-server-federation.production.other
cat > /tmp/dummydns <<EOF
spiffe-oidc-discovery-provider:
tests:
hostAliases:
- ip: "$ip"
hostnames:
- "oidc-discovery.production.other"
spire-agent:
hostAliases:
- ip: "$ip"
hostnames:
- "spire-server.production.other"
spire-server:
tests:
hostAliases:
- ip: "$ip"
hostnames:
- "spire-server-federation.production.other"
EOF
# The Gateway (and its data-plane Service) already exist, so the hostnames
# resolve immediately and the chart installs in a single pass.
helm upgrade --install --create-namespace spire charts/spire \
--namespace spire-server \
--values "${COMMON_TEST_YOUR_VALUES}" \
--values "${SCRIPTPATH}/values-expose-spire-server-gateway-api.yaml" \
--values "${SCRIPTPATH}/values-expose-spiffe-oidc-discovery-provider-gateway-api.yaml" \
--values "${SCRIPTPATH}/values-expose-federation-https-web-gateway-api.yaml" \
--values /tmp/dummydns \
--set spiffe-oidc-discovery-provider.tests.tls.customCA=tls-cert,spire-server.tests.tls.customCA=tls-cert \
--set spire-agent.server.address=spire-server.production.other,spire-agent.server.port=443 \
--set spire-server.federation.tls.externalSecret.secretName=tls-cert \
--wait
helm test --namespace spire-server spire
if helm get manifest -n spire-server spire | grep -i example; then
echo Global settings did not work. Please fix.
exit 1
fi
@@ -0,0 +1,9 @@
# Alternate federation profile (https_spiffe): the federation endpoint is
# exposed via Gateway API with TLS passthrough of the pod's SPIFFE-authenticated
# endpoint. Provided for a targeted passthrough-federation test; NOT referenced
# by run-tests.sh by default (mirrors the production https_spiffe variant).
spire-server:
federation:
enabled: true
gatewayAPI:
enabled: true
@@ -0,0 +1,18 @@
# Expose the federation bundle endpoint via Gateway API. The federation pod
# serves a web-PKI cert (externalSecret), so the Gateway uses TLS passthrough
# (empty gatewayAPI.tlsSecret => TLSRoute + a passthrough SNI listener) and the
# client validates the pod's cert directly. This is the https_web profile.
spire-server:
tests:
tls:
enabled: true
federation:
enabled: true
tls:
spire:
enabled: false
externalSecret:
enabled: true
secretName: spire-server-federation-tls
gatewayAPI:
enabled: true
@@ -0,0 +1,15 @@
# Expose the OIDC discovery provider via Gateway API with HTTPS termination at
# the edge. The provider's SPIRE TLS is disabled so it serves plaintext behind
# the Gateway; the Gateway terminates TLS with tls-cert (set gatewayAPI.tlsSecret)
# => HTTPRoute + a terminating SNI listener. The helm test curls the discovery
# document over HTTPS and validates the gateway cert against the tls-cert CA.
spiffe-oidc-discovery-provider:
tls:
spire:
enabled: false
tests:
tls:
enabled: true
gatewayAPI:
enabled: true
tlsSecret: tls-cert
@@ -0,0 +1,7 @@
# Expose spire-server via Gateway API using TLS passthrough (mTLS terminated at
# the pod). Empty gatewayAPI.tlsSecret => TLSRoute + a passthrough SNI listener.
# The shared Gateway object is created by the test harness (not the chart), so
# gatewayAPI.gateway.enabled is left at its default (false).
spire-server:
gatewayAPI:
enabled: true