Azure Sentinel Integration

Azure Sentinel Integration

Azure Sentinel Integration Guide

Forward Keycloak authentication events and logs to Microsoft Azure Sentinel for cloud-native security monitoring, AI-powered threat detection, and seamless Microsoft ecosystem integration.

Overview

This guide walks you through configuring Skycloak to send security events to Azure Sentinel using HTTP webhooks and the Azure Monitor HTTP Data Collector API. Azure Sentinel is ideal for organizations using Microsoft 365, Azure AD, and Azure cloud services.

⚠️
Known limitation: Skycloak’s generic HTTP webhook forwarder does not support Azure Sentinel’s native HTTP Data Collector authentication. See Step 3 before setting up a destination — you’ll need an intermediate relay to reach Sentinel today.

What You’ll Achieve

  • Real-time authentication event streaming to Azure Sentinel
  • Integration with Azure’s AI-powered threat detection
  • Unified security monitoring across Microsoft services
  • KQL-based queries and custom workbooks

Prerequisites

Before starting, ensure you have:

  1. Skycloak Requirements

    • Enterprise plan with SIEM Integration add-on
    • Workspace Owner or Admin permissions
  2. Azure Requirements

    • Active Azure subscription
    • Azure Sentinel workspace (Log Analytics workspace with Sentinel enabled)
    • Permission to create Data Connectors and access workspace keys

Step 1: Set Up Azure Sentinel Workspace

Create Log Analytics Workspace (if needed)

  1. Sign in to Azure Portal

  2. Search for Log Analytics workspaces

  3. Click CreateLog Analytics workspace

  4. Configure:

    • Subscription: Select your subscription
    • Resource Group: Create or select existing
    • Name: skycloak-sentinel-workspace
    • Region: Choose region closest to your Skycloak deployment
    • Pricing Tier: Pay-as-you-go (default)
  5. Click Review + CreateCreate

Enable Azure Sentinel

  1. Search for Microsoft Sentinel
  2. Click CreateAdd Microsoft Sentinel to a workspace
  3. Select your Log Analytics workspace
  4. Click Add

Step 2: Create Custom Data Connector

Get Workspace Credentials

  1. Navigate to your Log Analytics workspace
  2. Click Agents (under Settings)
  3. Copy and save:
    • Workspace ID: Long alphanumeric string (e.g., a1b2c3d4-...)
    • Primary Key: Base64-encoded key

Determine Data Collector API Endpoint

The endpoint format is:

https://<WorkspaceID>.ods.opinsights.azure.com/api/logs?api-version=2016-04-01

Replace <WorkspaceID> with your Workspace ID.

Example:

https://a1b2c3d4-e5f6-7890-abcd-ef1234567890.ods.opinsights.azure.com/api/logs?api-version=2016-04-01

Step 3: Known Limitation on Native Sentinel Authentication

Read this before configuring a destination.

Skycloak’s HTTP webhook forwarder sends a static header on every request: either Authorization: Bearer <token> or Authorization: Basic <token>, exactly as configured on the destination. Azure Sentinel’s HTTP Data Collector API does not accept either of those — it requires Authorization: SharedKey <WorkspaceID>:<signature>, where <signature> is an HMAC-SHA256 hash computed per request over that request’s date, content length, and HTTP method (the curl example under Troubleshooting below shows the real header shape). Because the signature has to be recomputed from each request’s own metadata, no static bearer or basic token can stand in for it.

⚠️
Known limitation: Skycloak’s current generic HTTP/syslog SIEM export does not support Azure Sentinel’s native HMAC-signed ingestion API. A destination configured with “Bearer Token” auth and your Log Analytics Primary Key will not authenticate — Azure rejects every request.

Workaround: relay through an intermediate service

To reach Sentinel today, put something capable of computing the SharedKey signature between Skycloak and Azure:

  1. Stand up a relay that accepts an HTTP POST and forwards it to the Data Collector API endpoint (from Step 2), computing the SharedKey signature per request. An Azure Logic App (HTTP trigger → the built-in “Send Data” Log Analytics action, which signs requests for you) or a small Azure Function are the two most common choices.
  2. In Skycloak, configure an HTTP Webhook destination pointed at your relay’s URL, using whatever auth (Bearer token or Basic auth) your relay expects — this part works exactly as documented for any other webhook destination.
  3. Configure event filtering and batching as usual:
    • Event Filtering: select the events to forward, e.g. LOGIN, LOGIN_ERROR, LOGOUT, UPDATE_PASSWORD, UPDATE_EMAIL, REGISTER, CODE_TO_TOKEN, REFRESH_TOKEN
    • Include Server Logs: enable for detailed application logs (increases relay/ingestion volume)
    • Batching: batch size 500, batch interval 120 seconds are reasonable starting points
  4. Have your relay add the Log-Type: KeycloakEvents and time-generated-field: timestamp headers (or their Logic App connector equivalents) when it calls the real Data Collector API — these are what create the KeycloakEvents_CL custom table and tell Azure which field is the event timestamp.

Once your relay is signing requests correctly, everything from Step 4 onward — verifying ingestion, analytics rules, workbooks — applies as documented below; Sentinel doesn’t distinguish a relayed request from a direct one.

Step 4: Verify Data Flow

Check Azure Monitor Logs

  1. Navigate to your Log Analytics workspace
  2. Click Logs (under General)
  3. Run this KQL query:
KeycloakEvents_CL
| take 10

Note: It may take 5-15 minutes for the custom table to appear after first data ingestion.

Verify Table Schema

Check the automatically created table schema:

KeycloakEvents_CL
| getschema

Test with Recent Login Event

  1. Log in to one of your Keycloak realms
  2. Wait 3-5 minutes for batch processing and ingestion
  3. Run this query:
KeycloakEvents_CL
| where event_type_s == "LOGIN"
| order by TimeGenerated desc
| take 1

Note: Azure automatically adds _s suffix to string fields, _d to numbers, _b to booleans.

Step 5: Create Analytics Rules

Example: Detect Brute Force Attacks

  1. Navigate to Microsoft SentinelAnalytics
  2. Click CreateScheduled query rule
  3. Configure:

General:

  • Name: Keycloak Brute Force Attempt
  • Description: Detects multiple failed login attempts from same IP
  • Severity: High
  • MITRE ATT&CK: Credential Access → Brute Force (T1110)

Rule Query:

KeycloakEvents_CL
| where event_type_s == "LOGIN_ERROR"
| where TimeGenerated > ago(5m)
| summarize FailedAttempts=count() by ip_address_s, username_s, bin(TimeGenerated, 5m)
| where FailedAttempts >= 5
| project TimeGenerated, username_s, ip_address_s, FailedAttempts

Query Scheduling:

  • Run query every: 5 minutes
  • Lookup data from the last: 5 minutes

Alert Threshold: Greater than 0 results

Entity Mapping:

  • Account: username_s
  • IP: ip_address_s
  1. Click Review and CreateCreate

Example: Detect Suspicious Login Locations

Alert on logins from unusual geographic locations:

let UserLoginLocations = KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where TimeGenerated > ago(30d)
| extend Location = geo_info_from_ip_address(ip_address_s)
| summarize KnownCountries=make_set(Location.country) by username_s;
KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where TimeGenerated > ago(5m)
| extend Location = geo_info_from_ip_address(ip_address_s)
| join kind=inner UserLoginLocations on username_s
| where not(Location.country in (KnownCountries))
| project TimeGenerated, username_s, ip_address_s, NewCountry=Location.country

Example: Privileged Account Changes

Alert on password changes for admin accounts:

KeycloakEvents_CL
| where event_type_s == "UPDATE_PASSWORD"
| where username_s contains "admin" or username_s contains "root"
| project TimeGenerated, username_s, ip_address_s, realm_s

Step 6: Create Custom Workbooks

Example: Authentication Monitoring Workbook

  1. Navigate to Microsoft SentinelWorkbooks
  2. Click Add workbookEdit
  3. Add visualizations:

Query 1: Login Success Rate (Donut Chart)

KeycloakEvents_CL
| where event_type_s in ("LOGIN", "LOGIN_ERROR")
| where TimeGenerated > ago(24h)
| summarize Count=count() by event_type_s
| render piechart

Query 2: Failed Logins Timeline (Line Chart)

KeycloakEvents_CL
| where event_type_s == "LOGIN_ERROR"
| where TimeGenerated > ago(7d)
| summarize FailedLogins=count() by bin(TimeGenerated, 1h)
| render timechart

Query 3: Top Failed Login Users (Bar Chart)

KeycloakEvents_CL
| where event_type_s == "LOGIN_ERROR"
| where TimeGenerated > ago(24h)
| summarize Failures=count() by username_s
| top 10 by Failures desc
| render barchart

Query 4: Login Activity Map

KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where TimeGenerated > ago(24h)
| extend Location = geo_info_from_ip_address(ip_address_s)
| summarize Logins=count() by Location.country, Location.latitude, Location.longitude
| render scatterchart with (kind=map)
  1. Click Done EditingSave As
  2. Name: Keycloak Authentication Monitoring

Useful KQL Queries

Recent Failed Logins

KeycloakEvents_CL
| where event_type_s == "LOGIN_ERROR"
| where TimeGenerated > ago(1h)
| project TimeGenerated, username_s, ip_address_s, realm_s
| order by TimeGenerated desc

User Login History

KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where username_s == "john.doe"
| where TimeGenerated > ago(30d)
| project TimeGenerated, ip_address_s, realm_s, client_id=details_s.client_id
| order by TimeGenerated desc

Login Activity by Realm

KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where TimeGenerated > ago(7d)
| summarize LoginCount=count() by realm_s
| order by LoginCount desc

Account Changes Summary

KeycloakEvents_CL
| where event_type_s in ("UPDATE_PASSWORD", "UPDATE_EMAIL", "REGISTER", "DELETE_ACCOUNT")
| where TimeGenerated > ago(24h)
| summarize Count=count() by event_type_s, username_s
| order by Count desc

Concurrent Login Detection

KeycloakEvents_CL
| where event_type_s == "LOGIN"
| where TimeGenerated > ago(1h)
| summarize UniqueIPs=dcount(ip_address_s) by username_s, bin(TimeGenerated, 5m)
| where UniqueIPs > 2
| project TimeGenerated, username_s, UniqueIPs

Integration with Microsoft Services

Azure AD Integration

Correlate Keycloak events with Azure AD sign-ins:

KeycloakEvents_CL
| where event_type_s == "LOGIN"
| join kind=inner (
    SigninLogs
    | where TimeGenerated > ago(1h)
) on $left.username_s == $right.UserPrincipalName
| project KeycloakLogin=TimeGenerated, AzureADLogin=TimeGenerated1, username_s, ip_address_s

Microsoft 365 Correlation

Find users with suspicious activity in both Keycloak and M365:

let SuspiciousKeycloakUsers = KeycloakEvents_CL
| where event_type_s == "LOGIN_ERROR"
| where TimeGenerated > ago(1h)
| summarize FailedAttempts=count() by username_s
| where FailedAttempts > 5;
OfficeActivity
| where TimeGenerated > ago(1h)
| join kind=inner SuspiciousKeycloakUsers on $left.UserId == $right.username_s
| project TimeGenerated, Operation, UserId, ClientIP

Troubleshooting

No Data Appearing in Sentinel

  1. Check Skycloak destination status:

    • Navigate to SIEM Integration page
    • Verify “Healthy” status
    • Check “Events Sent” metric is increasing
  2. Verify Workspace ID and Key:

    • Ensure Workspace ID matches your Log Analytics workspace
    • Verify Primary Key is correct (not Secondary Key)
    • Check for trailing spaces in credentials
  3. Test HTTP endpoint manually:

    # Use curl to test (replace placeholders):
    curl -X POST \
      -H "Content-Type: application/json" \
      -H "Log-Type: KeycloakEvents" \
      -H "Authorization: SharedKey <WorkspaceID>:<SharedKey>" \
      "https://<WorkspaceID>.ods.opinsights.azure.com/api/logs?api-version=2016-04-01" \
      -d '[{"test": "data", "timestamp": "2024-01-15T10:00:00Z"}]'
  4. Check Azure Monitor Logs ingestion:

    • Navigate to your workspace → Usage and estimated costs
    • Verify data is being ingested

Custom Table Not Appearing

  • Wait 5-15 minutes: Azure takes time to create custom tables on first ingestion
  • Check Log-Type header: Must be in Custom Headers configuration
  • Verify data format: Azure expects JSON array with timestamp field

Query Performance Issues

  • Use time filters: Always filter by TimeGenerated for better performance
    KeycloakEvents_CL
    | where TimeGenerated > ago(1h)  // Always add this
  • Limit result sets: Use take or top to limit results
  • Optimize joins: Join on indexed fields when possible

High Ingestion Costs

  1. Filter events: Only forward critical event types
  2. Reduce batch frequency: Increase batch interval to 300-600 seconds
  3. Disable server logs: Turn off “Include Logs” if not needed
  4. Set retention policies: Configure data retention to manage costs
    • Navigate to workspace → Usage and estimated costsData Retention

Best Practices

  1. Use Managed Identity: For production, use Azure Managed Identity instead of shared keys (requires Azure-hosted connector)
  2. Enable Diagnostic Logging: Turn on Azure Monitor diagnostic logs for troubleshooting
  3. Create Automation Playbooks: Use Azure Logic Apps to automate incident response
  4. Set Up Notifications: Configure email/SMS alerts for critical incidents
  5. Regular Rule Tuning: Review and adjust analytics rules based on false positives
  6. Data Retention: Balance retention needs with cost (default: 90 days)
  7. Use Watchlists: Create watchlists for VIP users, known good IPs, etc.

Next Steps

  • Create custom analytics rules for your security requirements
  • Build comprehensive workbooks for monitoring
  • Set up automation playbooks for incident response
  • Integrate with Azure Security Center for unified security posture
  • Configure data retention policies based on compliance needs
  • Explore Microsoft Sentinel content hub for pre-built solutions

Support

For assistance:

Last updated on