Secure OpenSearch with cnspec
Scan OpenSearch clusters and the security plugin against security and compliance best practices with cnspec.
Scan your OpenSearch clusters to find security risks before they become incidents. cnspec evaluates anonymous access, audit logging, the enabled authentication realms, the internal user database, roles and the index patterns they reach, and role mappings from the OpenSearch security plugin.
cnspec inventories the cluster through read-only requests to the REST API. It never reads your documents.
This provider is for OpenSearch. Elasticsearch serves a different security API and is covered by the separate elasticsearch provider; pointing this one at an Elasticsearch node is detected and refused.
If you're new to cnspec, start with the Quickstart. For an overview of every database cnspec can scan, see the database scanning overview.
Prerequisites
To scan OpenSearch with cnspec, you must have:
- cnspec installed on your workstation
- Network access to the REST API (port 9200 by default)
- A user mapped to a role with
cluster_monitorand read access to the security API
Authenticate
cnspec authenticates with basic auth. The security plugin serves HTTPS with a self-signed demo certificate by default, so a default local install typically needs --tls-insecure:
cnspec shell opensearch os.contoso.com --user admin --ask-pass --tls-insecureFor a cluster served with a real CA, verify it instead:
cnspec shell opensearch os.contoso.com --user auditor --ask-pass --tls-ca ca.pemPrefer a least-privileged account for auditing. With only monitor-level access the cluster resolves while the security posture, users, roles, and role mappings come back empty rather than failing the scan, so confirm they're populated before trusting a passing check that reads them.
Connection options
| Option | Description |
|---|---|
--host | Cluster hostname or IP address (also accepted as the positional argument) |
--port | REST API port (default 9200) |
--scheme | http or https (default https) |
--user, -u | User for basic authentication |
--password, -p | Password, or --ask-pass to be prompted |
--tls-ca | Path to a CA certificate to verify the cluster |
--tls-insecure | Skip TLS certificate verification, for the default self-signed demo certificate |
Verify with a quick OpenSearch check
cnspec shell opensearch os.contoso.com --user admin --ask-pass --tls-insecurecnspec> opensearch.cluster { name version distribution healthStatus nodeCount }
opensearch.cluster: {
name: "docker-cluster"
version: "2.17.1"
distribution: "opensearch"
healthStatus: "yellow"
nodeCount: 1
}If cnspec connects and shows the version, you're ready to scan.
Scan OpenSearch
cnspec scan opensearch os.contoso.com --user auditor --ask-passWhen a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy, along with a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues. To learn more about reading scan results, read Understand cnspec Results.
Mondoo doesn't yet ship an out-of-the-box OpenSearch policy, so use the checks below as a starting point and create your own policies to meet your specific requirements.
Explore and test checks interactively
cnspec shell opensearch os.contoso.com --user auditor --ask-passRead the security posture
cnspec> opensearch.cluster.security { anonymousAccessEnabled auditLoggingEnabled doNotFailOnForbidden enabledAuthRealms }
opensearch.cluster.security: {
anonymousAccessEnabled: false
auditLoggingEnabled: true
doNotFailOnForbidden: false
enabledAuthRealms: ["basic_internal_auth_domain"]
}List internal users
cnspec> opensearch.cluster.users { name isReserved isStatic securityRoles backendRoles }List roles and their cluster permissions
cnspec> opensearch.cluster.roles { name isReserved clusterPermissions }Inspect a role's index permissions
cnspec> opensearch.cluster.roles.where(name == "readers").first.indexPermissions { indexPatterns allowedActions hasFieldLevelSecurity hasFieldMasking }List role mappings
cnspec> opensearch.cluster.roleMappings { name users backendRoles hosts }Example security checks
Ensure anonymous access is disabled
cnspec> opensearch.cluster.security.anonymousAccessEnabled == false
[ok] value: trueEnsure audit logging is enabled
cnspec> opensearch.cluster.security.auditLoggingEnabled == true
[ok] value: trueEnsure authorization failures are not hidden
doNotFailOnForbidden filters results instead of returning a 403, which makes an over-broad or under-privileged role hard to notice:
cnspec> opensearch.cluster.security.doNotFailOnForbidden == false
[ok] value: trueEnsure an authentication realm is configured
cnspec> opensearch.cluster.security.enabledAuthRealms.length > 0
[ok] value: trueEnsure no custom role grants every cluster permission
cnspec> opensearch.cluster.roles.where(isReserved == false).none(clusterPermissions.contains("*"))
[ok] value: trueEnsure no custom role grants every action on every index
cnspec> opensearch.cluster.roles.where(isReserved == false).all(indexPermissions.none(indexPatterns.contains("*") && allowedActions.contains("*")))
[ok] value: trueEnsure all_access is not mapped broadly
all_access is the built-in administrator role, and a mapping is how someone actually receives it:
cnspec> opensearch.cluster.roleMappings.where(name == "all_access") { users backendRoles hosts }Ensure no role mapping matches on host alone
A host-based mapping grants the role to anything connecting from that address, with no regard for who is connecting:
cnspec> opensearch.cluster.roleMappings.all(hosts.length == 0)
[ok] value: trueEnsure the demo internal users have been removed
A default install ships with well-known accounts such as kibanaserver and logstash:
cnspec> opensearch.cluster.users.where(isReserved == false && isStatic == false) { name securityRoles }Ensure hidden roles are reviewed
A hidden role doesn't appear in the security dashboards but still grants what it grants:
cnspec> opensearch.cluster.roles.where(isHidden) { name clusterPermissions }Review roles that read every field
Field-level security and field masking are what keep a broad read grant from exposing sensitive columns:
cnspec> opensearch.cluster.roles.where(isReserved == false) { name indexPermissions.where(hasFieldLevelSecurity == false && hasFieldMasking == false) { indexPatterns allowedActions } }Learn more
- OpenSearch Resource Pack Reference: every OpenSearch resource and field cnspec can query
- Secure Elasticsearch with cnspec: for Elasticsearch clusters
- Write Effective MQL: guide to authoring checks and queries
OpenSearch masks user password hashes in the security API, so the provider doesn't report whether a user has a password set.