Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hack/dev-manifests/uds-operator-config-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ metadata:
type: Opaque
stringData:
devMode: "active"
KEYCLOAK_CLIENT_MODE: "CLIENT_SECRET"
16 changes: 16 additions & 0 deletions schemas/clusterconfig-v1alpha1.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,25 @@
"type": "string",
"description": "Domain all cluster services on the admin gateway will be exposed on"
},
"adminContextPath": {
"type": "string",
"description": "Optional admin URL context path appended after contextPath when pathRouting is enabled"
},
"contextPath": {
"type": "string",
"description": "Optional public URL context path used when pathRouting is enabled, for example /bar"
},
"domain": {
"type": "string",
"description": "Domain all cluster services will be exposed on"
},
"pathRouting": {
"type": "boolean",
"description": "Route built-in public and admin services on a single host using URL paths"
},
"subdomain": {
"type": "string",
"description": "Optional subdomain used with domain for single-host path routing, for example foo in foo.uds.dev"
}
},
"required": [
Expand Down
67 changes: 67 additions & 0 deletions src/grafana/chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Create the name of the service account to use
{{- fail "Missing remoteSelector or remoteNamespace for internal PostgreSQL." -}}
{{- end }}
{{- end }}

{{- if (empty (compact (values (omit .Values.postgresql "port" "internal" "ssl_mode")))) -}}
{{- if .Values.postgresql.internal.enabled -}}
{{- fail "Missing configuration for internal postgres host, database, user, and password." -}}
Expand All @@ -97,3 +98,69 @@ Create the name of the service account to use
{{- default "true" "" }}
{{- end }}
{{- end }}

{{- define "grafana.pathRouting.enabled" -}}
{{- eq (toString .Values.pathRouting) "true" -}}
{{- end -}}

{{- define "grafana.contextPath" -}}
{{- $path := .Values.contextPath | default "" -}}
{{- if or (eq $path "") (eq $path "/") (hasPrefix "###ZARF_VAR_" $path) -}}
{{- "" -}}
{{- else -}}
{{- $withLeading := ternary $path (printf "/%s" $path) (hasPrefix "/" $path) -}}
{{- trimSuffix "/" $withLeading -}}
{{- end -}}
{{- end -}}

{{- define "grafana.adminContextPath" -}}
{{- $path := .Values.adminContextPath | default "/admin" -}}
{{- if hasPrefix "###ZARF_VAR_" $path -}}
{{- "/admin" -}}
{{- else -}}
{{- $withLeading := ternary $path (printf "/%s" $path) (hasPrefix "/" $path) -}}
{{- trimSuffix "/" $withLeading -}}
{{- end -}}
{{- end -}}

{{- define "grafana.host" -}}
{{- $subdomain := .Values.subdomain | default "" -}}
{{- if or (eq $subdomain "") (hasPrefix "###ZARF_VAR_" $subdomain) -}}
{{- .Values.domain -}}
{{- else -}}
{{- printf "%s.%s" $subdomain .Values.domain -}}
{{- end -}}
{{- end -}}

{{- define "grafana.adminHost" -}}
{{- $subdomain := .Values.subdomain | default "" -}}
{{- if or (eq $subdomain "") (hasPrefix "###ZARF_VAR_" $subdomain) -}}
{{- include "grafana.adminDomain" . -}}
{{- else -}}
{{- printf "%s.%s" $subdomain (include "grafana.adminDomain" .) -}}
{{- end -}}
{{- end -}}

{{- define "grafana.adminDomain" -}}
{{- if .Values.adminDomain -}}
{{- tpl .Values.adminDomain . -}}
{{- else -}}
{{- printf "admin.%s" .Values.domain -}}
{{- end -}}
{{- end -}}

{{- define "grafana.externalUrl" -}}
{{- if eq (include "grafana.pathRouting.enabled" .) "true" -}}
{{- printf "https://%s%s%s/grafana" (include "grafana.adminHost" .) (include "grafana.contextPath" .) (include "grafana.adminContextPath" .) -}}
{{- else -}}
{{- printf "https://grafana.%s" (include "grafana.adminDomain" .) -}}
{{- end -}}
{{- end -}}

{{- define "grafana.ssoUrl" -}}
{{- if eq (include "grafana.pathRouting.enabled" .) "true" -}}
{{- printf "https://%s%s/sso" (include "grafana.host" .) (include "grafana.contextPath" .) -}}
{{- else -}}
{{- printf "https://sso.%s" .Values.domain -}}
{{- end -}}
{{- end -}}
19 changes: 10 additions & 9 deletions src/grafana/chart/templates/uds-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,8 @@ spec:
- name: Grafana
clientId: uds-core-admin-grafana
redirectUris:
{{- if .Values.adminDomain }}
- "https://grafana.{{ .Values.adminDomain }}/login/generic_oauth"
- "https://grafana.{{ .Values.adminDomain }}/login"
{{- else }}
- "https://grafana.admin.{{ .Values.domain }}/login/generic_oauth"
- "https://grafana.admin.{{ .Values.domain }}/login"
{{- end }}
- "{{ include "grafana.externalUrl" . }}/login/generic_oauth"
- "{{ include "grafana.externalUrl" . }}/login"
{{- if .Values.sso.groups }}
groups:
anyOf:
Expand All @@ -43,10 +38,17 @@ spec:
- service: grafana
selector:
app.kubernetes.io/name: grafana
host: grafana
host: {{ .Values.subdomain | default "grafana" }}
gateway: admin
port: 80
targetPort: 3000
{{- if eq (include "grafana.pathRouting.enabled" .) "true" }}
advancedHTTP:
match:
- name: grafana-admin-path
uri:
prefix: {{ printf "%s%s/grafana" (include "grafana.contextPath" .) (include "grafana.adminContextPath" .) | quote }}
{{- end }}
{{- if .Values.uptime.enabled }}
uptime:
checks:
Expand Down Expand Up @@ -133,4 +135,3 @@ spec:
{{- with .Values.additionalNetworkAllow }}
{{ toYaml . | nindent 6 }}
{{- end }}

21 changes: 20 additions & 1 deletion src/grafana/chart/tests/grafana_sso_groups_test.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2025 Defense Unicorns
# Copyright 2025-2026 Defense Unicorns
# SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial

# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
Expand Down Expand Up @@ -26,3 +26,22 @@ tests:
- "/UDS Core/Admin"
- "/UDS Core/Auditor"
- "/UDS Core/Test"

- it: should render path-routed redirect URIs and admin route
set:
pathRouting: true
subdomain: foo
contextPath: /bar
adminContextPath: /admin
template: uds-package.yaml
asserts:
- equal:
path: spec.sso[0].redirectUris
value:
- https://foo.admin.example.com/bar/admin/grafana/login/generic_oauth
- https://foo.admin.example.com/bar/admin/grafana/login
- equal:
path: spec.network.expose[0].advancedHTTP.match[0].uri.prefix
value: /bar/admin/grafana
- notExists:
path: spec.network.expose[0].advancedHTTP.rewrite
1 change: 0 additions & 1 deletion src/grafana/chart/tests/grafana_uptime_toggle_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ tests:
path: spec.network.expose
any: true
content:
host: grafana
gateway: admin
service: grafana
port: 80
Expand Down
4 changes: 4 additions & 0 deletions src/grafana/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

domain: "###ZARF_VAR_DOMAIN###"
adminDomain: "###ZARF_VAR_ADMIN_DOMAIN###"
subdomain: "###ZARF_VAR_SUBDOMAIN###"
contextPath: "###ZARF_VAR_CONTEXT_PATH###"
adminContextPath: "###ZARF_VAR_ADMIN_CONTEXT_PATH###"
pathRouting: "###ZARF_VAR_PATH_ROUTING###"

# optional extra datasources to be added to configmap
extraDatasources: []
Expand Down
11 changes: 6 additions & 5 deletions src/grafana/values/values.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2024 Defense Unicorns
# Copyright 2024-2026 Defense Unicorns
# SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial

sidecar:
Expand Down Expand Up @@ -47,7 +47,8 @@ extraConfigmapMounts:

grafana.ini:
server:
root_url: https://grafana.{{ "###ZARF_VAR_ADMIN_DOMAIN###" | default "admin.###ZARF_VAR_DOMAIN###" }}
root_url: '{{ if eq (toString "###ZARF_VAR_PATH_ROUTING###") "true" }}https://{{ if and "###ZARF_VAR_SUBDOMAIN###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_SUBDOMAIN###")) }}###ZARF_VAR_SUBDOMAIN###.{{ end }}{{ "###ZARF_VAR_ADMIN_DOMAIN###" | default "admin.###ZARF_VAR_DOMAIN###" }}{{ if and "###ZARF_VAR_CONTEXT_PATH###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_CONTEXT_PATH###")) }}###ZARF_VAR_CONTEXT_PATH###{{ end }}{{ "###ZARF_VAR_ADMIN_CONTEXT_PATH###" | default "/admin" }}/grafana{{ else }}https://grafana.{{ "###ZARF_VAR_ADMIN_DOMAIN###" | default "admin.###ZARF_VAR_DOMAIN###" }}{{ end }}'
serve_from_sub_path: '{{ eq (toString "###ZARF_VAR_PATH_ROUTING###") "true" }}'
# Disable telemetry that doesn't function in the airgap
analytics:
reporting_enabled: false
Expand All @@ -70,10 +71,10 @@ grafana.ini:
login_attribute_path: preferred_username
name_attribute_path: name
name: UDS Identity Service
auth_url: https://sso.###ZARF_VAR_DOMAIN###/realms/uds/protocol/openid-connect/auth
auth_url: '{{ if eq (toString "###ZARF_VAR_PATH_ROUTING###") "true" }}https://{{ if and "###ZARF_VAR_SUBDOMAIN###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_SUBDOMAIN###")) }}###ZARF_VAR_SUBDOMAIN###.{{ end }}###ZARF_VAR_DOMAIN###{{ if and "###ZARF_VAR_CONTEXT_PATH###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_CONTEXT_PATH###")) }}###ZARF_VAR_CONTEXT_PATH###{{ end }}/sso{{ else }}https://sso.###ZARF_VAR_DOMAIN###{{ end }}/realms/uds/protocol/openid-connect/auth'

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisit and cleanup?

# Note: this has to be the external URL to ensure that the token issuer checks in Grafana line up with this URL
token_url: https://sso.###ZARF_VAR_DOMAIN###/realms/uds/protocol/openid-connect/token
signout_redirect_url: https://sso.###ZARF_VAR_DOMAIN###/realms/uds/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2Fgrafana.admin.###ZARF_VAR_DOMAIN###%2Flogin
token_url: '{{ if eq (toString "###ZARF_VAR_PATH_ROUTING###") "true" }}https://{{ if and "###ZARF_VAR_SUBDOMAIN###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_SUBDOMAIN###")) }}###ZARF_VAR_SUBDOMAIN###.{{ end }}###ZARF_VAR_DOMAIN###{{ if and "###ZARF_VAR_CONTEXT_PATH###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_CONTEXT_PATH###")) }}###ZARF_VAR_CONTEXT_PATH###{{ end }}/sso{{ else }}https://sso.###ZARF_VAR_DOMAIN###{{ end }}/realms/uds/protocol/openid-connect/token'
signout_redirect_url: '{{ if eq (toString "###ZARF_VAR_PATH_ROUTING###") "true" }}https://{{ if and "###ZARF_VAR_SUBDOMAIN###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_SUBDOMAIN###")) }}###ZARF_VAR_SUBDOMAIN###.{{ end }}###ZARF_VAR_DOMAIN###{{ if and "###ZARF_VAR_CONTEXT_PATH###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_CONTEXT_PATH###")) }}###ZARF_VAR_CONTEXT_PATH###{{ end }}/sso/realms/uds/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2F{{ if and "###ZARF_VAR_SUBDOMAIN###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_SUBDOMAIN###")) }}###ZARF_VAR_SUBDOMAIN###.{{ end }}{{ "###ZARF_VAR_ADMIN_DOMAIN###" | default "admin.###ZARF_VAR_DOMAIN###" }}{{ if and "###ZARF_VAR_CONTEXT_PATH###" (not (hasPrefix "###ZARF_VAR_" "###ZARF_VAR_CONTEXT_PATH###")) }}{{ replace "/" "%2F" "###ZARF_VAR_CONTEXT_PATH###" }}{{ end }}{{ replace "/" "%2F" ("###ZARF_VAR_ADMIN_CONTEXT_PATH###" | default "/admin") }}%2Fgrafana%2Flogin{{ else }}https://sso.###ZARF_VAR_DOMAIN###/realms/uds/protocol/openid-connect/logout?post_logout_redirect_uri=https%3A%2F%2Fgrafana.admin.###ZARF_VAR_DOMAIN###%2Flogin{{ end }}'
use_refresh_token: true
allow_sign_up: true
# Require a UDS Core group to access Grafana
Expand Down
17 changes: 17 additions & 0 deletions src/grafana/zarf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ variables:

- name: ADMIN_DOMAIN
description: "Domain for admin services, defaults to `admin.DOMAIN`"
default: "admin.uds.dev"

- name: SUBDOMAIN
description: "Optional subdomain for single-host path routing"
default: ""

- name: CONTEXT_PATH
description: "Optional public context path for single-host path routing"
default: ""

- name: ADMIN_CONTEXT_PATH
description: "Optional admin context path for single-host path routing"
default: "/admin"

- name: PATH_ROUTING
description: "Whether to route built-in services on one host using URL paths"
default: "false"

components:
- name: grafana
Expand Down
83 changes: 82 additions & 1 deletion src/keycloak/chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,88 @@ Create the service DNS name.
{{ include "keycloak.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}
{{- end }}

{{- define "keycloak.pathRouting.enabled" -}}
{{- eq (toString .Values.pathRouting) "true" -}}
{{- end -}}

{{- define "keycloak.contextPath" -}}
{{- $path := .Values.contextPath | default "" -}}
{{- if or (eq $path "") (eq $path "/") (hasPrefix "###ZARF_VAR_" $path) -}}
{{- "" -}}
{{- else -}}
{{- $withLeading := ternary $path (printf "/%s" $path) (hasPrefix "/" $path) -}}
{{- trimSuffix "/" $withLeading -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.adminContextPath" -}}
{{- $path := .Values.adminContextPath | default "/admin" -}}
{{- if hasPrefix "###ZARF_VAR_" $path -}}
{{- "/admin" -}}
{{- else -}}
{{- $withLeading := ternary $path (printf "/%s" $path) (hasPrefix "/" $path) -}}
{{- trimSuffix "/" $withLeading -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.host" -}}
{{- $subdomain := .Values.subdomain | default "" -}}
{{- if or (eq $subdomain "") (hasPrefix "###ZARF_VAR_" $subdomain) -}}
{{- .Values.domain -}}
{{- else -}}
{{- printf "%s.%s" $subdomain .Values.domain -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.adminHost" -}}
{{- $subdomain := .Values.subdomain | default "" -}}
{{- if or (eq $subdomain "") (hasPrefix "###ZARF_VAR_" $subdomain) -}}
{{- include "keycloak.adminDomain" . -}}
{{- else -}}
{{- printf "%s.%s" $subdomain (include "keycloak.adminDomain" .) -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.adminDomain" -}}
{{- if .Values.adminDomain -}}
{{- tpl .Values.adminDomain . -}}
{{- else -}}
{{- printf "admin.%s" .Values.domain -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.ssoPath" -}}
{{- printf "%s/sso" (include "keycloak.contextPath" .) -}}
{{- end -}}

{{- define "keycloak.adminPath" -}}
{{- printf "%s%s/keycloak" (include "keycloak.contextPath" .) (include "keycloak.adminContextPath" .) -}}
{{- end -}}

{{- define "keycloak.ssoPathForwardRegex" -}}
{{- printf "^%s/(.+)$" (include "keycloak.ssoPath" . | regexQuoteMeta) -}}
{{- end -}}

{{- define "keycloak.adminPathForwardRegex" -}}
{{- printf "^%s/(.+)$" (include "keycloak.adminPath" . | regexQuoteMeta) -}}
{{- end -}}

{{- define "keycloak.ssoUrl" -}}
{{- if eq (include "keycloak.pathRouting.enabled" .) "true" -}}
{{- printf "https://%s%s" (include "keycloak.host" .) (include "keycloak.ssoPath" .) -}}
{{- else -}}
{{- printf "https://sso.%s" .Values.domain -}}
{{- end -}}
{{- end -}}

{{- define "keycloak.adminUrl" -}}
{{- if eq (include "keycloak.pathRouting.enabled" .) "true" -}}
{{- printf "https://%s%s" (include "keycloak.adminHost" .) (include "keycloak.adminPath" .) -}}
{{- else -}}
{{- printf "https://keycloak.%s" (include "keycloak.adminDomain" .) -}}
{{- end -}}
{{- end -}}

{{/*
Check external PostgreSQL connection information. Fails when required values are missing or if PostgreSQL is configured when devMode is enabled.
*/}}
Expand Down Expand Up @@ -217,4 +299,3 @@ Check external PostgreSQL connection information. Fails when required values are
{{- "host" -}}
{{- end -}}
{{- end -}}

5 changes: 5 additions & 0 deletions src/keycloak/chart/templates/istio-authpol-admin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ spec:
paths:
- "/admin*"
- "/realms/master*"
{{- if eq (include "keycloak.pathRouting.enabled" .) "true" }}
notPaths:
- "/realms/master/protocol/openid-connect/*"
- "/realms/master/login-actions/*"
{{- end }}
from:
- source:
notNamespaces:
Expand Down
6 changes: 4 additions & 2 deletions src/keycloak/chart/templates/path-parameter-envoyfilter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ spec:
-- Only apply to keycloak endpoints
if host and (
host == "sso.{{ .Values.domain }}" or
host == "keycloak.{{ tpl .Values.adminDomain . }}"
host == "keycloak.{{ tpl .Values.adminDomain . }}"{{- if eq (include "keycloak.pathRouting.enabled" .) "true" }} or
host == "{{ include "keycloak.host" . }}" or
host == "{{ include "keycloak.adminHost" . }}"{{- end }}
) then
if path then
local invalid_semicolon = string.match(path, "^[^;]*;[^/]*/")
Expand All @@ -45,4 +47,4 @@ spec:
end
end
end
{{- end }}
{{- end }}
10 changes: 10 additions & 0 deletions src/keycloak/chart/templates/statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ spec:
- "--proxy-headers=xforwarded"
- "--http-enabled=true"
- "--hostname-strict=false"
{{- if eq (include "keycloak.pathRouting.enabled" .) "true" }}
- "--hostname={{ include "keycloak.ssoUrl" . }}"
- "--hostname-admin={{ include "keycloak.adminUrl" . }}"
{{- end }}
{{- if .Values.jsonLogFormat }}
- "--log-console-output=json"
{{- end }}
Expand Down Expand Up @@ -210,6 +214,12 @@ spec:
value: "{{ .Values.domain }}"
- name: UDS_ADMIN_DOMAIN
value: "{{ tpl .Values.adminDomain . }}"
{{- if eq (include "keycloak.pathRouting.enabled" .) "true" }}
- name: UDS_URL
value: "{{ include "keycloak.ssoUrl" . }}"
- name: UDS_ADMIN_URL
value: "{{ include "keycloak.adminUrl" . }}"
{{- end }}

# Enable health and metrics endpoints
- name: KC_HEALTH_ENABLED
Expand Down
Loading
Loading