Files
mgmt/chart/mgmt-suite/values.yaml
scott f4bd03dc52 Add read-only LDAP gateway microservice backed by Keycloak
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>
2026-06-15 10:37:20 -07:00

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"