background 
Kris' Tech Blog ( and stuff ) Home About Kris Contact Kris

Categories


All by Date

Linux

Security Onion

Xymon & Networks

Religion

General





Kris Springer's Tech Blog - Security Onion Alerts to Teams channel
Security Onion Alerts to Teams channel 7-26-25
Kris Springer


Security Onion has an existing method to output alerts to various platforms, but enabling that requires a Pro license. The following method is how to accomplish the results with a script and cron. It will check for ‘high’ and ‘critical’ Custom detection alerts every 5 minutes, then sends the ‘name’ of the alert to Teams. If you want to adjust what Elasticsearch Indexes the script monitors, ask Skynet.

Assumptions:
  • You have a running Security Onion server.
  • You have permissions to setup a Teams Workflow in Power Automate to get the required Webhook URL.
  • You know something about Linux commands, cron, and scripts. If you have questions, ask Skynet for help.

In Power Automate website:
  1. Log into your account at https://make.powerautomate.com
  2. Click Templates from the left menu
  3. Search for keywords teams webhook
  4. Choose Send webhook alerts to a channel to start the wizard
    Click Continue button
    Microsoft Teams Team: pick your desired Team
    Microsoft Teams Channel: pick your desired Channel
    Click Create button
  5. Get the Webhook URL
    Click Edit on your new workflow
    Click When a Teams webhook request is received box

In Security Onion Manager:
  1. Log into Manager's terminal. View your path. You'll need to know this later for the cron command.
    Mine is /home/soadmin
    pwd
  2. Create the following script and enter the required cred's and webhook url. You'll need an SO user/pass with at least 'auditor' role for the script to query Alerts. I setup a user via the SOC web gui just for this purpose.
    sudo nano alerts-to-teams.sh
    #!/bin/bash # Elasticsearch URL and index to pull alerts from ES_URL="https://localhost:9200" INDEX=".ds-logs-detections*" # Elasticsearch credentials USERNAME="ENTER-HERE" PASSWORD="ENTER-HERE" # Teams webhook URL - SIEM Alerts channel TEAMS_WEBHOOK_URL="ENTER-HERE" # Calculate the current time and the time 5 minutes ago current_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ") start_time=$(date -u -d '5 minutes ago' +"%Y-%m-%dT%H:%M:%SZ") # Elasticsearch query with time range filter read -r -d '' QUERY <<-EOF { "query": { "bool": { "must": [ { "terms": { "event.severity_label": ["high", "critical"] } }, { "range": { "@timestamp": { "gte": "$start_time", "lte": "$current_time" } } } ] } }, "_source": ["rule.name"] } EOF # Execute the query with authentication and skip SSL verification response=$(curl -s -u "$USERNAME:$PASSWORD" -k -X POST "$ES_URL/$INDEX/_search" -H 'Content-Type: application/json' -d "$QUERY") # Check if the curl command was successful if [ $? -ne 0 ]; then echo "Error: Failed to connect to Elasticsearch" exit 1 fi # Check if the response contains hits hits=$(echo "$response" | jq '.hits.total.value') if [ "$hits" -eq "0" ]; then echo "No high or critical severity events found in the last 5 minutes." exit 0 fi # Parse and display the 'rule.name' fields rule_names=$(echo "$response" | jq -r '.hits.hits[]._source.rule.name') # Display the parsed rule names if [ -z "$rule_names" ]; then echo "No high or critical severity event names found." else echo "Parsed rule names (last 5 minutes):" echo "$rule_names" # Format the rule names for Teams message, preserving newlines formatted_rule_names=$(echo "$rule_names" | sed 's/$/\\n/' | sed 's/"/\\"/g') # Static Adaptive Card payload teams_message='{ "type": "message", "attachments": [ { "contentType": "application/vnd.microsoft.card.adaptive", "content": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.2", "body": [ { "type": "TextBlock", "text": "[SIEM Alert](https://so.yourdomain.com)", "size": "medium", "spacing": "none", "wrap": true }, { "type": "TextBlock", "text": "'"$formatted_rule_names"'", "size": "medium", "spacing": "none", "wrap": true } ] } } ] }' # Send the alert to Teams, capturing response body curl_response=$(curl -s -v -w "%{http_code}" -X POST -H 'Content-Type: application/json' --data "$teams_message" "$TEAMS_WEBHOOK_URL" -o curl_response_body.log 2> curl_debug.log) # Check if the Teams webhook call was successful (Logic App expects 202) if [ "$curl_response" -eq 202 ]; then echo "Alert sent to Teams." else echo "Error: Failed to send alert to Teams (HTTP status: $curl_response)" echo "DEBUG: curl verbose output saved to curl_debug.log" echo "DEBUG: curl response body saved to curl_response_body.log" cat curl_response_body.log exit 1 fi fi
  3. Make it executable
    sudo chmod +x alerts-to-teams.sh
  4. Run it to test
    sudo ./alerts-to-teams.sh
  5. Set it as a Cron job with root access
    sudo crontab -e
    */5 * * * * /home/soadmin/alerts-to-teams.sh
  6. The script should now be running every 5 minutes, so test a ‘high’ or ‘critical’ alert in SO and see if Slack gets notified. A failed gui login will flag a red alert. If you want to adjust what Elasticsearch Indexes the script monitors, ask Skynet.






© Copyright 2025 WarriorSon Productions. All rights reserved.