Skip to content
Ashish.
All posts
Diagram illustrating the flow of Keycloak events from the server to an immutable SIEM for compliance.
8 min readBackendAdvancedFeatured#keycloak#auditing#security-logging#compliance#gdpr#soc2#siem#event-logging

Keycloak Event Logging and Auditing for Compliance

A guide to configuring Keycloak event logging and auditing to meet compliance requirements like GDPR and SOC2.

By Ashish Srivastava

Keycloak generates Event instances whenever a critical security boundary is crossed. For advanced practitioners managing GDPR or SOC2 compliance, understanding this generation mechanism is the prerequisite for building a defensible audit trail. While default configurations route these objects to a console or local file, this approach fails compliance standards because local storage lacks the immutability required to prove non-repudiation. To meet rigorous standards, you must intercept the Event stream at the EventStoreProvider interface and redirect it to a centralized, write-once-read-many (WORM) storage system, such as a Security Information and Event Management (SIEM) solution.

The Event Mechanism and Serialization

When a user attempts authentication or an administrator modifies a realm, Keycloak's EventBuilder creates an Event object. This object captures the precise timestamp, the user ID, the client ID, the IP address, and the result status (SUCCESS, FAILURE, or ERROR). Crucially, the type field distinguishes between administrative actions, such as creating a user, and standard authentication flows.

Consider a scenario where a user named Alice (User ID: alice-123) fails to log in from IP 192.168.1.50 using the client web-app. Keycloak generates an event with type=LOGIN, result=FAILED, and clientId=web-app. This object is serialized into a JSON payload by the EventLogger component. In the default configuration, this payload is written directly to the server's stdout or a local file.

The mechanism relies on the org.keycloak.events.EventStoreProvider SPI. By default, Keycloak uses org.keycloak.events.log.ConsoleEventListenerProviderFactory or org.keycloak.events.log.FileEventListenerProviderFactory. These providers write the JSON directly to the local disk. While this satisfies basic debugging needs, it fails the SOC2 requirement for "system log integrity" because a malicious administrator with root access can modify or delete these local files before an audit occurs.

Configuration for Granular Event Capture

To capture the specific data points required for compliance, you must configure the server startup parameters. The --log-events flag controls which event types are recorded. Default behavior varies by version and configuration, so relying on defaults for security monitoring is risky. Compliance frameworks like SOC2 require logging all failed access attempts to detect brute-force attacks.

You must start the Keycloak server with flags that explicitly define event types. Note that --log-events-failed is a legacy JBoss CLI flag and is invalid in modern Quarkus-based Keycloak versions; event filtering is now handled via SPI or specific configuration properties.

kc.sh start \
  --log-level INFO \
  --log-events LOGIN,REGISTER,ADMIN,REALM

The --log-events parameter accepts a comma-separated list of event types. Enabling these flags ensures that rejected login attempts are persisted, which is critical for SOC2 security monitoring and general incident response. Without explicit configuration, gaps may appear in the timeline of a user's session history.

However, enabling these flags increases the volume of data. If you route this high-volume stream to a local file, you risk disk exhaustion. The mechanism here is a trade-off: you must balance the granularity of the audit log against the storage capacity of your infrastructure. For compliance, the volume is secondary to the completeness of the data; therefore, the output should be piped to a log aggregator rather than stored locally. While raw security logs support operational monitoring, GDPR Article 30 specifically focuses on the Record of Processing Activities (RoPA), which is a distinct artifact from raw security audit logs.

The Compliance Gap: Immutability and Retention

Local file logging fails two specific compliance mechanisms: tamper resistance and retention enforcement.

For SOC2, auditors require proof that logs cannot be altered by privileged users. A standard Linux log file allows a user with sudo privileges to truncate the file or modify timestamps. This breaks the chain of custody. Similarly, GDPR Article 30 requires that you can demonstrate who accessed what data and when. If the logs are stored on the same ephemeral instance that hosts the application, a container restart or disk failure can wipe the audit trail entirely.

To fix this, you must implement the EventStoreProvider SPI to push events to a SIEM (Security Information and Event Management) system. This moves the storage mechanism outside the Keycloak process boundary. The Keycloak server becomes a producer, and the SIEM becomes the consumer.

Threading Model Clarification: It is critical to understand that the onEvent method of a custom EventStoreProvider runs in the request thread by default unless the provider explicitly implements asynchronous dispatching. If the provider performs synchronous I/O (like a blocking HTTP call) within this method, it will block the authentication request, potentially causing timeouts during high load. To mitigate this, the provider should offload the network transmission to a background thread or use an async queue, ensuring the Keycloak server remains responsive while maintaining the integrity of the audit trail.

You can implement a custom SPI provider that serializes the Event object and sends it via HTTP POST or UDP to a Logstash, Splunk, or Datadog receiver. This provider runs in the same JVM but decouples the storage. The mechanism ensures that once the event leaves Keycloak, the server has no control over it, satisfying the "write-once" requirement of many compliance frameworks.

Data Mapping for SIEM Integration

Once the events are routed to a SIEM, the mechanism shifts to schema mapping. Keycloak JSON events contain nested structures that must be flattened for correlation. A typical Keycloak event payload looks like this:

{
  "time": "2023-10-27T10:00:00.000Z",
  "type": "LOGIN",
  "result": "SUCCESS",
  "clientId": "web-app",
  "clientName": "Web Application",
  "userId": "alice-123",
  "ipAddress": "192.168.1.50",
  "sessionId": "abc-xyz-123",
  "details": {
    "username": "alice",
    "clientIp": "192.168.1.50"
  }
}

A SIEM typically expects a normalized schema like the Elastic Common Schema (ECS). You must map type to event.action, result to event.outcome, and ipAddress to source.ip. This mapping is not automatic; it requires a transformation pipeline (e.g., Logstash filters or Fluentd parsers).

For GDPR compliance, the userId and ipAddress fields are personally identifiable information (PII). The mechanism of ingestion must include a step to hash or mask these fields if they are stored in a plaintext log repository that is not strictly access-controlled. However, for the audit trail itself, the raw PII must be retained to satisfy the "who, what, where" requirement of an investigation, provided the retention policy aligns with GDPR Article 17 (right to erasure) and Article 30.

Operationalizing the Audit Trail

The final mechanism is the retention policy. Keycloak generates events but does not manage log retention; the storage and retention policies are entirely determined by the external EventStoreProvider implementation or the receiving SIEM. You must configure the SIEM to archive logs for the duration specified by your compliance framework (often 1 year for SOC2, 6 months to 2 years for GDPR depending on the jurisdiction).

In a practical deployment, you would configure the custom SPI provider to buffer events in memory and flush them in batches. This prevents the Keycloak server from blocking on network I/O during high-load authentication spikes. If the SIEM is temporarily unreachable, the SPI should queue events locally (in a temporary file or database) before retrying, ensuring no audit data is lost. This buffering mechanism is essential for maintaining the integrity of the audit trail during network partitions.

By decoupling the event generation from the storage, you create a system where the Keycloak server acts purely as a source of truth for identity events, while the SIEM acts as the immutable ledger. This separation of concerns is the only way to satisfy the strictest interpretations of SOC2 and GDPR regarding log integrity and accessibility.

Conclusion

Implementing this architecture requires writing a small Java extension for Keycloak. The code must implement EventStoreProvider and override the onEvent method. Inside this method, you serialize the Event object and send it to your SIEM endpoint. This approach ensures that the audit log is not just a collection of text lines, but a structured data stream that can be queried, correlated, and retained according to legal standards.

The mechanism is robust because it relies on the Keycloak core to generate the event, but the storage is entirely external. This prevents a compromised Keycloak instance from altering its own history. The only remaining risk is the network path between Keycloak and the SIEM. You must encrypt this traffic using TLS to prevent interception or modification in transit. This end-to-end encryption is a standard requirement for GDPR data protection by design.

Common Pitfalls

  • Relying on Local Files: Storing logs on the same server or ephemeral container allows privileged users to tamper with or delete audit trails, violating SOC2 integrity controls.
  • Missing Encryption: Failing to encrypt the traffic between Keycloak and the SIEM exposes sensitive PII in transit, which is a direct violation of GDPR data protection principles.
  • Incorrect Threading Models: Implementing synchronous I/O within the onEvent method without explicit async handling can block the main request thread, degrading authentication performance.

Practical Takeaways

  • Implement a custom EventStoreProvider to route events to a centralized, immutable SIEM rather than local storage.
  • Configure explicit event flags (--log-events) to capture all necessary authentication and administrative actions, avoiding reliance on version-specific defaults.
  • Ensure your ingestion pipeline includes schema mapping (e.g., to ECS) and PII masking strategies where appropriate for long-term storage.

FAQ

Q: Does Keycloak natively support log retention policies? A: No. Keycloak generates events but does not manage their retention. You must configure the external SIEM or the custom EventStoreProvider to handle archiving and deletion based on your compliance requirements.

Q: Can I use the default console logger for GDPR compliance? A: No. The default console or file logger does not guarantee immutability or long-term retention, which are core requirements for GDPR Article 30 and SOC2.

Q: How do I handle PII in Keycloak events for GDPR? A: You must implement a transformation step in your SIEM ingestion pipeline (e.g., Logstash or Fluentd) to hash or mask fields like userId and ipAddress if they are stored in a repository that is not strictly access-controlled, while retaining raw data for the specific audit trail where legally permissible.

Related posts