What/Why
What are you proposing?
A standalone OpenSearch plugin (opensearch-audit-logging) that provides audit logging as an independent, first-class capability — decoupled from the security plugin.
Today, audit logging is tightly embedded inside the security plugin (org.opensearch.security.auditlog package). It only works when the security plugin is installed and fine-grained access control (FGAC) is enabled. This proposal extracts audit logging into a separate plugin that:
- Works with or without the security plugin — no FGAC dependency
- Captures data-plane operations: REST requests, transport actions, document reads/writes
- Enriches events with user identity when the security plugin is present (via
ThreadContext)
- Provides an extensible sink architecture — built-in Log4j and OpenSearch index sinks, plus custom sinks loadable via reflection
- Runs alongside the existing security plugin with zero breaking changes
- Follows the same build system, project structure, and release conventions as existing OpenSearch plugins (Gradle with
opensearch.opensearchplugin, GitHub Actions CI, plugin-descriptor.properties)
What users have asked for this feature?
-
Audit logging without FGAC: Users running OpenSearch for log analytics and observability often don't need FGAC but still require audit trails for compliance. The current hard dependency on the security plugin forces them to adopt the full security stack just for audit logging.
-
Separation of concerns: Audit logging is a compliance and observability feature, not strictly a security feature. Multiple users have expressed that coupling it to the security plugin adds unnecessary complexity and configuration overhead.
-
Custom SIEM integration: Users integrating OpenSearch with enterprise SIEM systems (Splunk, Datadog, custom pipelines) must modify the security plugin itself since the current sink architecture is internal and not independently extensible.
What problems are you trying to solve?
-
When deploying OpenSearch for log analytics without FGAC, an operations team wants to track who accessed which indices and when, so they can meet their organization's compliance requirements (SOC2, HIPAA, PCI-DSS, ISO 27001, GDPR) without enabling the full security plugin.
-
When running OpenSearch with basic authentication only, a platform engineer wants to get audit trails of all data-plane operations, so they can investigate security incidents and demonstrate compliance to auditors.
-
When integrating OpenSearch with an enterprise SIEM, a security engineer wants to route audit events to a custom destination via a simple interface, so they can correlate OpenSearch activity with events from other systems without forking the security plugin.
-
When operating OpenSearch at scale, a cluster administrator wants audit logging that can be upgraded and configured independently from the security plugin, so they can reduce the blast radius of plugin updates and simplify their operational runbook.
-
When building a managed OpenSearch offering, a service provider wants a modular audit logging component with an extensible sink architecture, so they can add platform-specific log destinations without modifying the open-source plugin.
What is the developer experience going to be?
Installation
# Standard OpenSearch plugin installation
bin/opensearch-plugin install opensearch-audit-logging
Configuration (opensearch.yml)
# Master switch
plugins.audit.enabled: true
# Layer control
plugins.audit.enable_rest: true
plugins.audit.enable_transport: true
# Category filtering (same categories as security plugin for compatibility)
plugins.audit.disabled_rest_categories:
- AUTHENTICATED
- GRANTED_PRIVILEGES
plugins.audit.disabled_transport_categories:
- AUTHENTICATED
- GRANTED_PRIVILEGES
# User/request filtering
plugins.audit.ignore_users:
- kibanaserver
plugins.audit.ignore_requests:
- "SearchRequest"
- "indices:data/read/*"
# Request body and index resolution
plugins.audit.log_request_body: true
plugins.audit.resolve_indices: true
plugins.audit.resolve_bulk_requests: false
plugins.audit.exclude_sensitive_headers: true
# Thread pool for async event dispatch
plugins.audit.threadpool.size: 10
plugins.audit.threadpool.max_queue_len: 100000
# Sink: Log4j (file-based logging)
plugins.audit.sink.log4j.enabled: true
plugins.audit.sink.log4j.logger_name: "opensearch.audit"
plugins.audit.sink.log4j.level: "INFO"
# Sink: Internal OpenSearch index
plugins.audit.sink.index.enabled: true
plugins.audit.sink.index.name: "'audit-'YYYY.MM.dd"
# Compliance (document-level tracking)
plugins.audit.compliance.enabled: true
plugins.audit.compliance.write_watched_indices:
- "sensitive-*"
plugins.audit.compliance.read_watched_fields:
sensitive-data:
- "ssn"
- "credit_card"
plugins.audit.compliance.write_metadata_only: false
plugins.audit.compliance.read_metadata_only: false
# Security plugin integration (when security plugin is co-installed)
plugins.audit.security_integration.enabled: true
plugins.audit.security_integration.read_user_from_threadcontext: true
REST API
GET /_plugins/_audit/config # Retrieve current audit configuration
PUT /_plugins/_audit/config # Update configuration (hot-reload, no restart)
PATCH /_plugins/_audit/config # Partial update
GET /_plugins/_audit/health # Sink health status
GET /_plugins/_audit/stats # Event counts per category, per sink
Extensible Sink Interface
Third-party developers can implement custom sinks:
public interface AuditSink extends Closeable {
/**
* Store an audit event. Returns true if successfully stored.
*/
boolean store(AuditEvent event);
/**
* Check if the sink is healthy and accepting events.
*/
boolean isHealthy();
}
Custom sinks are loaded via reflection — no compile-time dependency required:
plugins.audit.sink.custom.type: "com.example.MySplunkAuditSink"
plugins.audit.sink.custom.config.endpoint: "https://splunk.example.com:8088"
plugins.audit.sink.custom.config.token: "${SPLUNK_HEC_TOKEN}"
Audit Event Model
Every audit event contains:
{
"audit_format_version": 5,
"audit_timestamp": "2026-04-15T00:00:00.000Z",
"audit_category": "REST_REQUEST",
"audit_origin": "REST",
"audit_node_id": "node-1",
"audit_node_name": "opensearch-node-1",
"audit_cluster_name": "my-cluster",
"audit_request_effective_user": "admin",
"audit_request_effective_user_roles": ["all_access"],
"audit_request_remote_address": "192.168.1.100",
"audit_request_action": "indices:data/read/search",
"audit_trace_indices": ["my-index-*"],
"audit_trace_resolved_indices": ["my-index-2026.04.15"],
"audit_request_body": "{\"query\":{\"match_all\":{}}}",
"audit_request_headers": {}
}
The format is compatible with the existing security plugin audit event format (format version 4+), ensuring existing log parsers and dashboards continue to work during migration.
Tracked Event Categories
| Category |
Layer |
Description |
REST_REQUEST |
REST |
Any REST API call received by the cluster |
TRANSPORT_ACTION |
Transport |
Internal transport actions between nodes |
INDEX_EVENT |
Transport |
Index administration (create, delete, alias, force merge) |
DOCUMENT_WRITE |
Index |
Document indexed, updated, or deleted (compliance) |
DOCUMENT_READ |
Index |
Document read during search/get (compliance) |
FAILED_LOGIN |
REST/Transport |
Authentication failure (when security plugin present) |
AUTHENTICATED |
REST/Transport |
Successful authentication (when security plugin present) |
MISSING_PRIVILEGES |
Transport |
Authorization failure (when security plugin present) |
GRANTED_PRIVILEGES |
Transport |
Successful authorization (when security plugin present) |
SSL_EXCEPTION |
REST/Transport |
TLS/SSL errors |
BAD_HEADERS |
REST/Transport |
Spoofed or invalid headers |
Are there any security considerations?
- The plugin is read-only with respect to user data — it observes requests but does not authenticate, authorize, or modify them.
- Sensitive headers (
Authorization, Cookie, etc.) are excluded from audit events by default.
- Request body logging is configurable and can be disabled for sensitive deployments.
- The audit configuration index (
.plugins-audit-config) and audit data indices (audit-*) should be protected via the security plugin's index-level permissions when FGAC is enabled.
- The plugin reads user identity from
ThreadContext using the same transient key (_opendistro_security_user) that the security plugin sets. It does not access the security plugin's internal index or configuration directly.
Are there any breaking changes to the API?
No. This is a new plugin with new REST endpoints under /_plugins/_audit/. It does not modify any existing OpenSearch APIs or any security plugin APIs. The security plugin's existing audit logging continues to work unchanged.
What is the user experience going to be?
Scenario 1: OpenSearch without security plugin
User installs audit plugin → configures sinks → all REST/transport/index
operations are logged with timestamp, source IP, action, indices.
User identity shows as "<anonymous>" since there's no authentication layer.
Scenario 2: OpenSearch with security plugin (parallel operation)
Both plugins installed → security plugin handles auth as usual →
audit plugin reads user identity from ThreadContext →
events enriched with username, roles, backend roles →
both plugins log independently (dual logging during transition)
Scenario 3: Custom SIEM integration
User implements AuditSink interface → packages as JAR →
places on classpath → configures via opensearch.yml →
audit events flow to custom destination (Splunk, Datadog, etc.)
Architecture Diagram
┌─────────────────────────────────────────────┐
│ OpenSearch Node │
│ │
REST Request ────────►│ ┌───────────────────┐ ┌────────────────┐ │
│ │ Security Plugin │ │ Audit Plugin │ │
│ │ (optional) │ │ (NEW) │ │
│ │ │ │ │ │
│ │ • Authenticates │ │ Interceptors: │ │
│ │ • Authorizes │ │ • ActionFilter│ │
│ │ • Sets user in │ │ • IndexOpList.│ │
│ │ ThreadContext ──────►• SecurityCtx │ │
│ │ │ │ Enricher │ │
│ │ (unchanged) │ │ │ │ │
│ └───────────────────┘ │ ▼ │ │
│ │ SinkRouter │ │
│ │ ┌──────────┐ │ │
│ │ │Log4jSink │ │ │
│ │ │IndexSink │ │ │
│ │ │CustomSink│ │ │
│ │ └──────────┘ │ │
│ └────────────────┘ │
└─────────────────────────────────────────────┘
How Interception Works (OpenSearch Extension Points)
REST Request
│
▼
ActionFilter.apply() ◄── Standard OpenSearch plugin API
│ (same as alerting, anomaly-detection)
├── Captures: action, source IP, indices, request body
├── Reads user from ThreadContext (if security plugin present)
├── Builds AuditEvent
└── Sends to SinkRouter (async, non-blocking)
│
▼
Normal request processing continues (zero latency impact on request path)
Index Write/Delete
│
▼
IndexingOperationListener ◄── Standard OpenSearch plugin API
│ (same as security plugin's
│ ComplianceIndexingOperationListener)
├── Captures: index, doc ID, shard ID, operation
├── Builds AuditEvent (DOCUMENT_WRITE)
└── Sends to SinkRouter (async)
Sink Architecture
AuditEvent
│
▼
SinkRouter
(async thread pool)
│
┌───────────┼───────────┐
▼ ▼ ▼
Log4jSink IndexSink CustomSink
(built-in) (built-in) (via reflection)
│ │ │
▼ ▼ ▼
Log files audit-* Any destination
index (SIEM, webhook,
cloud logging, etc.)
Phased Rollout (No Breaking Changes)
Phase 1 (Initial Release):
• Audit plugin runs alongside security plugin
• Both log independently — acceptable for validation
• Zero changes to security plugin required
Phase 2 (Integration):
• Security plugin optionally forwards auth events to audit plugin
• Security plugin's own audit sinks can be set to noop
• Single audit pipeline
Phase 3 (Migration Complete):
• Audit logging code removed from security plugin
• Audit plugin is the sole audit system
• Security plugin focuses on auth/authz only
Are there breaking changes to the User Experience?
No. This is purely additive. The existing security plugin audit logging is completely unaffected. Users can adopt the new plugin at their own pace.
Why should it be built? Any reason not to?
Why build it:
-
Modularity: Audit logging is a compliance/observability concern, not strictly a security concern. Decoupling it follows the OpenSearch project's philosophy of modular, composable plugins. This is the same pattern as opensearch-storage-encryption being decoupled from core.
-
Broader reach: Today, audit logging is gated behind FGAC. Removing this dependency makes audit logging available to the entire OpenSearch user base — including users who run OpenSearch for log analytics, observability, and search without FGAC.
-
Extensibility: The AuditSink interface enables the community to build integrations without forking the security plugin. This lowers the barrier for SIEM vendors and managed service providers to add OpenSearch audit support.
-
Reduced complexity in security plugin: The security plugin's auditlog package (~20 classes across config/, impl/, routing/, sink/) adds significant surface area. Extracting it reduces the security plugin's maintenance burden and test matrix.
-
Independent release cycle: Audit logging improvements and bug fixes can ship without waiting for security plugin releases.
Reasons not to:
-
Dual logging during transition: During Phase 1, both plugins may log the same events. This is mitigated by allowing users to disable the security plugin's audit sinks (plugins.security.audit.type: noop) when they adopt this plugin.
-
Additional repo to maintain: Offset by reduced complexity in the security plugin.
What will it take to execute?
Build System & Project Structure
The plugin will follow the same conventions as existing OpenSearch plugins (security, anomaly-detection, alerting):
opensearch-audit-logging/
├── .github/
│ └── workflows/ # CI: build, test, release (GitHub Actions)
├── build-tools/ # Shared build utilities
├── gradle/
│ └── wrapper/ # Gradle wrapper
├── release-notes/ # Per-version release notes
├── scripts/ # Helper scripts
├── src/
│ ├── main/
│ │ ├── java/org/opensearch/audit/
│ │ │ ├── AuditLoggingPlugin.java
│ │ │ ├── interceptor/
│ │ │ ├── event/
│ │ │ ├── sink/
│ │ │ ├── config/
│ │ │ └── ...
│ │ └── resources/
│ │ └── plugin-descriptor.properties
│ ├── test/ # Unit tests
│ └── integrationTest/ # Integration tests (separate source set)
├── build.gradle # Uses opensearch.opensearchplugin
├── settings.gradle
├── gradle.properties
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER_GUIDE.md
├── LICENSE.txt # Apache 2.0
├── MAINTAINERS.md
├── NOTICE.txt
└── README.md
build.gradle approach:
- Uses
opensearch.opensearchplugin Gradle plugin (same as security plugin)
opensearch.pluginzip for artifact packaging
opensearch.testclusters for integration testing
- Spotless for code formatting, Checkstyle for style enforcement
- JaCoCo for code coverage
- Maven Central publishing via
maven-publish plugin
Dependencies
- Compile-time: OpenSearch core APIs only (
ActionFilter, IndexingOperationListener, ThreadContext, ClusterService, Settings)
- Runtime: Log4j2 (already provided by OpenSearch), Jackson (already provided by OpenSearch)
- No dependency on the security plugin JAR — user identity is read from
ThreadContext via reflection/string parsing, not by importing security plugin classes
- No external SDK dependencies — keeps the plugin lightweight
Any remaining open questions?
-
Event format version: Should the plugin use the same audit event format version as the security plugin (currently v4) for backward compatibility, or define a new format (v5) with additional fields?
-
System index protection: Should the audit configuration index (.plugins-audit-config) and audit data indices (audit-*) be registered as system indices?
-
Security plugin integration mechanism: For Phase 2, what is the preferred mechanism for the security plugin to forward auth-specific events (FAILED_LOGIN, MISSING_PRIVILEGES, etc.) to this plugin?
- Option A: ThreadContext injection (proven pattern — already used by the Jetty proxy integration)
- Option B: SPI/ExtensiblePlugin interface
- Option C: Event bus (e.g., Guava EventBus, already used by security plugin for config changes)
-
Compliance features scope: Should document-level compliance tracking (watched indices, field-level read tracking, write diffs) be included in the initial release or deferred to a follow-up?
-
Dashboards UI: Should a companion Dashboards plugin for audit log configuration and visualization be part of this proposal, or a separate future proposal?
-
Naming: Is opensearch-audit-logging the right name, or should it be opensearch-audit or opensearch-audit-log?
What/Why
What are you proposing?
A standalone OpenSearch plugin (
opensearch-audit-logging) that provides audit logging as an independent, first-class capability — decoupled from the security plugin.Today, audit logging is tightly embedded inside the security plugin (
org.opensearch.security.auditlogpackage). It only works when the security plugin is installed and fine-grained access control (FGAC) is enabled. This proposal extracts audit logging into a separate plugin that:ThreadContext)opensearch.opensearchplugin, GitHub Actions CI,plugin-descriptor.properties)What users have asked for this feature?
Audit logging without FGAC: Users running OpenSearch for log analytics and observability often don't need FGAC but still require audit trails for compliance. The current hard dependency on the security plugin forces them to adopt the full security stack just for audit logging.
Separation of concerns: Audit logging is a compliance and observability feature, not strictly a security feature. Multiple users have expressed that coupling it to the security plugin adds unnecessary complexity and configuration overhead.
Custom SIEM integration: Users integrating OpenSearch with enterprise SIEM systems (Splunk, Datadog, custom pipelines) must modify the security plugin itself since the current sink architecture is internal and not independently extensible.
What problems are you trying to solve?
When deploying OpenSearch for log analytics without FGAC, an operations team wants to track who accessed which indices and when, so they can meet their organization's compliance requirements (SOC2, HIPAA, PCI-DSS, ISO 27001, GDPR) without enabling the full security plugin.
When running OpenSearch with basic authentication only, a platform engineer wants to get audit trails of all data-plane operations, so they can investigate security incidents and demonstrate compliance to auditors.
When integrating OpenSearch with an enterprise SIEM, a security engineer wants to route audit events to a custom destination via a simple interface, so they can correlate OpenSearch activity with events from other systems without forking the security plugin.
When operating OpenSearch at scale, a cluster administrator wants audit logging that can be upgraded and configured independently from the security plugin, so they can reduce the blast radius of plugin updates and simplify their operational runbook.
When building a managed OpenSearch offering, a service provider wants a modular audit logging component with an extensible sink architecture, so they can add platform-specific log destinations without modifying the open-source plugin.
What is the developer experience going to be?
Installation
# Standard OpenSearch plugin installation bin/opensearch-plugin install opensearch-audit-loggingConfiguration (
opensearch.yml)REST API
Extensible Sink Interface
Third-party developers can implement custom sinks:
Custom sinks are loaded via reflection — no compile-time dependency required:
Audit Event Model
Every audit event contains:
{ "audit_format_version": 5, "audit_timestamp": "2026-04-15T00:00:00.000Z", "audit_category": "REST_REQUEST", "audit_origin": "REST", "audit_node_id": "node-1", "audit_node_name": "opensearch-node-1", "audit_cluster_name": "my-cluster", "audit_request_effective_user": "admin", "audit_request_effective_user_roles": ["all_access"], "audit_request_remote_address": "192.168.1.100", "audit_request_action": "indices:data/read/search", "audit_trace_indices": ["my-index-*"], "audit_trace_resolved_indices": ["my-index-2026.04.15"], "audit_request_body": "{\"query\":{\"match_all\":{}}}", "audit_request_headers": {} }The format is compatible with the existing security plugin audit event format (format version 4+), ensuring existing log parsers and dashboards continue to work during migration.
Tracked Event Categories
REST_REQUESTTRANSPORT_ACTIONINDEX_EVENTDOCUMENT_WRITEDOCUMENT_READFAILED_LOGINAUTHENTICATEDMISSING_PRIVILEGESGRANTED_PRIVILEGESSSL_EXCEPTIONBAD_HEADERSAre there any security considerations?
Authorization,Cookie, etc.) are excluded from audit events by default..plugins-audit-config) and audit data indices (audit-*) should be protected via the security plugin's index-level permissions when FGAC is enabled.ThreadContextusing the same transient key (_opendistro_security_user) that the security plugin sets. It does not access the security plugin's internal index or configuration directly.Are there any breaking changes to the API?
No. This is a new plugin with new REST endpoints under
/_plugins/_audit/. It does not modify any existing OpenSearch APIs or any security plugin APIs. The security plugin's existing audit logging continues to work unchanged.What is the user experience going to be?
Scenario 1: OpenSearch without security plugin
Scenario 2: OpenSearch with security plugin (parallel operation)
Scenario 3: Custom SIEM integration
Architecture Diagram
How Interception Works (OpenSearch Extension Points)
Sink Architecture
Phased Rollout (No Breaking Changes)
Are there breaking changes to the User Experience?
No. This is purely additive. The existing security plugin audit logging is completely unaffected. Users can adopt the new plugin at their own pace.
Why should it be built? Any reason not to?
Why build it:
Modularity: Audit logging is a compliance/observability concern, not strictly a security concern. Decoupling it follows the OpenSearch project's philosophy of modular, composable plugins. This is the same pattern as opensearch-storage-encryption being decoupled from core.
Broader reach: Today, audit logging is gated behind FGAC. Removing this dependency makes audit logging available to the entire OpenSearch user base — including users who run OpenSearch for log analytics, observability, and search without FGAC.
Extensibility: The
AuditSinkinterface enables the community to build integrations without forking the security plugin. This lowers the barrier for SIEM vendors and managed service providers to add OpenSearch audit support.Reduced complexity in security plugin: The security plugin's
auditlogpackage (~20 classes acrossconfig/,impl/,routing/,sink/) adds significant surface area. Extracting it reduces the security plugin's maintenance burden and test matrix.Independent release cycle: Audit logging improvements and bug fixes can ship without waiting for security plugin releases.
Reasons not to:
Dual logging during transition: During Phase 1, both plugins may log the same events. This is mitigated by allowing users to disable the security plugin's audit sinks (
plugins.security.audit.type: noop) when they adopt this plugin.Additional repo to maintain: Offset by reduced complexity in the security plugin.
What will it take to execute?
Build System & Project Structure
The plugin will follow the same conventions as existing OpenSearch plugins (security, anomaly-detection, alerting):
build.gradleapproach:opensearch.opensearchpluginGradle plugin (same as security plugin)opensearch.pluginzipfor artifact packagingopensearch.testclustersfor integration testingmaven-publishpluginDependencies
ActionFilter,IndexingOperationListener,ThreadContext,ClusterService,Settings)ThreadContextvia reflection/string parsing, not by importing security plugin classesAny remaining open questions?
Event format version: Should the plugin use the same audit event format version as the security plugin (currently v4) for backward compatibility, or define a new format (v5) with additional fields?
System index protection: Should the audit configuration index (
.plugins-audit-config) and audit data indices (audit-*) be registered as system indices?Security plugin integration mechanism: For Phase 2, what is the preferred mechanism for the security plugin to forward auth-specific events (FAILED_LOGIN, MISSING_PRIVILEGES, etc.) to this plugin?
Compliance features scope: Should document-level compliance tracking (watched indices, field-level read tracking, write diffs) be included in the initial release or deferred to a follow-up?
Dashboards UI: Should a companion Dashboards plugin for audit log configuration and visualization be part of this proposal, or a separate future proposal?
Naming: Is
opensearch-audit-loggingthe right name, or should it beopensearch-auditoropensearch-audit-log?