Duo
Overview
Duo collects Cisco Duo MFA authentication logs by polling the
Duo Admin API
(GET /admin/v2/logs/authentication). Every record in Duo's Authentication Log is
collected — sign-ins, denials, fraud reports and enrollments, for every user, application
and factor — and forwarded with the original Duo JSON as the log body, so the Google SecOps
DUO_AUTH parser receives the complete record.
Supported types: Logs
Supported platforms
- Linux:
Logs - Windows:
Logs - macOS:
Logs
Typical uses:
- MFA monitoring in a SIEM — successful and failed sign-ins, with the user, application, factor and IP address of each attempt.
- Fraud and abuse detection — pushes users reported as fraud, lockouts, and sign-ins denied by policy.
- Enrollment tracking — users adding Duo Mobile, passkeys or security keys.
Requires collector v0.5.59 or later. Earlier releases do not build the Duo receiver, and the node will not be offered for older agents.
Authentication logs only. Duo's administrator, telephony, activity and authorization logs are separate Admin API endpoints and are not collected. Signing in to the Duo Admin Panel itself is recorded in the administrator log, not here.
Authentication
The node holds no secret. Attach one Duo credential:
| Credential | Required | Fields |
|---|---|---|
Duo (duo) | Yes | integration_key, secret_key |
Create it from an Admin API application in the Duo Admin Panel (requires the Owner role):
- Go to Applications → Protect an Application and choose Admin API.
- Under Permissions, select only Grant read log.
- Click Save Changes — the permission does not apply until saved.
- Copy the Integration key and Secret key into the
duocredential, and the API hostname into the node'sapi_hostname.
The collector signs every request itself (HMAC-SHA512, as the Admin API requires). The secret key is stored encrypted, is never returned by the API, and reaches the collector as a credential reference rather than in plaintext.
Use Admin API keys, not Auth API keys. Keys from any other Duo application type fail with
duo error 40102: Invalid identity in request credentials. A wrong secret key fails with40103: Invalid signature, and a missing or unsaved Grant read log permission with40301: Access forbidden.
Basic Configuration
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
| api_hostname | string | none | Yes | The Admin API hostname, e.g. api-xxxxxxxx.duosecurity.com. Host only, without https://. |
| poll_interval | integer (seconds) | 120 | No | How often new logs are fetched. Minimum 60, per Duo's guidance for the log endpoints. |
| lookback | integer (seconds) | 3600 | No | First start only: how far back to back-fill when no saved position exists. Minimum 180 and must be longer than delay; at most 15552000 (180 days, Duo's retention). |
| persist_offsets | boolean | true | No | Save the read position so a restart resumes from the last delivered record instead of replaying lookback. |
Lookback applies only once. After the first start the source always continues from its saved position, so changing
lookbackon a source that has already run has no effect. Set it before first publishing the node. To redo a back-fill, stop the collector, delete<collector home>/metadata/duo_offsets/<node name>, and start it again — records already delivered are sent again.
How polling works
Each poll asks Duo for every authentication log between the saved position and
now − delay, oldest first. Duo returns up to limit records per request and a
next_offset while more remain; the source follows it until the window is fully read. The first
poll runs immediately when the collector starts, then one every poll_interval.
After each page is accepted downstream the read position is saved, and records already
delivered are skipped by their Duo transaction ID — so a restart, crash or downstream failure
neither duplicates nor skips records. With persist_offsets on, the position is written under
<collector home>/metadata/duo_offsets/<node name>, one directory per node.
New sign-ins arrive 2–4 minutes after they happen. Duo intentionally returns nothing newer than two minutes, and the next poll can be up to
poll_intervalaway.
Failures never lose records. A rate limit (429), server error or network failure is retried with backoff (1s → 60s, up to 6 attempts); if it still fails, the next poll requests the same window again. A page the pipeline temporarily refuses is re-sent on the next poll.
Output
Each Duo authentication log becomes one log record:
| Duo field | Record |
|---|---|
| (whole record JSON) | Body |
isotimestamp (or timestamp) | Timestamp |
result | Severity — success → INFO; denied, failure, fraud → WARN; error → ERROR |
txid, event_type, result, reason, factor, email | Attributes duo.txid, duo.event_type, duo.result, duo.reason, duo.factor, duo.email |
user.name / user.key | Attributes duo.user.name / duo.user.key |
application.name / application.key | Attributes duo.application.name / duo.application.key |
access_device.ip / access_device.hostname | Attributes duo.access_device.ip / duo.access_device.hostname |
auth_device.ip / auth_device.name | Attributes duo.auth_device.ip / duo.auth_device.name |
(constant / from api_hostname) | Resource attributes vendor = duo, duo.api_hostname |
Fields not listed (location, browser, OS, groups, trust assessments, …) remain in the body.
Advanced Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
| delay | integer (seconds) | 120 | How far behind now each fetch stops. Range 120–3600: Duo returns nothing newer than two minutes, so a shorter delay would skip records. |
| limit | integer | 1000 | Records per request (1–1000, Duo's maximum). Larger windows are read page by page, so no records are missed. |
| timeout | integer (seconds) | 30 | HTTP timeout for each request. A timed-out request is retried; the read position does not move. |
Leave
delayat120unless clocks drift. If the collector's clock may run ahead of Duo's, a slightly larger value (e.g.150) keeps the edge of each window safely inside what Duo has released.
The connection is always HTTPS with Duo's certificate verified. Behind a proxy that re-signs HTTPS traffic the source fails with
duo TLS certificate not trusted; install the proxy's CA certificate in the collector host's trust store.
Example Configuration
{
"api_hostname": "api-xxxxxxxx.duosecurity.com", // required
"poll_interval": 120, // default; minimum 60
"lookback": 3600, // default; first start only, minimum 180
"persist_offsets": true, // default
"advanced": {
"delay": 120, // default; minimum 120
"limit": 1000, // default; Duo max
"timeout": 30, // default
},
}
Attach a duo credential to the node; integration_key and secret_key are not part of
the node config.
Metrics
| Metric Name | Description |
|---|---|
prx_src_records_received_total{signal="logs"} | Log records emitted, one per Duo authentication log. |
prx_src_bytes_received_total | API response bytes read from Duo. |
prx_src_pipeline_latency_seconds | Downstream handoff time per page. |
prx_src_errors_total{reason} | Source errors by reason — io for request failures, including credential and permission errors. |
prx_src_parse_errors_total{parser="json"} | A page or record returned by Duo that could not be fully decoded. |
prx_src_records_dropped_total{reason} | backpressure — the pipeline refused a page; it is re-sent on the next poll. permanent — the pipeline permanently rejected a page; it is dropped. |
prx_health_status{component="receiver"} | 1 after start and after each successful poll; 0 when a poll fails (credentials, permission, Duo or network down) and at shutdown. |