Vault-Wazuh Secrets Integration
|
Status: Stub - Research complete, implementation pending Origin: Post-incident action from CR-2026-02-25 Wazuh Credential Rotation |
Overview
Evaluate options for managing Wazuh credentials via HashiCorp Vault instead of static Kubernetes secrets.
|
This is NOT a simple Vault Agent Injector use case. OpenSearch (Wazuh indexer) stores password hashes in an internal security index ( |
Current State
| Component | Secret Source | Credential Storage | Rotation |
|---|---|---|---|
wazuh-indexer |
|
Internal security index |
Manual (requires securityadmin.sh) |
wazuh-dashboard |
|
opensearch_dashboards.yml |
Manual (restart required) |
wazuh-manager |
|
Internal config |
Manual |
Why Vault Agent Injector Won’t Work
Standard Vault Agent Injector pattern:
Vault KV → Agent Sidecar → /vault/secrets/file → Application reads file
Problem with OpenSearch/Wazuh:
Vault KV → Agent Sidecar → /vault/secrets/file → ???
↓
OpenSearch ignores file!
Uses internal .opendistro_security index
OpenSearch security plugin stores credentials as bcrypt hashes in a dedicated index. Changing the K8s secret alone does NOT change the password - you must:
-
Generate bcrypt hash of new password
-
Update
internal_users.yml -
Run
securityadmin.shto push to security index -
Restart dependent services
Integration Options
Option A: External Secrets Operator (Recommended)
Difficulty: Medium
┌──────────┐ ┌─────────────────────┐ ┌──────────────┐
│ Vault KV │────▶│ External Secrets │────▶│ K8s Secret │
│ │ │ Operator (ESO) │ │ indexer-cred │
└──────────┘ └─────────────────────┘ └──────────────┘
│
▼
┌─────────────────┐
│ CronJob runs │
│ securityadmin.sh│
└─────────────────┘
Pros: - ESO is well-supported, widely used - Secrets stay in Vault (source of truth) - K8s secrets auto-sync on Vault changes
Cons: - Still need CronJob/Job to run securityadmin.sh after sync - Two moving parts (ESO + password sync job)
Implementation:
# ExternalSecret resource
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: wazuh-indexer-cred
namespace: wazuh
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: indexer-cred
data:
- secretKey: username
remoteRef:
key: kv/data/domus/applications/wazuh/indexer
property: username
- secretKey: password
remoteRef:
key: kv/data/domus/applications/wazuh/indexer
property: password
Option B: Vault + Password Sync CronJob
Difficulty: Medium-High
Custom CronJob that: 1. Reads password from Vault 2. Generates bcrypt hash 3. Updates internal_users.yml 4. Runs securityadmin.sh 5. Restarts affected pods
Pros: - Full automation - No ESO dependency
Cons: - Custom code to maintain - Must handle securityadmin.sh failures - Complex error handling
Option C: OpenSearch Kubernetes Operator
Difficulty: High (migration required)
The official OpenSearch K8s Operator handles password hashing automatically:
You no longer need to provide password hashes for the admin or kibanaserver users. The operator will automatically generate password hashes from the credentials secrets.
Pros: - Native secret handling - Automatic hash generation - Production-grade operator
Cons: - Requires migrating from Wazuh deployment to OpenSearch operator - May lose Wazuh-specific customizations - Data migration complexity
Option D: Manual Rotation (Current)
Difficulty: Low
Use CR-2026-02-25 process.
Pros: - Simple, well-documented - No additional infrastructure - Full control
Cons: - Manual process - Human error risk - No automatic rotation
Recommendation
| Timeframe | Approach | Rationale |
|---|---|---|
Now |
Manual rotation (Option D) |
CR process is solid, low risk |
Q2 2026 |
External Secrets Operator (Option A) |
Moderate effort, good ROI |
Future |
Evaluate OpenSearch Operator (Option C) |
If Wazuh adds native support |
Prerequisites for Option A (ESO)
-
Install External Secrets Operator in k3s
-
Configure ClusterSecretStore for Vault
-
Create password sync Job/CronJob
-
Test in non-prod first