Enriching Splunk IP Addresses with n8n and AI in Real Time
Enriching Splunk IP Addresses with n8n and AI in Real Time
When youβre hunting in Splunk and see an IP address in your logs, you want to know: who is this, and should I care? Manually looking it up in a threat intel feed takes you out of the flow. What if Splunk could enrich that IP automatically β in real time β using free tools?
This guide walks through building a real-time IP enrichment pipeline using Splunk webhook β n8n workflow β AI analysis β threat intelligence lookup, all with free/open-source tools.
The Problem
Youβre looking at a log search in Splunk. You see this IP address in your data:
185.220.101.42
Your brain goes: βHmm, should I care?β You tab over to VirusTotal, paste it in, wait for results. Five minutes gone. In that time, the incident has progressed.
What if you could get that enrichment before you even click on the IP?
The Architecture
Splunk (alert/webhook)
β
n8n webhook trigger
β
βββββββββββββββββββββββββββββββ
β Enrichment Pipeline β
β β
β 1. IP geolocation lookup β
β 2. Threat intel lookup β
β 3. AI analysis of results β
β 4. Return enriched data β
βββββββββββββββββββββββββββββββ
β
Splunk (enriched search results)
Step 1: Set Up the n8n Webhook
- Open your n8n instance and create a new workflow
- Add a Webhook node
- Set HTTP method to
POST - Set the webhook URL (youβll get a unique URL)
- Enable βRespond to Webhookβ so n8n can return data
- Set HTTP method to
- Save and activate the workflow
Step 2: IP Geolocation Lookup
Add an HTTP Request node after the webhook:
{
"ip": "="
}
Use a free geolocation API. Options:
- ip-api.com (free, no API key needed, 45 req/min)
- ipgeolocation.io (free tier, API key required)
- ipapi.co (free tier, 30K requests/month)
Example ip-api.com call:
GET https://ip-api.com/json/?fields=status,message,country,regionName,city,isp,org,as,mobile,proxy,hosting,query
Step 3: Threat Intelligence Lookup
Add another HTTP Request node for threat intel. Free options:
- VirusTotal (free tier: 50 req/day, API key required)
- AbuseIPDB (free tier: 100 req/day, API key required)
- Shodan (free tier: 1 req/min, API key required)
Example AbuseIPDB:
GET https://api.abuseipdb.com/api/v2/check
Headers: Key:
Query: ip=&maxAgeInDays=90
Step 4: AI Analysis
Add an AI Agent or HTTP Request node to call an AI model:
{
"prompt": "Based on the following IP enrichment data, assess whether this IP is suspicious. Return a concise risk assessment.\n\nIP: \nGeolocation: , \nISP: \nThreat Score: /100\nReports: \n\nReturn: risk_level (low/medium/high/critical), summary, and recommended action."
}
You can use:
- Ollama (local, free) β see the AI Security Toolchain post for setup
- LM Studio (local, free)
- OpenRouter (pay-per-use, many models)
Step 5: Return Enriched Data to Splunk
Configure the final node to return JSON:
{
"ip": "",
"risk_level": "",
"summary": "",
"country": "",
"city": "",
"isp": "",
"threat_score": ""
}
Step 6: Trigger from Splunk
There are two ways to connect Splunk to your n8n webhook: alert-driven (push) or lookup-driven (pull). Iβll cover both.
Method A: Alert-Driven (Splunk pushes to n8n)
This is the simplest approach. Splunk sends an HTTP POST to your n8n webhook whenever a search matches.
- Create a saved search with the query you want to monitor:
index=* | rex field=_raw "(?P<ip_address>\d+\.\d+\.\d+\.\d+)" | stats count by ip_address(Adjust the regex to match your actual IP field name.)
- Set up the alert:
- Click Save As β Alert
- Set the alert condition (e.g., βResults > 0β)
- Under Alert action, choose Webhook (if the Webhook action is installed) or Custom script
-
If Webhook action is not available (Splunk Free doesnβt include it by default), use a Custom Script action:
Create a script at
$SPLUNK_HOME/bin/scripts/enrich_ip.py:#!/usr/bin/env python3 import json import sys import urllib.request # Read the search results from Splunk's JSON payload payload = json.loads(sys.stdin.read()) # Extract the IP address from the results for event in payload.get('results', []): ip = event.get('ip_address') if ip: # Send to n8n webhook webhook_url = "https://your-n8n-instance/webhook/enrich-ip" data = json.dumps({"ip": ip}).encode() req = urllib.request.Request( webhook_url, data=data, headers={"Content-Type": "application/json"}, method="POST" ) try: with urllib.request.urlopen(req) as response: result = json.loads(response.read()) print(f"Enriched {ip}: {result.get('risk_level', 'unknown')}") except Exception as e: print(f"Enrichment failed for {ip}: {e}", file=sys.stderr) - Make the script executable:
chmod +x $SPLUNK_HOME/bin/scripts/enrich_ip.py - Configure the alert action in Splunk UI:
- Alert action β Run a script
- Script path:
enrich_ip.py - Pass search results: Yes
Method B: Lookup-Driven (Splunk pulls from n8n)
This approach enriches IPs in real-time during a search rather than via alerts. You create a Python lookup that calls n8n for each IP.
- Create a lookup definition in
$SPLUNK_HOME/etc/apps/splunk_ip_enrich/local/transforms.conf:[ip_enrich_lookup] filename = enrich_ip.py match_type = WILDCARD(ip) - Create the Python lookup script in
$SPLUNK_HOME/etc/apps/splunk_ip_enrich/bin/enrich_ip.py:#!/usr/bin/env python3 import json import sys import urllib.request def enrich_ip(ip_address): """Call n8n webhook to enrich an IP address.""" webhook_url = "https://your-n8n-instance/webhook/enrich-ip" try: data = json.dumps({"ip": ip_address}).encode() req = urllib.request.Request( webhook_url, data=data, headers={"Content-Type": "application/json"}, method="POST" ) with urllib.request.urlopen(req, timeout=5) as response: result = json.loads(response.read()) return { "ip_risk_level": result.get("risk_level", "unknown"), "ip_summary": result.get("summary", ""), "ip_country": result.get("country", ""), "ip_city": result.get("city", ""), "ip_isp": result.get("isp", ""), "ip_threat_score": result.get("threat_score", "") } except Exception: # Return empty enrichment on failure return { "ip_risk_level": "unknown", "ip_summary": "", "ip_country": "", "ip_city": "", "ip_isp": "", "ip_threat_score": "" } # Splunk passes rows as JSON lines via stdin for line in sys.stdin: row = json.loads(line) ip = row.get("ip", "") if ip: enrichment = enrich_ip(ip) row.update(enrichment) print(json.dumps(row)) - Make it executable:
chmod +x $SPLUNK_HOME/etc/apps/splunk_ip_enrich/bin/enrich_ip.py - Register the lookup in
$SPLUNK_HOME/etc/apps/splunk_ip_enrich/local/props.conf:[default] LOOKUP-enrich_ip = ip_enrich_lookup ip OUTPUT ip_risk_level,ip_summary,ip_country,ip_city,ip_isp,ip_threat_score - Use it in a search:
index=* | rex field=_raw "(?P<ip>\d+\.\d+\.\d+\.\d+)" | lookup ip_enrich_lookup ip OUTPUT ip_risk_level,ip_country,ip_isp | where ip_risk_level IN ("high", "critical")
Method C: Manual Enrichment from Splunk Search Results
You can enrich IPs manually from any Splunk search without setting up alerts or lookups. This is the lowest-friction way to start β pick IPs from your search results and send them to n8n on demand.
- Run your search and identify the IP addresses you want to enrich:
index=firewall action=allow | stats count by src_ip -
Open the results table in Splunk. In the top-right corner, click Actions β Export.
- Copy the IP you want to enrich and send it to your n8n webhook. The quickest way is a simple curl command (or use a browser):
curl -X POST https://your-n8n-instance/webhook/enrich-ip \ -H "Content-Type: application/json" \ -d '{"ip": "185.220.101.42"}' - For bulk enrichment, export results to CSV and pipe them:
# Export your search results to CSV, then: while IFS=',' read -r ip rest; do curl -s -X POST https://your-n8n-instance/webhook/enrich-ip \ -H "Content-Type: application/json" \ -d "{\"ip\": \"$ip\"}" echo done < enriched_ips.csv - Or use Splunkβs built-in βRun a scriptβ action on a single event:
- In any search results, click on an event
- Click Actions β Run a script (if configured)
- Point to a simple enrichment script
This method requires zero Splunk configuration changes. You enrich IPs manually as you find them, which is great for ad-hoc investigations and testing your pipeline before automating it.
Method D: Alert-Driven (Automatic)
This is the automated version of Method C β Splunk pushes to n8n whenever a search matches, with no manual intervention needed.
Setup:
- Create a saved search with the query you want to monitor:
index=* | rex field=_raw "(?P<ip_address>\d+\.\d+\.\d+\.\d+)" | stats count by ip_address(Adjust the regex to match your actual IP field name.)
- Set up the alert:
- Click Save As β Alert
- Set the alert condition (e.g., βResults > 0β)
- Under Alert action, choose Webhook (if the Webhook action is installed) or Custom script
-
If Webhook action is not available (Splunk Free doesnβt include it by default), use a Custom Script action:
Create a script at
$SPLUNK_HOME/bin/scripts/enrich_ip.py:#!/usr/bin/env python3 import json import sys import urllib.request # Read the search results from Splunk's JSON payload payload = json.loads(sys.stdin.read()) # Extract the IP address from the results for event in payload.get('results', []): ip = event.get('ip_address') if ip: # Send to n8n webhook webhook_url = "https://your-n8n-instance/webhook/enrich-ip" data = json.dumps({"ip": ip}).encode() req = urllib.request.Request( webhook_url, data=data, headers={"Content-Type": "application/json"}, method="POST" ) try: with urllib.request.urlopen(req) as response: result = json.loads(response.read()) print(f"Enriched {ip}: {result.get('risk_level', 'unknown')}") except Exception as e: print(f"Enrichment failed for {ip}: {e}", file=sys.stderr) - Make the script executable:
chmod +x $SPLUNK_HOME/bin/scripts/enrich_ip.py - Configure the alert action in Splunk UI:
- Alert action β Run a script
- Script path:
enrich_ip.py - Pass search results: Yes
Which Method to Choose?
| Method | Best For | Complexity |
|---|---|---|
| Method C (Manual) | Ad-hoc enrichment, testing | None |
| Method D (Alert) | Monitoring specific searches | Low |
| Method B (Lookup) | Real-time enrichment in any search | Medium |
Start with Method C to validate your n8n pipeline works. Once youβre happy with the enrichment quality, move to Method D for automation or Method B for the most powerful inline enrichment.
Putting It All Together: The n8n Workflow
Hereβs the full workflow structure:
[Webhook Trigger]
β
[HTTP Request: ip-api.com]
β
[HTTP Request: AbuseIPDB/VirusTotal]
β
[HTTP Request: Ollama/LM Studio AI]
β
[Respond to Webhook]
Each step feeds into the next. The AI node gets all the enrichment data and produces a human-readable risk assessment.
Example Output
After the pipeline runs, you get:
{
"ip": "185.220.101.42",
"risk_level": "high",
"summary": "Known Tor exit node. 47 threat reports in last 90 days. Associated with scanning and brute-force activity.",
"country": "Germany",
"city": "Frankfurt",
"isp": "Tor Project",
"threat_score": 87
}
Now you know immediately: this is a Tor exit node with a high threat score. You can adjust your search or alert accordingly.
Free Tools Required
| Tool | Purpose | Cost |
|---|---|---|
| n8n | Workflow orchestration | Free (self-hosted) |
| ip-api.com | Geolocation | Free (45 req/min) |
| AbuseIPDB | Threat intel | Free (100 req/day) |
| Ollama or LM Studio | AI analysis | Free (local) |
| Splunk Free | Log analysis | Free (200 MB/day) |
Why This Matters
- Speed: Enrichment happens in seconds, not minutes
- Automation: No manual lookups needed
- Free: All tools are open-source or free-tier
- Extensible: Add more enrichment sources (Shodan, Censys, custom feeds)
- Learning: Great way to practice n8n, APIs, and AI integration
Next Steps
- Set up the n8n workflow with just the geolocation step
- Add the threat intel lookup
- Add the AI analysis
- Connect Splunk
- Test with known-good and known-bad IPs
- Iterate β add more enrichment sources, refine the AI prompt
The key insight: you donβt need expensive SIEM features to get enrichment. You need a webhook, a workflow tool, and an AI model. Everything else is plumbing.
This post is part of the Cyber-AI initiative β free, open-source cybersecurity and AI education for everyone.