TLS & Credentials¶
The operator manages all TLS material and credentials itself — no cert-manager
dependency and no manual wazuh-cert-tool step.
Certificates¶
On first reconcile the operator generates a self-signed CA and signs the leaf certificates the stack needs, storing each in a Secret (created once, reused afterwards):
| Secret | Contents | Used by |
|---|---|---|
<name>-ca |
CA cert + key | signing all leaves |
<name>-indexer-certs |
node cert/key, CA, admin client cert/key | indexer transport/HTTP, securityadmin |
<name>-manager-certs |
filebeat client cert/key, CA | manager → indexer (9200, TLS) |
<name>-dashboard-certs |
dashboard server cert/key, CA | dashboard HTTPS |
Keys are PKCS#8 (required by the OpenSearch security tooling). The indexer node cert carries SANs for the service and per-pod headless DNS names.
Bring your own CA
Set spec.tls.caSecretRef to an existing Secret holding tls.crt/tls.key and
the operator signs all leaves from it instead of generating one.
Credentials¶
The credentials Secret (<name>-credentials by default) holds:
| Key | Purpose |
|---|---|
admin-password |
indexer admin (login + filebeat) |
kibanaserver-password |
dashboard → indexer service account |
api-password |
Wazuh API |
registration-password |
agent enrollment password (authd.pass) |
cluster-key |
manager cluster key |
admin-hash, kibanaserver-hash |
bcrypt hashes for internal_users.yml |
Passwords are injected into pods via environment variables and are never written
into ConfigMaps. Bcrypt hashes are stored so the rendered internal_users.yml stays
stable across reconciles. A one-shot securityadmin Job applies the hashed users to the
running indexer.
Setting credentials¶
Three ways, following the nextcloud-operator pattern. Anything you omit is generated.
1. Inline (scrubbed into a Secret). Set values under spec.credentials; the operator
writes them into the Secret, then removes the plaintext from the CR and records
credentialsSecret — so the stored object never keeps plaintext (GitOps-safe):
spec:
credentials:
adminPassword: "choose-a-strong-one"
registrationPassword: "agent-enrollment-secret"
After reconcile the CR reads:
2. Reference an existing Secret you manage:
Populate it with any of the keys above; the operator fills in (and hashes) only what is missing.
3. Omit spec.credentials — everything is generated into <name>-credentials.
Getting credentials¶
NS=wazuh-demo; NAME=demo
SECRET=$(kubectl get wazuhcluster $NAME -n $NS -o jsonpath='{.spec.credentials.credentialsSecret}')
# Dashboard / API login (user: admin)
kubectl get secret $SECRET -n $NS -o jsonpath='{.data.admin-password}' | base64 -d; echo
# Agent enrollment password
kubectl get secret $SECRET -n $NS -o jsonpath='{.data.registration-password}' | base64 -d; echo
Rotating credentials¶
Rotation is automated. Change a password — inline under spec.credentials (scrubbed
again) or directly in the referenced Secret — and the operator:
- recomputes the security revision (
status.appliedSecurityRevisiontracks what the indexer currently has); - runs a fresh
securityadminJob to re-key the running indexer to the new password; - only then rolls the affected consumers — the dashboard and manager pods carry a
wazuh.bnerd.com/credentials-checksumannotation, so they restart and reconnect with the new value.
Because the consumers are gated on the re-key Job, they never start or roll against an indexer that still has the old password — so a rotation cannot lock you out.
TLS certificate rotation
Credential rotation is automated; certificate/CA rotation is not yet — see the Roadmap.