[linstor-gui] Add Keycloak-protected Ingress for the UI (#2390)
## What this PR does Stacked on top of #2382. Publishes the linstor-gui UI on `https://linstor-gui.<root-host>` behind the cluster Keycloak realm using the same oauth2-proxy gatekeeper pattern that the dashboard uses, so the LINSTOR REST API is never exposed unauthenticated through Ingress. The Ingress is rendered only when **both** gates are satisfied: - `_cluster.oidc-enabled == "true"` - `linstor-gui` is listed in `_cluster.expose-services` (i.e. in `publishing.exposedServices` in the core cozystack values) If OIDC is disabled cluster-wide we intentionally do **not** ship an unauthenticated ``token-proxy`` fallback — unlike dashboard, there is no reason to front a raw storage-management API with k8s bearer tokens. Operators can still reach the UI via \`kubectl port-forward\` to the ClusterIP service. ## What's new - \`templates/gatekeeper.yaml\` — oauth2-proxy Deployment (OIDC mode), upstream \`linstor-gui.<ns>.svc:80\` - \`templates/gatekeeper-svc.yaml\` — ClusterIP :8000 in front of it - \`templates/gatekeeper-sa.yaml\` — dedicated SA, no auto-token - \`templates/keycloakclient.yaml\` — persistent \`linstor-gui-client\` + \`linstor-gui-auth-config\` Secrets, \`KeycloakClient\` CRD that auto-provisions the OIDC client with \`redirectUris: [/oauth2/callback/*]\` - \`templates/ingress.yaml\` — Ingress to the gatekeeper Service with cert-manager ClusterIssuer, gated on expose-services + oidc-enabled - \`tests/ingress_auth_test.yaml\` — unit tests for each conditional branch, KeycloakClient rendering, oauth2-proxy args README updated with Option 1 (Keycloak-protected Ingress) and Option 2 (port-forward). ### Release note \`\`\`release-note Added an opt-in Keycloak-protected Ingress for the linstor-gui package. Add \`linstor-gui\` to \`publishing.exposedServices\` to publish \`https://linstor-gui.<root-host>\` behind the cluster OIDC realm. \`\`\` ## Test plan - [ ] Deploy to dev10 with \`linstor-gui\` added to \`publishing.exposedServices\` - [ ] Verify cert-manager issues the TLS certificate - [ ] Open \`https://linstor-gui.<root-host>\` in a browser, confirm Keycloak login challenge - [ ] After login, confirm LINSTOR node list loads via the nginx → mTLS proxy - [ ] Verify helm-unittest cases pass in CI - [ ] Sanity-check that when \`oidc-enabled=false\` or the service is not in \`expose-services\`, the Ingress + gatekeeper resources are skipped
This commit is contained in:
commit
cbc79600dd
9 changed files with 545 additions and 4 deletions
|
|
@ -186,6 +186,23 @@ spec:
|
|||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
# Allow the tenant's ingress-nginx to reach both the linstor-gui
|
||||
# gatekeeper pods (for /oauth2/* and app traffic) AND the transient
|
||||
# ACME http-01 solver pods that cert-manager spins up in the same
|
||||
# namespace during certificate issuance/renewal. Matching the whole
|
||||
# namespace mirrors allow-to-dashboard / allow-to-keycloak.
|
||||
name: allow-to-linstor-gui
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
spec:
|
||||
endpointSelector: {}
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": cozy-linstor
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: allow-to-keycloak
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,28 @@ using mTLS with the `linstor-client-tls` secret created by the `linstor` package
|
|||
|
||||
## Exposing the UI
|
||||
|
||||
This package only creates a `ClusterIP` Service. It does **not** ship an ingress,
|
||||
because authentication depends on the deployment's Keycloak / OIDC setup and
|
||||
LINSTOR's controller API is a privileged cluster-wide storage management
|
||||
surface. Cluster admins should wire up ingress + auth explicitly, for example:
|
||||
### Option 1 — Keycloak-protected Ingress (recommended)
|
||||
|
||||
The chart ships an `oauth2-proxy` based gatekeeper plus a `KeycloakClient` CRD
|
||||
so the UI can be published on `linstor-gui.<root-host>` behind the cluster
|
||||
Keycloak realm. Access is granted to any user who can authenticate against the
|
||||
`cozy` realm.
|
||||
|
||||
To turn it on, add `linstor-gui` to `publishing.exposedServices` in the core
|
||||
`cozystack` values (same list that controls `dashboard`). OIDC must be
|
||||
enabled (`authentication.oidc.enabled: true`) — if it is not, the Ingress and
|
||||
gatekeeper Deployment are deliberately **not** rendered, because the LINSTOR
|
||||
REST API surface must not be exposed unauthenticated.
|
||||
|
||||
Once enabled, the UI is reachable at `https://linstor-gui.<root-host>` and
|
||||
authentication is delegated to Keycloak via the `linstor-gui` client
|
||||
(auto-provisioned through the `KeycloakClient` CRD; the client secret is
|
||||
persisted in the `linstor-gui-client` Secret in `cozy-linstor`).
|
||||
|
||||
### Option 2 — Port-forward
|
||||
|
||||
If you have not set up Keycloak or want ad-hoc access, use the `ClusterIP`
|
||||
Service:
|
||||
|
||||
```bash
|
||||
kubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80
|
||||
|
|
|
|||
|
|
@ -60,6 +60,38 @@ data:
|
|||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Lock the linstor-gui in-app authentication panel.
|
||||
#
|
||||
# Unlike most LINSTOR features, the GUI's "authenticationEnabled"
|
||||
# toggle and user list are NOT controller-side properties — they
|
||||
# are persisted as ordinary KeyValueStore instances named
|
||||
# `__gui__settings` and `__gui__users`. The SPA reads them on
|
||||
# every page load and, if `authenticationEnabled=true`, gates the
|
||||
# whole UI behind its own login screen against a self-managed
|
||||
# admin user.
|
||||
#
|
||||
# Cozystack already enforces authentication at the Ingress
|
||||
# (oauth2-proxy + Keycloak), and the in-app login is a foot-gun:
|
||||
# if a user enables it and forgets/misconfigures the password, the
|
||||
# only recovery is shelling into the linstor-controller pod and
|
||||
# running `linstor key-value-store modify __gui__settings
|
||||
# authenticationEnabled false`.
|
||||
#
|
||||
# We allow GETs (so the SPA can read state and render normally
|
||||
# with auth=off) but reject every mutating method on those two
|
||||
# KeyValueStore instances. Any other key-value-store entry the
|
||||
# GUI uses (e.g. __gui__mode) keeps working as expected.
|
||||
location ~ ^/v1/key-value-store/__gui__(settings|users)(/|$) {
|
||||
default_type application/json;
|
||||
# `if ... return` is the one documented-safe use of `if` in
|
||||
# nginx (see "If is Evil"), so it's fine here. We allow GET/HEAD
|
||||
# for the SPA's read path and reject every mutating method.
|
||||
if ($request_method !~ ^(GET|HEAD)$) {
|
||||
return 403 '{"ret_code":-1,"message":"linstor-gui in-app authentication is disabled by cozystack: authentication is enforced at the Ingress via Keycloak."}';
|
||||
}
|
||||
proxy_pass {{ .Values.linstor.endpoint }};
|
||||
}
|
||||
|
||||
# Proxy LINSTOR REST API over mTLS to the controller
|
||||
location /v1 {
|
||||
proxy_pass {{ .Values.linstor.endpoint }};
|
||||
|
|
|
|||
13
packages/system/linstor-gui/templates/gatekeeper-sa.yaml
Normal file
13
packages/system/linstor-gui/templates/gatekeeper-sa.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
|
||||
{{- if eq $oidcEnabled "true" }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: linstor-gui-gatekeeper
|
||||
labels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: cozystack
|
||||
automountServiceAccountToken: false
|
||||
{{- end }}
|
||||
22
packages/system/linstor-gui/templates/gatekeeper-svc.yaml
Normal file
22
packages/system/linstor-gui/templates/gatekeeper-svc.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
|
||||
{{- if eq $oidcEnabled "true" }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: linstor-gui-gatekeeper
|
||||
labels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: cozystack
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: ingress
|
||||
port: 8000
|
||||
protocol: TCP
|
||||
targetPort: 8000
|
||||
selector:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
{{- end }}
|
||||
142
packages/system/linstor-gui/templates/gatekeeper.yaml
Normal file
142
packages/system/linstor-gui/templates/gatekeeper.yaml
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
{{- $host := index .Values._cluster "root-host" }}
|
||||
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
|
||||
{{- $oidcInsecureSkipVerify := index .Values._cluster "oidc-insecure-skip-verify" }}
|
||||
{{- $keycloakInternalUrl := index .Values._cluster "keycloak-internal-url" | default "" }}
|
||||
|
||||
{{- /*
|
||||
Gatekeeper (oauth2-proxy) fronts the linstor-gui Service and handles the
|
||||
OIDC flow against the cluster Keycloak realm. The Ingress below points at
|
||||
this Service — not at linstor-gui directly — so every external request
|
||||
authenticates before hitting nginx/mTLS→controller.
|
||||
|
||||
Only rendered when oidc-enabled=true. If the cluster has OIDC disabled we
|
||||
deliberately do not ship an unauthenticated token-proxy fallback (unlike
|
||||
dashboard): linstor-gui surfaces raw storage state and should not be
|
||||
reachable via Ingress without Keycloak login.
|
||||
*/ -}}
|
||||
{{- if eq $oidcEnabled "true" }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: linstor-gui-gatekeeper
|
||||
labels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: cozystack
|
||||
annotations:
|
||||
reloader.stakater.com/auto: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
spec:
|
||||
serviceAccountName: linstor-gui-gatekeeper
|
||||
automountServiceAccountToken: false
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: linstor-gui
|
||||
app.kubernetes.io/name: gatekeeper
|
||||
topologyKey: kubernetes.io/hostname
|
||||
containers:
|
||||
- name: auth-proxy
|
||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.12.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --provider=oidc
|
||||
- --upstream=http://linstor-gui.{{ .Release.Namespace }}.svc:80
|
||||
- --http-address=0.0.0.0:8000
|
||||
- --redirect-url=https://linstor-gui.{{ $host }}/oauth2/callback
|
||||
- --oidc-issuer-url=https://keycloak.{{ $host }}/realms/cozy
|
||||
{{- if $keycloakInternalUrl }}
|
||||
- --skip-oidc-discovery
|
||||
- --login-url=https://keycloak.{{ $host }}/realms/cozy/protocol/openid-connect/auth
|
||||
- --redeem-url={{ $keycloakInternalUrl }}/protocol/openid-connect/token
|
||||
- --oidc-jwks-url={{ $keycloakInternalUrl }}/protocol/openid-connect/certs
|
||||
- --validate-url={{ $keycloakInternalUrl }}/protocol/openid-connect/userinfo
|
||||
- --backend-logout-url={{ $keycloakInternalUrl }}/protocol/openid-connect/logout?id_token_hint={id_token}
|
||||
{{- else }}
|
||||
- --backend-logout-url=https://keycloak.{{ $host }}/realms/cozy/protocol/openid-connect/logout?id_token_hint={id_token}
|
||||
{{- end }}
|
||||
- --whitelist-domain=keycloak.{{ $host }}
|
||||
- --email-domain=*
|
||||
- --pass-access-token=true
|
||||
- --pass-authorization-header=true
|
||||
- --cookie-refresh=3m
|
||||
- --cookie-name=kc-access
|
||||
- --cookie-secure=true
|
||||
- --cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET)
|
||||
- --skip-provider-button
|
||||
- --scope=openid email profile offline_access
|
||||
{{- if eq $oidcInsecureSkipVerify "true" }}
|
||||
- --ssl-insecure-skip-verify=true
|
||||
{{- end }}
|
||||
env:
|
||||
- name: OAUTH2_PROXY_CLIENT_ID
|
||||
value: linstor-gui
|
||||
- name: OAUTH2_PROXY_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: linstor-gui-client
|
||||
key: client-secret-key
|
||||
- name: OAUTH2_PROXY_COOKIE_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: linstor-gui-auth-config
|
||||
key: cookieSecret
|
||||
ports:
|
||||
- name: proxy
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: proxy
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: proxy
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
{{- end }}
|
||||
54
packages/system/linstor-gui/templates/ingress.yaml
Normal file
54
packages/system/linstor-gui/templates/ingress.yaml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{{- $solver := (index .Values._cluster "solver") | default "http01" }}
|
||||
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
|
||||
{{- $host := index .Values._cluster "root-host" }}
|
||||
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
|
||||
{{- $exposeServices := splitList "," ((index .Values._cluster "expose-services") | default "") }}
|
||||
{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }}
|
||||
|
||||
{{- /*
|
||||
Only publish an Ingress when the operator has opted this service in AND
|
||||
OIDC is enabled cluster-wide. If OIDC is off we deliberately do not
|
||||
expose linstor-gui: without Keycloak there is no authentication layer in
|
||||
front of the LINSTOR REST API proxy. Users can still reach the UI via
|
||||
`kubectl port-forward` to the ClusterIP service.
|
||||
*/ -}}
|
||||
{{- if and (has "linstor-gui" $exposeServices) (eq $oidcEnabled "true") }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: linstor-gui-ingress
|
||||
labels:
|
||||
{{- include "linstor-gui.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $exposeIngress }}
|
||||
{{- end }}
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: 100m
|
||||
# Keycloak access+refresh+id tokens make the oauth2-proxy session
|
||||
# cookie ~8–10 KB (split across multiple Set-Cookie headers), which
|
||||
# overflows the default proxy_buffer_size and causes "upstream sent
|
||||
# too big header" -> HTTP 502 on /oauth2/callback. Same numbers as
|
||||
# dashboard ingress.
|
||||
nginx.ingress.kubernetes.io/proxy-buffer-size: 100m
|
||||
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: {{ $exposeIngress }}
|
||||
rules:
|
||||
- host: linstor-gui.{{ $host }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: linstor-gui-gatekeeper
|
||||
port:
|
||||
number: 8000
|
||||
tls:
|
||||
- hosts:
|
||||
- linstor-gui.{{ $host }}
|
||||
secretName: linstor-gui-ingress-tls
|
||||
{{- end }}
|
||||
72
packages/system/linstor-gui/templates/keycloakclient.yaml
Normal file
72
packages/system/linstor-gui/templates/keycloakclient.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
{{- $host := index .Values._cluster "root-host" }}
|
||||
|
||||
{{- /*
|
||||
Persist client + cookie secrets across upgrades: if the Secrets already
|
||||
exist, reuse their values; otherwise generate new random ones. The
|
||||
KeycloakClient CRD references the same client secret so Keycloak stays
|
||||
in sync with what oauth2-proxy reads.
|
||||
*/ -}}
|
||||
{{- $existingClientSecret := lookup "v1" "Secret" .Release.Namespace "linstor-gui-client" }}
|
||||
{{- $clientSecret := "" }}
|
||||
{{- if $existingClientSecret }}
|
||||
{{- $clientSecret = index $existingClientSecret.data "client-secret-key" | b64dec }}
|
||||
{{- else }}
|
||||
{{- $clientSecret = randAlphaNum 32 }}
|
||||
{{- end }}
|
||||
|
||||
{{- $existingAuthConfig := lookup "v1" "Secret" .Release.Namespace "linstor-gui-auth-config" }}
|
||||
{{- $cookieSecret := "" }}
|
||||
{{- if $existingAuthConfig }}
|
||||
{{- $cookieSecret = index $existingAuthConfig.data "cookieSecret" | b64dec }}
|
||||
{{- else }}
|
||||
{{- $cookieSecret = randAlphaNum 16 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: linstor-gui-client
|
||||
labels:
|
||||
{{- include "linstor-gui.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
client-secret-key: {{ $clientSecret | b64enc }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: linstor-gui-auth-config
|
||||
labels:
|
||||
{{- include "linstor-gui.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
cookieSecret: {{ $cookieSecret | b64enc }}
|
||||
{{- if .Capabilities.APIVersions.Has "v1.edp.epam.com/v1" }}
|
||||
---
|
||||
apiVersion: v1.edp.epam.com/v1
|
||||
kind: KeycloakClient
|
||||
metadata:
|
||||
name: linstor-gui-client
|
||||
annotations:
|
||||
# Re-reconcile the Keycloak client if the random secret regenerates.
|
||||
secret-hash: {{ $clientSecret | sha256sum }}
|
||||
spec:
|
||||
realmRef:
|
||||
name: keycloakrealm-cozy
|
||||
kind: ClusterKeycloakRealm
|
||||
secret: $linstor-gui-client:client-secret-key
|
||||
advancedProtocolMappers: true
|
||||
name: linstor-gui
|
||||
clientId: linstor-gui
|
||||
directAccess: true
|
||||
public: false
|
||||
webUrl: "https://linstor-gui.{{ $host }}"
|
||||
defaultClientScopes:
|
||||
- groups
|
||||
optionalClientScopes:
|
||||
- offline_access
|
||||
attributes:
|
||||
post.logout.redirect.uris: "+"
|
||||
redirectUris:
|
||||
- "https://linstor-gui.{{ $host }}/oauth2/callback/*"
|
||||
{{- end }}
|
||||
171
packages/system/linstor-gui/tests/ingress_auth_test.yaml
Normal file
171
packages/system/linstor-gui/tests/ingress_auth_test.yaml
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
suite: linstor-gui ingress + keycloak auth
|
||||
templates:
|
||||
- templates/ingress.yaml
|
||||
- templates/gatekeeper.yaml
|
||||
- templates/gatekeeper-svc.yaml
|
||||
- templates/gatekeeper-sa.yaml
|
||||
- templates/keycloakclient.yaml
|
||||
|
||||
release:
|
||||
name: linstor-gui
|
||||
namespace: cozy-linstor
|
||||
|
||||
tests:
|
||||
- it: does not render an Ingress when OIDC is disabled cluster-wide
|
||||
template: templates/ingress.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.org
|
||||
oidc-enabled: "false"
|
||||
expose-services: "linstor-gui"
|
||||
expose-ingress: "tenant-root"
|
||||
issuer-name: "letsencrypt-prod"
|
||||
solver: "http01"
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
- it: does not render an Ingress when linstor-gui is not in expose-services
|
||||
template: templates/ingress.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.org
|
||||
oidc-enabled: "true"
|
||||
expose-services: "dashboard,api"
|
||||
expose-ingress: "tenant-root"
|
||||
issuer-name: "letsencrypt-prod"
|
||||
solver: "http01"
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
- it: renders an Ingress to the gatekeeper service when exposed and OIDC enabled
|
||||
template: templates/ingress.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: dev10.example.org
|
||||
oidc-enabled: "true"
|
||||
expose-services: "dashboard,linstor-gui"
|
||||
expose-ingress: "tenant-root"
|
||||
issuer-name: "letsencrypt-prod"
|
||||
solver: "http01"
|
||||
asserts:
|
||||
- isKind:
|
||||
of: Ingress
|
||||
- equal:
|
||||
path: spec.ingressClassName
|
||||
value: tenant-root
|
||||
- equal:
|
||||
path: spec.rules[0].host
|
||||
value: linstor-gui.dev10.example.org
|
||||
- equal:
|
||||
path: spec.rules[0].http.paths[0].backend.service.name
|
||||
value: linstor-gui-gatekeeper
|
||||
- equal:
|
||||
path: spec.rules[0].http.paths[0].backend.service.port.number
|
||||
value: 8000
|
||||
- equal:
|
||||
path: metadata.annotations["cert-manager.io/cluster-issuer"]
|
||||
value: letsencrypt-prod
|
||||
|
||||
- it: does not render the gatekeeper Deployment when OIDC is disabled
|
||||
template: templates/gatekeeper.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.org
|
||||
oidc-enabled: "false"
|
||||
expose-services: "linstor-gui"
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 0
|
||||
|
||||
- it: renders an oauth2-proxy Deployment pointing at the cluster Keycloak realm
|
||||
template: templates/gatekeeper.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: dev10.example.org
|
||||
oidc-enabled: "true"
|
||||
oidc-insecure-skip-verify: "false"
|
||||
keycloak-internal-url: ""
|
||||
expose-services: "linstor-gui"
|
||||
asserts:
|
||||
- isKind:
|
||||
of: Deployment
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: linstor-gui-gatekeeper
|
||||
- matchRegex:
|
||||
path: spec.template.spec.containers[0].image
|
||||
pattern: "^quay\\.io/oauth2-proxy/oauth2-proxy:"
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: --provider=oidc
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: --upstream=http://linstor-gui.cozy-linstor.svc:80
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: --redirect-url=https://linstor-gui.dev10.example.org/oauth2/callback
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].args
|
||||
content: --oidc-issuer-url=https://keycloak.dev10.example.org/realms/cozy
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: OAUTH2_PROXY_CLIENT_ID
|
||||
value: linstor-gui
|
||||
- equal:
|
||||
path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
|
||||
value: true
|
||||
|
||||
- it: creates client + cookie Secrets and a KeycloakClient CRD
|
||||
template: templates/keycloakclient.yaml
|
||||
capabilities:
|
||||
apiVersions:
|
||||
- v1.edp.epam.com/v1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: dev10.example.org
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 3
|
||||
- documentIndex: 0
|
||||
isKind:
|
||||
of: Secret
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: linstor-gui-client
|
||||
- documentIndex: 1
|
||||
isKind:
|
||||
of: Secret
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: linstor-gui-auth-config
|
||||
- documentIndex: 2
|
||||
isKind:
|
||||
of: KeycloakClient
|
||||
- documentIndex: 2
|
||||
equal:
|
||||
path: spec.clientId
|
||||
value: linstor-gui
|
||||
- documentIndex: 2
|
||||
contains:
|
||||
path: spec.redirectUris
|
||||
content: "https://linstor-gui.dev10.example.org/oauth2/callback/*"
|
||||
|
||||
- it: skips the KeycloakClient CRD when the API is absent, still creates Secrets
|
||||
template: templates/keycloakclient.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.org
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
- documentIndex: 0
|
||||
isKind:
|
||||
of: Secret
|
||||
- documentIndex: 1
|
||||
isKind:
|
||||
of: Secret
|
||||
Loading…
Add table
Add a link
Reference in a new issue