Audit Trail : Investigation de compromission de clé API
Reconstituez ce qu'a fait une clé API Datadog, d'où provenaient les requêtes et quelles ressources ont été affectées.
Prérequis
pup auth login # OAuth2 (recommandé)
# ou définir DD_API_KEY + DD_APP_KEY avec la portée audit_logs_read
Vous avez besoin de l'ID de la clé suspecte (pas la valeur de la clé). Trouvez-le dans l'interface Datadog sous Organization Settings > API Keys, ou dans un contexte affichant @metadata.api_key.id.
Flux d'investigation
Étape 1 — Établir la chronologie
pup audit-logs search --query "@metadata.api_key.id:KEY_ID" --from 90d --limit 200 -o json \
| jq '[.data[] | {
timestamp: .attributes.timestamp,
action: .attributes.attributes.action,
event: .attributes.attributes.evt.name,
resource_type: .attributes.attributes.asset.type,
resource_id: .attributes.attributes.asset.id,
endpoint: .attributes.attributes.http.url_details.path,
method: .attributes.attributes.http.method,
ip: .attributes.attributes.network.client.ip,
city: .attributes.attributes.network.client.geoip.city.name,
country: .attributes.attributes.network.client.geoip.country.name,
asn: .attributes.attributes.network.client.geoip.as.name
}]'
Étape 2 — Répartition géographique/IP
pup audit-logs search --query "@metadata.api_key.id:KEY_ID" --from 90d --limit 500 -o json \
| jq '[.data[] | {
country: .attributes.attributes.network.client.geoip.country.name,
asn: .attributes.attributes.network.client.geoip.as.name,
ip: .attributes.attributes.network.client.ip
}]
| group_by(.country)
| map({
country: .[0].country,
count: length,
asns: [.[].asn] | unique,
ips: [.[].ip] | unique
})
| sort_by(-.count)'
Étape 3 — Répartition des endpoints
pup audit-logs search --query "@metadata.api_key.id:KEY_ID" --from 90d --limit 500 -o json \
| jq '[.data[] | {
method: .attributes.attributes.http.method,
path: .attributes.attributes.http.url_details.path
}]
| group_by(.path)
| map({path: .[0].path, methods: [.[].method] | unique, count: length})
| sort_by(-.count)'
Étape 4 — Vérification des actions destructrices
pup audit-logs search --query "@metadata.api_key.id:KEY_ID @action:deleted" --from 90d -o json \
| jq '[.data[] | {
timestamp: .attributes.timestamp,
resource_type: .attributes.attributes.asset.type,
resource_id: .attributes.attributes.asset.id,
ip: .attributes.attributes.network.client.ip,
country: .attributes.attributes.network.client.geoip.country.name
}]'
Étape 5 — Quand la clé a-t-elle été créée et par qui ?
pup audit-logs search --query "@asset.type:api_key @asset.id:KEY_ID @action:created" --from 90d -o json \
| jq '[.data[] | {
created_at: .attributes.timestamp,
created_by: .attributes.attributes.usr.email,
creator_ip: .attributes.attributes.network.client.ip,
creator_country: .attributes.attributes.network.client.geoip.country.name
}]'
Indicateurs d'anomalie
| Signal | Pourquoi c'est important |
|---|---|
| Pays non présent dans la baseline normale de l'organisation | Possible exfiltration depuis une région inattendue |
| ASN est un fournisseur cloud/VPN (AWS, Cloudflare, NordVPN, etc.) | Trafic relayé ; origine masquée |
| Actions DELETE sur des monitors, dashboards ou pipelines de log | Sabotage potentiel |
| Rafale d'activité dans une fenêtre courte | Scraping automatisé ou exfiltration en masse |
| Activité en dehors des heures de bureau | Accès hors heures |
| Clé utilisée depuis plusieurs adresses IP simultanément | Clé partagée ou volée |
Format de sortie d'investigation
Key ID: <key_id>
Created: <timestamp> by <user_email>
Active period: <first_seen> to <last_seen>
Total events: <N>
Origins:
- <Country> (<ASN>): <N> events — [NORMAL / FLAG: first-time origin]
Endpoints called (top 5):
- <METHOD> <path>: <N> calls
Destructive actions: <N> deletions — [resource types affected]
Recommended actions:
1. Revoke the key immediately if not already done
2. Review affected resources: [list]
3. Check if any deleted resources need restoration
4. Audit who else had access to this key
Correction
Révoquez dans l'interface Datadog : Organization Settings > API Keys > Revoke.
Ou via API (nécessite la portée manage_api_keys) :
pup api-keys delete KEY_ID