mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-10 06:24:09 +09:00
The realm named here is where keycloak-configure creates every platform client and the cluster-admin role, and where dashboard users sign in. Naming master puts all of that inside the realm that holds the Keycloak super-admin, and publishes its login page wherever the platform realm is published — and the guard belongs where the value is set, since the Keycloak chart only reads it once a separate admin hostname is configured. Assisted-By: LLM Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
272 lines
16 KiB
YAML
272 lines
16 KiB
YAML
{{- $kubeRootCa := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }}
|
|
{{- $cozystackCm := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
|
{{- $bundleName := .Values.bundles.system.variant }}
|
|
{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }}
|
|
{{- /* Read root-host from ConfigMap if available, else use chart values */ -}}
|
|
{{- $rootHost := .Values.publishing.host -}}
|
|
{{- if and $cozystackCm $cozystackCm.data -}}
|
|
{{- $rootHost = default $rootHost (index $cozystackCm.data "root-host") -}}
|
|
{{- end -}}
|
|
---
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: cozystack-values
|
|
namespace: cozy-system
|
|
labels:
|
|
reconcile.fluxcd.io/watch: Enabled
|
|
type: Opaque
|
|
stringData:
|
|
values.yaml: |
|
|
# keda.clusterDomain: authoritative cluster domain injected into the KEDA subchart
|
|
# (a subchart value a template cannot derive from _cluster). Rationale: packages/system/keda.
|
|
keda:
|
|
clusterDomain: {{ .Values.networking.clusterDomain | quote }}
|
|
{{- /*
|
|
The operator builds every in-cluster name it needs from DNS_BASE: the
|
|
securityconfig job target, its own OpenSearch API client URL, the
|
|
Dashboards host, and the SANs it writes into node certificates. This is
|
|
a vendored subchart value, so it travels under the subchart key; _cluster
|
|
stops at each package's own templates and never reaches a subchart.
|
|
A package rendered without this Secret falls back to cluster.local.
|
|
*/}}
|
|
opensearch-operator:
|
|
manager:
|
|
dnsBase: {{ .Values.networking.clusterDomain | quote }}
|
|
_cluster:
|
|
root-host: {{ $rootHost | quote }}
|
|
bundle-name: {{ .Values.bundles.system.variant | quote }}
|
|
solver: {{ .Values.publishing.certificates.solver | quote }}
|
|
issuer-name: {{ .Values.publishing.certificates.issuerName | quote }}
|
|
{{- /*
|
|
Wildcard certificate mode. Only the Secret NAME rides this
|
|
channel — never the cert/key material (unlike a private key, the
|
|
name is not sensitive). When set, the ingress flow points the
|
|
root ingress controller's --default-ssl-certificate at it and
|
|
drops per-host ACME, and the gateway chart switches the
|
|
TenantGateway to certMode=existingSecret — unless
|
|
the tenant's Gateway is on an edge-terminated class, which wins
|
|
over it and issues nothing at all.
|
|
|
|
The name comes from one of two sources, with the operator-
|
|
provided (BYO) name always winning:
|
|
1. publishing.certificates.wildcardSecretName — a Secret the
|
|
operator pre-creates in the publishing namespace
|
|
(tenant-root). wildcard-issue stays false: the platform
|
|
mints nothing.
|
|
2. Otherwise, on the ingress-nginx path (gateway.enabled=false)
|
|
with solver=dns01 and publishing.certificates.wildcard=true,
|
|
the platform issues its own shared wildcard. wildcard-issue
|
|
becomes true and the publishing ingress package renders the
|
|
Certificate into this Secret name. http01 cannot issue
|
|
wildcards, and the Gateway API path issues its own wildcard
|
|
via the TenantGateway controller, so both are excluded.
|
|
Empty name + wildcard-issue=false = per-host ACME as before.
|
|
*/}}
|
|
{{- $byoWildcard := .Values.publishing.certificates.wildcardSecretName | default "" }}
|
|
{{- $wildcardSecretName := $byoWildcard }}
|
|
{{- $wildcardIssue := false }}
|
|
{{- if and (not $byoWildcard) (eq .Values.publishing.certificates.solver "dns01") .Values.publishing.certificates.wildcard (not .Values.gateway.enabled) }}
|
|
{{- $wildcardSecretName = "cozystack-wildcard-tls" }}
|
|
{{- $wildcardIssue = true }}
|
|
{{- end }}
|
|
wildcard-secret-name: {{ $wildcardSecretName | quote }}
|
|
wildcard-issue: {{ $wildcardIssue | quote }}
|
|
{{- /*
|
|
DNS-01 solver wiring. Read only when solver=dns01, but the
|
|
keys are written unconditionally so a render-time switch from
|
|
http01 to dns01 doesn't require chart redeploy of every
|
|
consumer. Each consumer (extra/gateway, ingress flow) reads
|
|
the keys it needs and validates per-provider requirements.
|
|
*/}}
|
|
{{- with .Values.publishing.certificates.dns01 }}
|
|
dns01-provider: {{ .provider | default "cloudflare" | quote }}
|
|
{{- with .cloudflare }}
|
|
dns01-cloudflare-secret-name: {{ .secretName | default "cloudflare-api-token-secret" | quote }}
|
|
dns01-cloudflare-secret-key: {{ .secretKey | default "api-token" | quote }}
|
|
{{- end }}
|
|
{{- with .route53 }}
|
|
dns01-route53-region: {{ .region | default "" | quote }}
|
|
dns01-route53-access-key-id: {{ .accessKeyID | default "" | quote }}
|
|
dns01-route53-secret-name: {{ .secretName | default "" | quote }}
|
|
dns01-route53-secret-key: {{ .secretKey | default "secret-access-key" | quote }}
|
|
{{- end }}
|
|
{{- with .digitalocean }}
|
|
dns01-digitalocean-secret-name: {{ .secretName | default "digitalocean-api-token-secret" | quote }}
|
|
dns01-digitalocean-secret-key: {{ .secretKey | default "access-token" | quote }}
|
|
{{- end }}
|
|
{{- with .rfc2136 }}
|
|
dns01-rfc2136-nameserver: {{ .nameserver | default "" | quote }}
|
|
dns01-rfc2136-tsig-key-name: {{ .tsigKeyName | default "" | quote }}
|
|
dns01-rfc2136-tsig-algorithm: {{ .tsigAlgorithm | default "HMACSHA256" | quote }}
|
|
dns01-rfc2136-secret-name: {{ .secretName | default "" | quote }}
|
|
dns01-rfc2136-secret-key: {{ .secretKey | default "tsig-secret-key" | quote }}
|
|
{{- end }}
|
|
{{- end }}
|
|
oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }}
|
|
oidc-insecure-skip-verify: {{ .Values.authentication.oidc.insecureSkipVerify | quote }}
|
|
extra-keycloak-redirect-uri-for-dashboard: {{ index .Values.authentication.oidc.keycloakExtraRedirectUri | quote }}
|
|
{{- /* The realm name becomes a Keycloak realm, a URL path segment in every
|
|
issuer/redirect/logout URL, and a shell token in the update-email
|
|
hook. Reject anything YAML or --set did not resolve to a string (a
|
|
bare number, or true/false) — coercing it would rename the realm to
|
|
something never typed and inject a "%!s(...)" into the derived URL —
|
|
and reject anything outside a safe slug charset, so a stray "/" or
|
|
space cannot split a URL path or the shell. Fail at the source
|
|
rather than emit a broken issuer later. The hint does not echo the
|
|
parsed value back: YAML has already lost the operator's original
|
|
token (on -> true), so quoting is the fix, not the reprinted bool. */}}
|
|
{{- /* Unset (null) falls back to cozy; an explicit "" is a meaningless
|
|
value, not a default request, so let it reach the charset guard
|
|
below rather than swallowing it — mirrors the keycloakInternalUrl
|
|
tri-state a few lines down (kindIs "invalid" is null, not ""). */}}
|
|
{{- $realmName := .Values.authentication.oidc.realmName }}
|
|
{{- if kindIs "invalid" $realmName }}
|
|
{{- $realmName = "cozy" }}
|
|
{{- end }}
|
|
{{- if not (kindIs "string" $realmName) }}
|
|
{{- fail (printf "authentication.oidc.realmName must be a string (got %s); wrap the value in quotes in the platform values, e.g. realmName: \"my-realm\"" (kindOf $realmName)) }}
|
|
{{- end }}
|
|
{{- if not (regexMatch "^[a-zA-Z0-9][a-zA-Z0-9._-]*$" $realmName) }}
|
|
{{- fail (printf "authentication.oidc.realmName %q is invalid: use only letters, digits, '.', '_' and '-' (it is used verbatim as a URL path segment and a shell token)" $realmName) }}
|
|
{{- end }}
|
|
{{- /* master holds the Keycloak super-admin. Naming it here would put
|
|
every platform client, the cluster-admin role and the users who
|
|
log in to the dashboard inside that realm, and publish its login
|
|
page and token endpoint wherever the platform realm is published. */}}
|
|
{{- if eq $realmName "master" }}
|
|
{{- fail "authentication.oidc.realmName must not be master: that realm holds the Keycloak super-admin, so platform clients and end users would share it. Pick any other name, e.g. realmName: \"cozy\"" }}
|
|
{{- end }}
|
|
oidc-realm-name: {{ $realmName | quote }}
|
|
{{- /* null keycloakInternalUrl derives the in-cluster Service URL for the
|
|
configured realm; "" is the external-discovery escape hatch and an
|
|
explicit value is passed through, so distinguish null from "" with
|
|
kindIs rather than `default`, which would also fire on "". */}}
|
|
{{- $keycloakInternalUrl := .Values.authentication.oidc.keycloakInternalUrl }}
|
|
{{- if kindIs "invalid" $keycloakInternalUrl }}
|
|
{{- $keycloakInternalUrl = printf "http://keycloak-http.cozy-keycloak.svc:8080/realms/%s" $realmName }}
|
|
{{- end }}
|
|
keycloak-internal-url: {{ $keycloakInternalUrl | quote }}
|
|
expose-services: {{ .Values.publishing.exposedServices | join "," | quote }}
|
|
{{- /*
|
|
expose-ingress carries two semantically distinct values that
|
|
happen to coincide in the default cozystack deployment:
|
|
1. The ingressClassName the per-system app (dashboard,
|
|
cozystack-api, etc.) sets on its Ingress resource when
|
|
gateway-enabled=false.
|
|
2. The namespace of the Gateway that the same per-system
|
|
app's HTTPRoute / TLSRoute parentRefs target when
|
|
gateway-enabled=true (publishing tenant namespace).
|
|
Default `tenant-root` satisfies both because the ingress-nginx
|
|
controller and the publishing Gateway both live in tenant-root.
|
|
If a future deployment splits these (e.g. an ingress controller
|
|
in cozy-ingress vs a publishing Gateway in tenant-root), the
|
|
per-system templates need a separate _cluster.gateway-namespace
|
|
key. Documented here as a known limitation.
|
|
*/}}
|
|
expose-ingress: {{ .Values.publishing.ingressName | quote }}
|
|
{{- /* Optional namespace of a SEPARATE Gateway/ingressClass that
|
|
system components (currently Keycloak) attach their admin
|
|
routes to — e.g. a private-IP Gateway unreachable from the
|
|
public internet. Empty -> admin shares expose-ingress. The
|
|
Gateway itself is provisioned out of band; this only selects
|
|
where the admin route attaches. */}}
|
|
expose-ingress-admin: {{ .Values.publishing.ingressNameAdmin | default "" | quote }}
|
|
expose-external-ips: {{ .Values.publishing.externalIPs | join "," | quote }}
|
|
{{- /* Native loadBalancerClass for the host ingress LoadBalancer Service
|
|
(see extra/ingress). Empty lets the default LB controller claim it. */}}
|
|
load-balancer-class: {{ .Values.publishing.loadBalancerClass | default "" | quote }}
|
|
{{- /* Host PROXY-protocol switch. extra/ingress reads this and applies
|
|
use-proxy-protocol to the external-facing ingress (the namespace
|
|
named by expose-ingress), keeping it host-scoped like expose-external-ips. */}}
|
|
proxy-protocol: {{ .Values.publishing.proxyProtocol | default false | quote }}
|
|
gateway-enabled: {{ .Values.gateway.enabled | default false | quote }}
|
|
gateway-attached-namespaces: {{ .Values.gateway.attachedNamespaces | default (list) | join "," | quote }}
|
|
{{- /* Normalised once here rather than at each reader: two charts
|
|
compare this scalar against the comma-joined lists below,
|
|
which trim their own entries on split, so a padded name that
|
|
reached them would match in neither.
|
|
|
|
Unset falls back to the bundled class. Whitespace-only does
|
|
NOT: unset means "I did not choose", while whitespace means
|
|
"I chose, and what I typed is garbage" — and this key decides
|
|
where TLS terminates. Resolving it quietly would publish a
|
|
tenant directly, with a real certificate on a real address,
|
|
that the operator meant to put behind an edge terminator. */}}
|
|
{{- /* Read before normalising, and check the kind first. `default`
|
|
fires on any falsy value, so a `default ""` ahead of these
|
|
checks would launder 0 and false into the empty string and
|
|
resolve them to the bundled class — the guard would then
|
|
cover the loudest bad input and miss the two quietest. The
|
|
order of a guard's steps is part of the guard. */}}
|
|
{{- $rawClassName := .Values.gateway.className }}
|
|
{{- if not (kindIs "invalid" $rawClassName) }}
|
|
{{- if not (kindIs "string" $rawClassName) }}
|
|
{{- fail (printf "gateway.className must be a GatewayClass name, got %v" $rawClassName) }}
|
|
{{- end }}
|
|
{{- if and (ne $rawClassName "") (eq (trim $rawClassName) "") }}
|
|
{{- fail "gateway.className is only whitespace. Leave it unset to use the bundled cilium class, or name a real GatewayClass — resolving a blank value silently would decide TLS termination for every tenant that did not choose." }}
|
|
{{- end }}
|
|
{{- end }}
|
|
gateway-class-name: {{ $rawClassName | default "" | trim | default "cilium" | quote }}
|
|
{{- /*
|
|
A non-string entry is rejected here rather than travelling as
|
|
something the join below would render unreadably. Note what is NOT
|
|
checked: whether the names correspond to GatewayClasses that exist.
|
|
A misspelled name in this list simply matches no class, so edge
|
|
behaviour never engages for it and nothing says so — the same shape
|
|
as a misspelled name in className, which lands a tenant Gateway on
|
|
a class the cluster does not serve and shows up as a Gateway that
|
|
never programs.
|
|
*/}}
|
|
{{- $edgeClasses := .Values.gateway.edgeTerminatedClasses | default (list) }}
|
|
{{- $tenantClasses := .Values.gateway.tenantSelectableClasses | default (list) }}
|
|
{{- /* Both lists travel as one comma-joined scalar, so a name that
|
|
carries a comma would arrive as two names and match neither. */}}
|
|
{{- range $edgeClasses }}
|
|
{{- if not (kindIs "string" .) }}
|
|
{{- fail (printf "gateway.edgeTerminatedClasses must be a list of GatewayClass names, got %v" .) }}
|
|
{{- end }}
|
|
{{- if contains "," (. | toString) }}
|
|
{{- fail (printf "gateway.edgeTerminatedClasses entry %q contains a comma; the list travels comma-joined and the name would be split" .) }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- range $tenantClasses }}
|
|
{{- if not (kindIs "string" .) }}
|
|
{{- fail (printf "gateway.tenantSelectableClasses must be a list of GatewayClass names, got %v" .) }}
|
|
{{- end }}
|
|
{{- if contains "," (. | toString) }}
|
|
{{- fail (printf "gateway.tenantSelectableClasses entry %q contains a comma; the list travels comma-joined and the name would be split" .) }}
|
|
{{- end }}
|
|
{{- end }}
|
|
gateway-edge-classes: {{ $edgeClasses | join "," | quote }}
|
|
gateway-tenant-classes: {{ $tenantClasses | join "," | quote }}
|
|
{{- /* Whether the root tenant hosts the platform monitoring stack
|
|
(Tenant/root spec.monitoring). cozystack-basics gates the
|
|
cozy-monitoring -> tenant-root vlinsert/vminsert ExternalName
|
|
redirects on this: when the root tenant has no monitoring, those
|
|
redirects dead-end and fluent-bit silently drops logs (#3181).
|
|
`dig` (not `default`) is used deliberately: it keys on presence,
|
|
so an explicit rootEnabled=false is preserved (Go's `default`
|
|
would treat the boolean false as empty and flip it back to true),
|
|
while `| default dict` guards against `.Values.monitoring` being
|
|
null/omitted in custom values. Missing key -> the true default,
|
|
matching the chart values.yaml. */}}
|
|
monitoring-enabled: {{ dig "rootEnabled" true (.Values.monitoring | default dict) | quote }}
|
|
cluster-domain: {{ .Values.networking.clusterDomain | quote }}
|
|
api-server-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }}
|
|
{{- with .Values.branding }}
|
|
branding:
|
|
{{- . | toYaml | nindent 8 }}
|
|
{{- end }}
|
|
cpu-allocation-ratio: {{ .Values.resources.cpuAllocationRatio | quote }}
|
|
memory-allocation-ratio: {{ .Values.resources.memoryAllocationRatio | quote }}
|
|
ephemeral-storage-allocation-ratio: {{ .Values.resources.ephemeralStorageAllocationRatio | quote }}
|
|
{{- with .Values.scheduling }}
|
|
scheduling:
|
|
{{- . | toYaml | nindent 8 }}
|
|
{{- end }}
|
|
{{- with $kubeRootCa.data }}
|
|
kube-root-ca: {{ index . "ca.crt" | b64enc | quote }}
|
|
{{- end }}
|