|
Categories
|
|
 |
|
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:
- Log into your account at https://make.powerautomate.com
- Click Templates from the left menu
- Search for keywords teams webhook
- 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
- Get the Webhook URL
Click Edit on your new workflow
Click When a Teams webhook request is received box
In Security Onion Manager:
- Log into Manager's terminal. View your path. You'll need to know this later for the cron command.
Mine is /home/soadmin
pwd
- 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
- Make it executable
sudo chmod +x alerts-to-teams.sh
- Run it to test
sudo ./alerts-to-teams.sh
- Set it as a Cron job with root access
sudo crontab -e
*/5 * * * * /home/soadmin/alerts-to-teams.sh
- 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.
|
|
|
|
|