AnalysisType: rule
Description: Alert when a duo administrator is locked out of their account.
DisplayName: "Duo Admin Lockout"
Enabled: true
Filename: duo_admin_lockout.py
Reference: https://duo.com/docs/adminapi
Severity: Medium
Tests:
- ExpectedResult: true
Log:
action: admin_lockout
description: '"message": "Admin temporarily locked out due to too many passcode attempts."'
isotimestamp: "2022-12-14 21:02:03"
timestamp: "2022-12-14 21:02:03"
username: Homer Simpson
Name: Admin lockout- invalid json
- ExpectedResult: true
Log:
action: admin_lockout
description: '{"message": "Admin temporarily locked out due to too many passcode attempts."}'
isotimestamp: "2022-12-14 21:02:03"
timestamp: "2022-12-14 21:02:03"
username: Homer Simpson
Name: Admin lockout- valid json
- ExpectedResult: false
Log:
action: bypass_create
description: '{"bypass": "", "count": 1, "valid_secs": 3600, "auto_generated": true, "remaining_uses": 1, "user_id": "D12345", "bypass_code_ids": ["A12345"]}'
isotimestamp: "2022-12-14 21:17:39"
object: target@example.io
timestamp: "2022-12-14 21:17:39"
username: Homer Simpson
Name: Bypass Create
DedupPeriodMinutes: 60
LogTypes:
- Duo.Administrator
RuleID: "Duo.Admin.Lockout"
Threshold: 1
# ------ paired body: duo_admin_lockout.py ------
import json
def rule(event):
# Return True to match the log event and trigger an alert.
return event.get("action", "") == "admin_lockout"
def title(event):
# If no 'dedup' function is defined, the return value
# of this method will act as deduplication string.
try:
desc = json.loads(event.get("description", {}))
message = desc.get("message", "<NO_MESSAGE_FOUND>")[:-1]
except ValueError:
message = "Invalid Json"
return (
f"Duo Admin [{event.get('username', '<NO_USER_FOUND>')}] is "
f"locked out. Reason: [{message}]."
)