This guide covers all OpenSearch Security CRDs for managing users, roles, and access control.
The Wazuh Operator provides Kubernetes-native management of OpenSearch security through Custom Resource Definitions (CRDs):
| CRD | Short Name | Purpose |
|---|---|---|
OpenSearchUser |
osuser |
Internal users |
OpenSearchRole |
osrole |
Security roles with permissions |
OpenSearchRoleMapping |
osrmap |
Map users/backends to roles |
OpenSearchTenant |
ostenant |
Multi-tenancy isolation |
OpenSearchActionGroup |
osag |
Reusable permission groups |
By default, the operator creates an admin user automatically when deploying a WazuhCluster. This user has full administrative privileges.
Default admin credentials:
- Username:
admin - Password: Auto-generated 24-character random password stored in Secret
<cluster-name>-indexer-credentials
Security Note: The operator never uses hardcoded default passwords. All passwords are cryptographically generated using
crypto/randfor maximum security.
# Get admin password
kubectl get secret -n wazuh wazuh-indexer-credentials \
-o jsonpath='{.data.admin-password}' | base64 -d
# Get admin username
kubectl get secret -n wazuh wazuh-indexer-credentials \
-o jsonpath='{.data.admin-username}' | base64 -dFor more details on credential management, see the Credentials Management Guide.
You can declare your own admin user using the OpenSearchUser CRD with defaultAdmin: true:
apiVersion: resources.wazuh.com/v1
kind: OpenSearchUser
metadata:
name: admin
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
defaultAdmin: true # This user becomes the default admin
passwordSecret:
secretName: my-admin-credentials
passwordKey: password
backendRoles:
- admin
openSearchRoles:
- all_access
description: "Custom admin user managed by CRD"| Scenario | Behavior |
|---|---|
No defaultAdmin: true CRD |
Operator creates auto-generated admin user |
One CRD with defaultAdmin: true |
That user becomes the admin |
Multiple CRDs with defaultAdmin: true |
First by creation timestamp is used, others emit warnings |
CRD named admin without defaultAdmin |
Normal user, auto-admin still created |
Important: The admin user is used by internal components (Dashboard, Filebeat) to authenticate with OpenSearch. If you override it, ensure the credentials are also updated in the relevant secrets.
Check which admin is active:
kubectl get wazuhcluster wazuh -n wazuh -o jsonpath='{.status.security}'Output:
{
"initialized": true,
"defaultAdminUser": "admin",
"defaultAdminSource": "crd", // or "auto"
"syncedUsers": 3
}Manage internal users in OpenSearch.
| Field | Type | Required | Description |
|---|---|---|---|
clusterRef.name |
string | Yes | WazuhCluster name |
defaultAdmin |
bool | No | Mark as default admin |
passwordSecret |
object | No | Password from Secret |
hash |
string | No | Pre-computed BCrypt hash |
backendRoles |
[]string | No | Backend roles for LDAP/SAML mapping |
openSearchRoles |
[]string | No | OpenSearch roles to assign |
attributes |
map | No | Custom user attributes |
description |
string | No | User description |
apiVersion: resources.wazuh.com/v1
kind: OpenSearchUser
metadata:
name: readonly-user
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
passwordSecret:
secretName: readonly-user-secret
passwordKey: password
openSearchRoles:
- readall
description: "Read-only user for dashboards"apiVersion: resources.wazuh.com/v1
kind: OpenSearchUser
metadata:
name: ldap-mapped-user
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
passwordSecret:
secretName: ldap-user-secret
passwordKey: password
backendRoles:
- security-team
- soc-analysts
attributes:
department: "Security"
location: "US-East"apiVersion: resources.wazuh.com/v1
kind: OpenSearchUser
metadata:
name: hashed-user
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
# BCrypt hash of password
hash: "$2y$12$abcdef..."
openSearchRoles:
- kibana_userGenerate hash:
# Using htpasswd
htpasswd -bnBC 12 "" 'MyPassword' | tr -d ':\n'
# Using Python
python3 -c "import bcrypt; print(bcrypt.hashpw(b'MyPassword', bcrypt.gensalt(12)).decode())"Define custom security roles with granular permissions.
| Field | Type | Required | Description |
|---|---|---|---|
clusterRef.name |
string | Yes | WazuhCluster name |
clusterPermissions |
[]string | No | Cluster-level permissions |
indexPermissions |
[]object | No | Index-level permissions |
tenantPermissions |
[]object | No | Tenant-level permissions |
description |
string | No | Role description |
| Field | Type | Required | Description |
|---|---|---|---|
indexPatterns |
[]string | Yes | Index patterns (e.g., logs-*) |
allowedActions |
[]string | Yes | Permitted actions |
dls |
string | No | Document-Level Security query |
fls |
[]string | No | Field-Level Security (include/exclude) |
maskedFields |
[]string | No | Fields to mask |
apiVersion: resources.wazuh.com/v1
kind: OpenSearchRole
metadata:
name: wazuh-alerts-reader
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
clusterPermissions:
- cluster_composite_ops_ro
indexPermissions:
- indexPatterns:
- "wazuh-alerts-*"
allowedActions:
- read
- search
description: "Read-only access to Wazuh alerts"apiVersion: resources.wazuh.com/v1
kind: OpenSearchRole
metadata:
name: team-a-alerts
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
indexPermissions:
- indexPatterns:
- "wazuh-alerts-*"
allowedActions:
- read
- search
# Only see alerts from specific agents
dls: '{"bool":{"must":[{"match":{"agent.name":"team-a-*"}}]}}'apiVersion: resources.wazuh.com/v1
kind: OpenSearchRole
metadata:
name: restricted-fields
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
indexPermissions:
- indexPatterns:
- "wazuh-alerts-*"
allowedActions:
- read
# Include only these fields
fls:
- "timestamp"
- "rule.description"
- "agent.name"
- "~data.srcip" # Exclude with ~
# Mask sensitive fields
maskedFields:
- "data.srcuser"
- "data.dstuser"apiVersion: resources.wazuh.com/v1
kind: OpenSearchRole
metadata:
name: logs-admin
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
clusterPermissions:
- cluster_monitor
- indices:admin/template/get
indexPermissions:
- indexPatterns:
- "logs-*"
- "application-*"
allowedActions:
- crud
- create_index
- manage
description: "Full admin for log indices"Cluster-level:
cluster_all- All cluster operationscluster_monitor- Monitor cluster healthcluster_composite_ops_ro- Read-only composite operationsmanage_snapshots- Manage snapshots
Index-level:
read- Read documentssearch- Search documentswrite- Write documentsdelete- Delete documentscrud- Create, read, update, deletecreate_index- Create indicesmanage- Manage index settings
Map users, backend roles, or hosts to OpenSearch roles.
| Field | Type | Required | Description |
|---|---|---|---|
clusterRef.name |
string | Yes | WazuhCluster name |
users |
[]string | No | Internal users to map |
backendRoles |
[]string | No | Backend roles to map (OR logic) |
andBackendRoles |
[]string | No | Backend roles (AND logic) |
hosts |
[]string | No | Host patterns to map |
description |
string | No | Mapping description |
Note: The CR name is used as the role name in OpenSearch.
apiVersion: resources.wazuh.com/v1
kind: OpenSearchRoleMapping
metadata:
name: wazuh-alerts-reader # Role name
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
users:
- readonly-user
- analyst-user
description: "Map users to alerts reader role"apiVersion: resources.wazuh.com/v1
kind: OpenSearchRoleMapping
metadata:
name: all_access
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
backendRoles:
- cn=admins,ou=groups,dc=example,dc=com
- security-adminsapiVersion: resources.wazuh.com/v1
kind: OpenSearchRoleMapping
metadata:
name: sensitive-data-access
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
# User must have BOTH backend roles
andBackendRoles:
- security-cleared
- data-access-approvedapiVersion: resources.wazuh.com/v1
kind: OpenSearchRoleMapping
metadata:
name: internal-full-access
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
hosts:
- "192.168.1.*"
- "10.0.0.*"Create isolated spaces in OpenSearch Dashboards for multi-tenancy.
| Field | Type | Required | Description |
|---|---|---|---|
clusterRef.name |
string | Yes | WazuhCluster name |
description |
string | No | Tenant description |
apiVersion: resources.wazuh.com/v1
kind: OpenSearchTenant
metadata:
name: team-security
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
description: "Security team private space"
---
apiVersion: resources.wazuh.com/v1
kind: OpenSearchTenant
metadata:
name: team-devops
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
description: "DevOps team private space"apiVersion: resources.wazuh.com/v1
kind: OpenSearchRole
metadata:
name: security-team-role
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
tenantPermissions:
- tenantPatterns:
- "team-security"
allowedActions:
- kibana_all_write # Full access
- tenantPatterns:
- "team-devops"
allowedActions:
- kibana_all_read # Read-onlyCreate reusable permission groups.
| Field | Type | Required | Description |
|---|---|---|---|
clusterRef.name |
string | Yes | WazuhCluster name |
allowedActions |
[]string | Yes | Actions or action groups |
type |
string | No | cluster, index, or all |
description |
string | No | Group description |
apiVersion: resources.wazuh.com/v1
kind: OpenSearchActionGroup
metadata:
name: wazuh-read
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
type: index
allowedActions:
- read
- search
- get
description: "Read operations for Wazuh indices"
---
apiVersion: resources.wazuh.com/v1
kind: OpenSearchActionGroup
metadata:
name: wazuh-write
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
type: index
allowedActions:
- write
- index
- bulk
description: "Write operations for Wazuh indices"
---
apiVersion: resources.wazuh.com/v1
kind: OpenSearchActionGroup
metadata:
name: wazuh-full
namespace: wazuh
spec:
clusterRefs:
- name: wazuh
namespace: wazuh
type: index
allowedActions:
- wazuh-read # Reference other action groups
- wazuh-write
- delete
description: "Full CRUD for Wazuh indices"All OpenSearch CRDs support:
- Phase:
Pending,Ready,Failed,Conflict - Drift Detection: Detects manual changes in OpenSearch
- Conflict Detection: Detects multiple CRDs targeting same resource
# List all users
kubectl get osuser -n wazuh
# Example output:
NAME CLUSTER PHASE DRIFT AGE
admin wazuh Ready false 2d
readonly-user wazuh Ready false 1d
analyst wazuh Ready true 5h # Drift detected!
# Get details
kubectl describe osuser analyst -n wazuhWhen driftDetected: true:
- Someone modified the resource directly in OpenSearch
- The operator will reconcile to match the CRD spec
- To keep manual changes, update the CRD
The operator emits Kubernetes events for OpenSearch security CRDs, visible via kubectl describe:
| Event Type | Reason | Description |
|---|---|---|
| Normal | Synced |
Resource successfully synchronized |
| Normal | Created |
Resource created in OpenSearch |
| Normal | Deleted |
Resource deleted from OpenSearch |
| Warning | SyncFailed |
Failed to sync to OpenSearch |
| Warning | ConnectionError |
Failed to connect to OpenSearch |
| Warning | PasswordError |
Failed to retrieve password (OpenSearchUser) |
| Warning | DeleteFailed |
Failed to delete from OpenSearch |
# View events for a specific resource
kubectl describe osuser my-user -n wazuh
# View all events in namespace
kubectl get events -n wazuh --field-selector involvedObject.kind=OpenSearchUserOpenSearch security CRDs enforce validation rules:
| CRD | Field | Constraint |
|---|---|---|
OpenSearchUser |
description |
Max 1024 characters |
OpenSearchRole |
description |
Max 1024 characters |
OpenSearchRole |
indexPermissions[].indexPatterns |
At least 1 item required |
OpenSearchRole |
indexPermissions[].allowedActions |
At least 1 item required |
OpenSearchRole |
tenantPermissions[].tenantPatterns |
At least 1 item required |
OpenSearchRole |
tenantPermissions[].allowedActions |
At least 1 item required |
- OpenSearch Index Management
- OpenSearch Security Examples - incl.
OpenSearchAuthConfig - CRD Reference