mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-06 12:03:39 +09:00
feat(keycloak): opt-in separate hostname and route for the admin console (#2954)
## What this PR does
Adds an opt-in way to serve the Keycloak admin console and
Administration
REST API on a separate hostname and route from the public login
endpoints,
so the admin surface can be kept off the public internet.
- New `ingress.adminHost` in the keycloak chart. When set:
- `KC_HOSTNAME_ADMIN` is configured so the admin console generates its
URLs on that hostname;
- the public (login) route is narrowed to the OIDC paths only
(`/realms`, `/resources`, `/.well-known`), so the admin console and
Administration REST API are no longer served on the login hostname;
- a dedicated route (Ingress or HTTPRoute, depending on
`gateway-enabled`)
serves the admin hostname.
- New platform `publishing.ingressNameAdmin`, propagated as
`_cluster.expose-ingress-admin`. The admin route attaches to the
Gateway/ingressClass it names, defaulting to `expose-ingress`. Pointing
it
at a separate (for example, private-IP) Gateway moves admin traffic off
the public one. The separate Gateway itself is provisioned out of band;
this only selects where the admin route attaches.
Everything is empty by default, so existing deployments are unchanged.
Note: `KC_HOSTNAME_ADMIN` on its own is not a security boundary — the
Administration REST API stays reachable on any hostname routed to
Keycloak
(per the Keycloak docs). The path and network separation introduced here
is
what restricts access; full isolation requires attaching the admin route
to
a non-public Gateway.
Covered by helm-unittest suites for the keycloak chart and the platform
value wiring.
### Screenshots
N/A — no UI changes.
### Release note
```release-note
feat(keycloak): add opt-in `ingress.adminHost` to serve the admin console and Administration REST API on a separate hostname/route, plus `publishing.ingressNameAdmin` (`_cluster.expose-ingress-admin`) to attach that route to a separate (e.g. private) Gateway/ingressClass. Disabled by default; existing deployments are unchanged.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added optional configuration to serve the Keycloak admin console on a
dedicated hostname, with separate routing and ingress/gateway attachment
so admin UI/API can be isolated from public login/OIDC paths.
* Introduced a cluster-level setting to optionally expose admin routes
via a separate ingress/gateway class/namespace.
* **Tests**
* Added new YAML test coverage to validate defaulting, hostname split
behavior, and correct routing for both gateway-enabled and
gateway-disabled modes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
8 files changed
+443
No files matched your search
@@ -108,6 +108,13 @@ stringData:
|
||||
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 }}
|
||||
{{- /* Host PROXY-protocol switch. extra/ingress reads this and applies
|
||||
use-proxy-protocol to the external-facing ingress (the namespace
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
suite: apps.yaml admin-ingress platform → tenant value wiring
|
||||
templates:
|
||||
- templates/apps.yaml
|
||||
|
||||
release:
|
||||
name: cozy-platform
|
||||
namespace: cozy-system
|
||||
|
||||
# Asserts that publishing.ingressNameAdmin lands in _cluster.expose-ingress-admin,
|
||||
# the key read by system charts (currently keycloak) to attach admin routes to a
|
||||
# separate Gateway/ingressClass. Empty by default so admin shares expose-ingress.
|
||||
tests:
|
||||
- it: writes an empty expose-ingress-admin by default
|
||||
asserts:
|
||||
- matchRegex:
|
||||
path: stringData["values.yaml"]
|
||||
pattern: 'expose-ingress-admin:\s*""'
|
||||
|
||||
- it: writes the configured admin ingress namespace
|
||||
set:
|
||||
publishing.ingressNameAdmin: cozy-private
|
||||
asserts:
|
||||
- matchRegex:
|
||||
path: stringData["values.yaml"]
|
||||
pattern: 'expose-ingress-admin:\s*"cozy-private"'
|
||||
@@ -49,6 +49,13 @@ networking:
|
||||
publishing:
|
||||
host: "example.org"
|
||||
ingressName: tenant-root
|
||||
# Optional namespace of a SEPARATE Gateway / ingressClass that system
|
||||
# components attach their admin routes to (currently Keycloak) — e.g. one
|
||||
# bound to a private IP, unreachable from the public internet. Empty means
|
||||
# admin endpoints share the same ingress as everything else (ingressName).
|
||||
# The private Gateway itself is NOT created by cozystack; provision it out
|
||||
# of band, then point this at its namespace to move admin traffic onto it.
|
||||
ingressNameAdmin: ""
|
||||
exposedServices:
|
||||
- api
|
||||
- dashboard
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
{{- $host := index .Values._cluster "root-host" }}
|
||||
{{- $ingressHost := .Values.ingress.host | default (printf "keycloak.%s" $host) }}
|
||||
{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }}
|
||||
{{- $adminExposeIngress := (index .Values._cluster "expose-ingress-admin") | default $exposeIngress }}
|
||||
{{- $gatewayEnabled := (index .Values._cluster "gateway-enabled") | default "false" }}
|
||||
{{- $adminHost := .Values.ingress.adminHost }}
|
||||
{{- if eq $gatewayEnabled "true" }}
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
@@ -16,7 +18,50 @@ spec:
|
||||
hostnames:
|
||||
- {{ $ingressHost | quote }}
|
||||
rules:
|
||||
{{- if $adminHost }}
|
||||
# adminHost is set: the public (login) hostname exposes only the
|
||||
# OIDC/login paths. The admin console and Administration REST API are
|
||||
# served exclusively on the dedicated admin route below, so /admin is
|
||||
# not reachable here. See the Keycloak reverse proxy guide,
|
||||
# "Exposed path recommendations".
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /realms
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /resources
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /.well-known
|
||||
backendRefs:
|
||||
- name: keycloak-http
|
||||
port: 80
|
||||
{{- else }}
|
||||
- backendRefs:
|
||||
- name: keycloak-http
|
||||
port: 80
|
||||
{{- end }}
|
||||
{{- if $adminHost }}
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: keycloak-admin
|
||||
spec:
|
||||
# Attaches to the Gateway named cozystack in the admin namespace. When
|
||||
# expose-ingress-admin points at a separate (e.g. private-IP) Gateway,
|
||||
# admin traffic leaves the public Gateway entirely.
|
||||
parentRefs:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
name: cozystack
|
||||
namespace: {{ $adminExposeIngress }}
|
||||
hostnames:
|
||||
- {{ $adminHost | quote }}
|
||||
rules:
|
||||
- backendRefs:
|
||||
- name: keycloak-http
|
||||
port: 80
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -3,8 +3,16 @@
|
||||
{{- $solver := (index .Values._cluster "solver") | default "http01" }}
|
||||
{{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }}
|
||||
{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }}
|
||||
{{- $adminExposeIngress := (index .Values._cluster "expose-ingress-admin") | default $exposeIngress }}
|
||||
{{- $gatewayEnabled := (index .Values._cluster "gateway-enabled") | default "false" }}
|
||||
{{- $wildcardSecret := (index .Values._cluster "wildcard-secret-name") | default "" }}
|
||||
{{- $adminHost := .Values.ingress.adminHost }}
|
||||
{{- /* Wildcard mode applies to the admin Ingress only when it shares the
|
||||
public ingressClass: the operator's default SSL cert covers it too, so
|
||||
skip per-host ACME like the login Ingress. When admin is moved onto a
|
||||
separate (e.g. private) ingress, that controller has no wildcard default
|
||||
cert, so keep its own per-host certificate regardless of wildcard mode. */}}
|
||||
{{- $adminWildcard := and $wildcardSecret (eq $adminExposeIngress $exposeIngress) }}
|
||||
{{- if ne $gatewayEnabled "true" }}
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
@@ -35,6 +43,64 @@ spec:
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ $ingressHost }}
|
||||
http:
|
||||
paths:
|
||||
{{- if $adminHost }}
|
||||
# adminHost is set: expose only the OIDC/login paths here. The admin
|
||||
# console and Administration REST API live on the admin Ingress below,
|
||||
# so /admin is not reachable on the login hostname. See the Keycloak
|
||||
# reverse proxy guide, "Exposed path recommendations".
|
||||
{{- range $path := list "/realms" "/resources" "/.well-known" }}
|
||||
- path: {{ $path }}
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: keycloak-http
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: keycloak-http
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
{{- if $adminHost }}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: keycloak-admin-ingress
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- /* Operator wildcard mode (shared public ingress): nginx serves the
|
||||
default SSL cert, so skip per-host ACME. */}}
|
||||
{{- if not $adminWildcard }}
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $adminExposeIngress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
{{- end }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
# Uses the admin ingressClass. When expose-ingress-admin points at a
|
||||
# separate (e.g. private) ingress controller, admin traffic leaves the
|
||||
# public ingress entirely.
|
||||
ingressClassName: {{ $adminExposeIngress }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ $adminHost }}
|
||||
{{- /* Wildcard mode on the shared public ingress: rely on the default SSL
|
||||
cert, no per-host secret. A separate admin ingress keeps its own. */}}
|
||||
{{- if not $adminWildcard }}
|
||||
secretName: keycloak-admin-tls
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ $adminHost }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
@@ -45,3 +111,4 @@ spec:
|
||||
port:
|
||||
name: http
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -182,6 +182,10 @@ spec:
|
||||
value: "docker"
|
||||
- name: KC_HOSTNAME
|
||||
value: https://{{ $ingressHost }}
|
||||
{{- with .Values.ingress.adminHost }}
|
||||
- name: KC_HOSTNAME_ADMIN
|
||||
value: https://{{ . }}
|
||||
{{- end }}
|
||||
- name: JAVA_OPTS_APPEND
|
||||
value: "-Djgroups.dns.query=keycloak-headless.cozy-keycloak.svc.{{ $clusterDomain }}"
|
||||
{{- with .Values.extraEnv }}
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
suite: admin hostname split (ingress.adminHost)
|
||||
|
||||
release:
|
||||
name: keycloak
|
||||
namespace: cozy-keycloak
|
||||
|
||||
# ingress.adminHost is an opt-in option. When set, Keycloak's admin console
|
||||
# and Administration REST API are moved onto a dedicated hostname/route, and
|
||||
# the public (login) route is narrowed to the OIDC paths only so /admin is no
|
||||
# longer served there. The admin route attaches to the Gateway/ingressClass
|
||||
# selected by _cluster.expose-ingress-admin (defaults to expose-ingress),
|
||||
# allowing it to be pointed at a separate, e.g. private, Gateway.
|
||||
tests:
|
||||
# --- StatefulSet env: KC_HOSTNAME_ADMIN ---
|
||||
- it: does not set KC_HOSTNAME_ADMIN when adminHost is empty (default)
|
||||
template: templates/sts.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
asserts:
|
||||
- isKind:
|
||||
of: StatefulSet
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: KC_HOSTNAME
|
||||
value: https://keycloak.example.com
|
||||
- notContains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: KC_HOSTNAME_ADMIN
|
||||
value: https://keycloak-admin.example.com
|
||||
|
||||
- it: sets KC_HOSTNAME_ADMIN when adminHost is set
|
||||
template: templates/sts.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- contains:
|
||||
path: spec.template.spec.containers[0].env
|
||||
content:
|
||||
name: KC_HOSTNAME_ADMIN
|
||||
value: https://keycloak-admin.example.com
|
||||
|
||||
# --- Ingress (gateway disabled) ---
|
||||
- it: renders one Ingress serving all paths when adminHost is empty
|
||||
template: templates/ingress.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- isKind:
|
||||
of: Ingress
|
||||
- equal:
|
||||
path: spec.rules[0].host
|
||||
value: keycloak.example.com
|
||||
- lengthEqual:
|
||||
path: spec.rules[0].http.paths
|
||||
count: 1
|
||||
- equal:
|
||||
path: spec.rules[0].http.paths[0].path
|
||||
value: /
|
||||
- lengthEqual:
|
||||
path: spec.tls
|
||||
count: 1
|
||||
|
||||
- it: narrows the login Ingress to OIDC paths and adds an admin Ingress
|
||||
template: templates/ingress.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
# doc 0: public login Ingress — only OIDC paths, never /admin or /
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: keycloak-ingress
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.rules[0].host
|
||||
value: keycloak.example.com
|
||||
- documentIndex: 0
|
||||
lengthEqual:
|
||||
path: spec.rules[0].http.paths
|
||||
count: 3
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.rules[0].http.paths[0].path
|
||||
value: /realms
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.rules[0].http.paths[1].path
|
||||
value: /resources
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.rules[0].http.paths[2].path
|
||||
value: /.well-known
|
||||
# doc 1: admin Ingress — full path, dedicated host/secret/class
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin-ingress
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.rules[0].host
|
||||
value: keycloak-admin.example.com
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.rules[0].http.paths[0].path
|
||||
value: /
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.tls[0].secretName
|
||||
value: keycloak-admin-tls
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.ingressClassName
|
||||
value: tenant-root
|
||||
|
||||
- it: admin Ingress uses expose-ingress-admin ingressClass when set
|
||||
template: templates/ingress.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
expose-ingress-admin: cozy-private
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin-ingress
|
||||
- equal:
|
||||
path: spec.ingressClassName
|
||||
value: cozy-private
|
||||
|
||||
- it: admin Ingress drops per-host TLS in wildcard mode on the shared ingress
|
||||
template: templates/ingress.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
wildcard-secret-name: cozystack-wildcard
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin-ingress
|
||||
# no per-host secret — relies on the operator default SSL cert
|
||||
- notExists:
|
||||
path: spec.tls[0].secretName
|
||||
# no ACME / cluster-issuer annotations in wildcard mode
|
||||
- notExists:
|
||||
path: metadata.annotations["cert-manager.io/cluster-issuer"]
|
||||
- notExists:
|
||||
path: metadata.annotations["acme.cert-manager.io/http01-ingress-ingressclassname"]
|
||||
|
||||
- it: admin Ingress keeps its own cert on a separate ingress even in wildcard mode
|
||||
template: templates/ingress.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
wildcard-secret-name: cozystack-wildcard
|
||||
expose-ingress-admin: cozy-private
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin-ingress
|
||||
# separate ingress has no wildcard default cert -> per-host secret stays
|
||||
- equal:
|
||||
path: spec.tls[0].secretName
|
||||
value: keycloak-admin-tls
|
||||
- equal:
|
||||
path: metadata.annotations["cert-manager.io/cluster-issuer"]
|
||||
value: letsencrypt-prod
|
||||
|
||||
# --- HTTPRoute (gateway enabled) ---
|
||||
- it: renders one HTTPRoute serving all paths when adminHost is empty
|
||||
template: templates/httproute.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
gateway-enabled: "true"
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 1
|
||||
- isKind:
|
||||
of: HTTPRoute
|
||||
- lengthEqual:
|
||||
path: spec.hostnames
|
||||
count: 1
|
||||
- equal:
|
||||
path: spec.hostnames[0]
|
||||
value: keycloak.example.com
|
||||
# no path matches -> all paths routed
|
||||
- isNull:
|
||||
path: spec.rules[0].matches
|
||||
|
||||
- it: narrows the login HTTPRoute to OIDC paths and adds an admin HTTPRoute
|
||||
template: templates/httproute.yaml
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
gateway-enabled: "true"
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- hasDocuments:
|
||||
count: 2
|
||||
# doc 0: public login route — only OIDC path prefixes
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: keycloak
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.hostnames[0]
|
||||
value: keycloak.example.com
|
||||
- documentIndex: 0
|
||||
lengthEqual:
|
||||
path: spec.rules[0].matches
|
||||
count: 3
|
||||
- documentIndex: 0
|
||||
equal:
|
||||
path: spec.rules[0].matches[0].path.value
|
||||
value: /realms
|
||||
# doc 1: admin route — dedicated hostname, default admin namespace
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.hostnames[0]
|
||||
value: keycloak-admin.example.com
|
||||
- documentIndex: 1
|
||||
equal:
|
||||
path: spec.parentRefs[0].namespace
|
||||
value: tenant-root
|
||||
|
||||
- it: admin HTTPRoute attaches to expose-ingress-admin namespace when set
|
||||
template: templates/httproute.yaml
|
||||
documentIndex: 1
|
||||
set:
|
||||
_cluster:
|
||||
root-host: example.com
|
||||
gateway-enabled: "true"
|
||||
expose-ingress-admin: cozy-private
|
||||
ingress:
|
||||
adminHost: keycloak-admin.example.com
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.name
|
||||
value: keycloak-admin
|
||||
- equal:
|
||||
path: spec.parentRefs[0].namespace
|
||||
value: cozy-private
|
||||
@@ -5,6 +5,22 @@ ingress:
|
||||
# If set, this value will be used as the Ingress hostname (e.g., "auth.example.com").
|
||||
# If left empty, defaults to "keycloak.<root-host>" based on the cluster root-host setting.
|
||||
host: ""
|
||||
# Optional separate hostname for the Keycloak admin console (opt-in).
|
||||
# When set (e.g., "keycloak-admin.example.com"):
|
||||
# - KC_HOSTNAME_ADMIN is configured so the admin console generates its
|
||||
# URLs on this hostname;
|
||||
# - the main `host` (login) route is narrowed to the public OIDC paths
|
||||
# (/realms, /resources, /.well-known) only, so the admin console and
|
||||
# Administration REST API (/admin) are NOT served on the login hostname;
|
||||
# - a dedicated Ingress/HTTPRoute for this hostname routes / to Keycloak.
|
||||
# The admin route attaches to the Gateway/ingressClass selected by the
|
||||
# cluster-level expose-ingress-admin (defaults to expose-ingress). Point
|
||||
# that at a separate, e.g. private-IP, Gateway to keep the admin console off
|
||||
# the public internet. Note: KC_HOSTNAME_ADMIN alone is NOT a security
|
||||
# boundary (admin REST stays reachable on any hostname routed to Keycloak);
|
||||
# the network/path separation here is what restricts access.
|
||||
# If left empty, the admin console stays on the main hostname (default).
|
||||
adminHost: ""
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/affinity: "cookie"
|
||||
nginx.ingress.kubernetes.io/session-cookie-expires: "86400"
|
||||
|
||||
Reference in new issue
Block a user