New ldap-gateway/ service (Twisted + ldaptor) that exposes Keycloak users and groups over LDAP v3 for legacy apps and Linux hosts (SSSD/NSS). Design (see ldap-gateway/SPEC.md): - Reads directly from the KC Admin API with an in-memory TTL cache + background refresh; fully stateless, no DB or queue. - Service-bind read-only: only configured service accounts may bind; no end-user password auth. Writes return unwillingToPerform. - Serves POSIX + inetOrgPerson entries (uid/gid/home, memberOf, group memberUid/member). UID/GID from a KC custom attribute else derived from the stable KC UUID. - Pluggable IdentityResolver (LDAP_IDENTITY_SOURCE): username today, a cert_cn strategy stubbed for the future cert-CN-as-identity direction. Build/deploy: - build.sh builds and (optionally) pushes mgmt-ldap-gateway alongside backend/frontend. - Helm: ldap-gateway deployment/service/configmap, gated by ldapGateway.enabled (off by default), reusing the shared KC secret. Verified end to end against a live Keycloak: service bind, user/group search, anonymous + bad-password denial, write rejection; container image builds, runs as non-root, warms from KC, and serves searches. 125 tests pass; ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
256 lines
10 KiB
YAML
256 lines
10 KiB
YAML
# Management Suite Helm Values
|
|
|
|
# Container registry prefix (e.g., "registry.example.com/team" or "" for local)
|
|
imageRegistry: ""
|
|
imagePullSecrets: []
|
|
|
|
backend:
|
|
image: mgmt-backend
|
|
tag: latest
|
|
replicas: 1
|
|
port: 5001
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
pdb:
|
|
enabled: false
|
|
minAvailable: 1
|
|
# Prometheus /metrics scrape via kube-prometheus-stack ServiceMonitor.
|
|
# The CRD only renders if monitoring.coreos.com is installed, so leaving
|
|
# this on by default is safe even without the stack present.
|
|
metrics:
|
|
enabled: true
|
|
path: /metrics
|
|
scrapeInterval: 30s
|
|
scrapeTimeout: 10s
|
|
# Extra labels (e.g. release: monitoring) for environments where the
|
|
# kube-prometheus-stack uses a release-label selector for ServiceMonitors.
|
|
serviceMonitorLabels: {}
|
|
|
|
frontend:
|
|
image: mgmt-frontend
|
|
tag: latest
|
|
replicas: 1
|
|
port: 8080
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 128Mi
|
|
limits:
|
|
cpu: 200m
|
|
memory: 256Mi
|
|
pdb:
|
|
enabled: false
|
|
minAvailable: 1
|
|
|
|
# ── Database ────────────────────────────────────────────────
|
|
# When postgresql.enabled is true, a bundled Postgres pod is deployed.
|
|
# When false and external.host is set, connects to an external database (e.g., RDS).
|
|
# One of these must be configured — there is no SQLite fallback.
|
|
postgresql:
|
|
enabled: false
|
|
image: postgres:16-alpine
|
|
database: mgmt
|
|
username: mgmt
|
|
password: "" # Goes into K8s Secret as DATABASE_PASSWORD
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 512Mi
|
|
persistence:
|
|
size: 5Gi
|
|
storageClass: ""
|
|
accessMode: ReadWriteOnce
|
|
external:
|
|
host: "" # e.g., mydb.abc123.us-east-1.rds.amazonaws.com
|
|
port: 5432
|
|
database: mgmt
|
|
username: mgmt
|
|
password: "" # Goes into K8s Secret as DATABASE_PASSWORD
|
|
sslMode: "" # "require", "verify-ca", "verify-full"
|
|
|
|
# ── Secrets ──────────────────────────────────────────────────
|
|
# Option 1: Provide an existing K8s Secret by name (e.g., from ExternalSecrets, Sealed Secrets, or manual creation).
|
|
# When set, Helm will NOT create its own Secret or ExternalSecret.
|
|
# The existing Secret must contain these keys:
|
|
# SECRET_KEY, FERNET_KEY
|
|
# And optionally: AUTH_PASSWORD_HASH, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_OIDC_CLIENT_SECRET,
|
|
# SMTP_USERNAME, SMTP_PASSWORD, MQTT_USERNAME, MQTT_PASSWORD,
|
|
# JIRA_USERNAME, JIRA_PASSWORD
|
|
# Option 2: Leave existingSecret empty and populate the values below — Helm will create the Secret for you.
|
|
# Option 3: Enable externalSecret below — Helm will create an ExternalSecret CR instead of a plain Secret.
|
|
existingSecret: ""
|
|
|
|
secrets:
|
|
# Generate with: python -c "import secrets; print(secrets.token_hex(32))"
|
|
secretKey: ""
|
|
# Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
fernetKey: ""
|
|
|
|
# ── Non-sensitive configuration ──────────────────────────────
|
|
auth:
|
|
username: admin
|
|
# Generate with: python -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('yourpassword'))"
|
|
passwordHash: ""
|
|
|
|
keycloak:
|
|
url: ""
|
|
realm: "mgmt"
|
|
clientId: "mgmt-admin-client"
|
|
clientSecret: ""
|
|
oidcClientId: "mgmt-web"
|
|
oidcClientSecret: ""
|
|
verifySsl: true # Set to false to skip SSL verification for Keycloak connections
|
|
timeout: 30 # Per-request timeout (seconds) for Keycloak Admin API calls; raise for slow/inspected network paths (e.g. F5 break-and-inspect)
|
|
|
|
# Frontend URL for OIDC redirect (should match ingress host)
|
|
frontendUrl: ""
|
|
|
|
# Session cookie domain (e.g., ".example.com"). Leave empty to let the browser scope to the origin.
|
|
sessionCookieDomain: ""
|
|
|
|
# ── Jira Server/Data Center ────────────────────────────────
|
|
# When jira.url is set, the Jira integration is enabled (VELA team only).
|
|
# Credentials should be in the existingSecret as JIRA_USERNAME and JIRA_PASSWORD.
|
|
jira:
|
|
url: "" # e.g., "https://jira.example.com"
|
|
projectKey: "OSAT" # Jira project key to query
|
|
username: "" # Goes into K8s Secret as JIRA_USERNAME
|
|
password: "" # Goes into K8s Secret as JIRA_PASSWORD
|
|
|
|
smtp:
|
|
host: ""
|
|
port: 587
|
|
username: ""
|
|
password: ""
|
|
useTls: true
|
|
useSsl: false
|
|
fromAddress: "mgmt@example.com"
|
|
|
|
# ── MQTT ───────────────────────────────────────────────────
|
|
# When mosquitto.enabled is true, a bundled Mosquitto pod is deployed.
|
|
# When false and external.host is set, connects to an external broker (e.g., Amazon MQ, EMQX Cloud).
|
|
# When neither is set but monitoring.releaseName is set, defaults to the mgmt-monitoring Mosquitto service.
|
|
# When none of the above, MQTT is disabled and announcements fall back to REST polling.
|
|
mosquitto:
|
|
enabled: false
|
|
image: eclipse-mosquitto:2
|
|
resources:
|
|
requests:
|
|
cpu: 25m
|
|
memory: 32Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
persistence:
|
|
size: 256Mi
|
|
storageClass: ""
|
|
accessMode: ReadWriteOnce
|
|
external:
|
|
host: "" # e.g., "mqtt.example.com" or "a1b2c3.iot.us-east-1.amazonaws.com"
|
|
port: 8883 # External brokers typically use 8883 (TLS)
|
|
wsPort: 443 # WebSocket port for browser clients
|
|
wsUrl: "" # Full browser-reachable WebSocket URL (e.g., "wss://mqtt.example.com/mqtt")
|
|
useTls: true
|
|
username: ""
|
|
password: "" # Goes into K8s Secret as MQTT_PASSWORD
|
|
# Default to the mgmt-monitoring chart's Mosquitto when no bundled or external host is set.
|
|
# Set monitoring.releaseName to the Helm release name of the mgmt-monitoring chart.
|
|
monitoring:
|
|
releaseName: "" # e.g., "mgmt-monitoring" → resolves to mgmt-monitoring-mosquitto.monitoring.svc
|
|
namespace: monitoring
|
|
useTls: false # Set to true when the monitoring mosquitto has TLS enabled
|
|
wsUrl: "" # Browser-reachable WebSocket URL (e.g., "wss://mqtt.osa.navy.mil/mqtt"). Defaults to mgmt-suite ingress host.
|
|
topic: "mgmt/announcements"
|
|
# Browser-reachable WebSocket URL override for the bundled broker
|
|
# (e.g., "wss://manage.example.com/mqtt"). When empty, the URL is built
|
|
# from ingress.host with wss/ws chosen by whether the site serves HTTPS.
|
|
wsUrl: ""
|
|
# Read-only credentials for browser WebSocket clients (separate from backend publish credentials).
|
|
# These must match a user in the mosquitto password file with read-only ACL.
|
|
wsUsername: "" # e.g., "mgmt-frontend"
|
|
wsPassword: "" # Goes into K8s Secret as MQTT_WS_PASSWORD
|
|
|
|
caCert:
|
|
enabled: false
|
|
secretName: "" # Name of the K8s Secret containing CA cert files (one or more keys)
|
|
|
|
ingress:
|
|
enabled: false
|
|
className: ""
|
|
host: manage.example.com
|
|
tls: false # In-cluster TLS via tlsSecretName (adds spec.tls to the Ingress)
|
|
tlsSecretName: ""
|
|
# Set when TLS terminates upstream of the pods (e.g. an ALB/NLB in front),
|
|
# so derived settings (wss:// MQTT URL, Secure cookies) treat the site as
|
|
# HTTPS even though no in-cluster TLS secret exists. Auto-detected when the
|
|
# AWS ALB annotation "alb.ingress.kubernetes.io/certificate-arn" is present.
|
|
externalTls: false
|
|
annotations: {}
|
|
|
|
# ── LDAP Gateway ─────────────────────────────────────────────
|
|
# Standalone microservice that exposes Keycloak users/groups over LDAP v3
|
|
# (read-only, service-bind only) for legacy apps and SSSD/NSS hosts.
|
|
# Stateless and horizontally scalable — no DB/queue. Each pod refreshes its
|
|
# own cache, so keep replicas modest or raise refreshInterval.
|
|
ldapGateway:
|
|
enabled: false
|
|
image: mgmt-ldap-gateway
|
|
tag: latest
|
|
replicas: 1
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 128Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 256Mi
|
|
service:
|
|
type: ClusterIP # ClusterIP for in-cluster consumers; LoadBalancer/NodePort for external LDAP clients
|
|
ldapPort: 389 # plain / StartTLS
|
|
ldapsPort: 636 # LDAPS
|
|
annotations: {}
|
|
bindHost: "0.0.0.0"
|
|
allowAnon: false
|
|
# Base DN for the directory tree, e.g. "dc=mgmt,dc=example,dc=com"
|
|
baseDn: ""
|
|
# Pluggable identity / user RDN source: "username" (default) or "cert_cn" (future, stubbed)
|
|
identitySource: "username"
|
|
# KC user attribute read when identitySource is "cert_cn"
|
|
certCnAttribute: "certCN"
|
|
# POSIX UID/GID allocation (KC custom attr wins, else derived from the stable KC UUID)
|
|
posix:
|
|
uidMin: 100000
|
|
uidMax: 599999
|
|
gidMin: 100000
|
|
gidMax: 599999
|
|
defaultShell: "/bin/bash"
|
|
homeBase: "/home"
|
|
# Caching & freshness (seconds)
|
|
cacheTtl: 60
|
|
refreshInterval: 60 # defaults to cacheTtl when unset
|
|
excludeDisabled: true
|
|
excludeServiceAccounts: true
|
|
groupInclude: "" # optional comma/space-separated name globs; empty = all
|
|
groupExclude: "" # optional name globs to exclude
|
|
# TLS material (cert/key) mounted from an existing Secret.
|
|
# Follow the chart's external-secret pattern: provide a Secret by name
|
|
# (e.g. from ExternalSecrets/Sealed Secrets/cert-manager) — Helm does not generate it.
|
|
tls:
|
|
secretName: "" # Secret containing tls.crt and tls.key
|
|
mountPath: "/etc/ldap-gateway/tls"
|
|
certPath: "/etc/ldap-gateway/tls/tls.crt"
|
|
keyPath: "/etc/ldap-gateway/tls/tls.key"
|
|
# Service-bind accounts (bind-DN→password JSON map) from an existing Secret.
|
|
# Provide the Secret by name (external-secret pattern); Helm does not generate it.
|
|
serviceAccountsSecret:
|
|
name: "" # Secret containing the LDAP_SERVICE_ACCOUNTS key
|
|
key: "LDAP_SERVICE_ACCOUNTS"
|