Arkime Integration
Arkime's REST API allows external tools to pivot directly into Arkime sessions, trigger PCAP downloads programmatically, and enrich session data with external context. This chapter covers integrating Arkime with a SIEM, automating PCAP retrieval on alert, and building custom tagging workflows that enrich sessions with threat intelligence.
Suricata fires an alert with a community_id. A Python script queries the Arkime API for sessions with the matching 5-tuple, retrieves the session ID, then automatically downloads the PCAP and emails a link to the analyst. The analyst arrives at their terminal to find the PCAP already waiting — the 30-second manual lookup is automated away.
Arkime REST API
#!/usr/bin/env python3
"""
Arkime REST API client for programmatic session search and PCAP retrieval.
"""
import requests
import json
from datetime import datetime, timezone
from urllib.parse import urlencode
from pathlib import Path
ARKIME_URL = "http://arkime:8005"
ARKIME_USER = "admin"
ARKIME_PASS = "your_password"
session = requests.Session()
session.auth = (ARKIME_USER, ARKIME_PASS)
session.verify = False # Set to True with proper TLS cert
def search_sessions(query: str, start_time: datetime, stop_time: datetime,
limit: int = 100) -> dict:
"""Search Arkime sessions by query expression and time range."""
params = {
"expression": query,
"startTime": int(start_time.timestamp()),
"stopTime": int(stop_time.timestamp()),
"length": limit,
"fields": "ip.src,ip.dst,port.src,port.dst,starttime,stoptime,"
"bytes.src,bytes.dst,tls.ja3,tls.sni,http.uri",
}
url = f"{ARKIME_URL}/api/sessions"
resp = session.get(url, params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def get_session_pcap(session_id: str, node: str, output_path: str) -> int:
"""Download PCAP for a specific session. Returns file size in bytes."""
url = f"{ARKIME_URL}/{node}/session/{session_id}/pcap"
resp = session.get(url, timeout=60, stream=True)
resp.raise_for_status()
path = Path(output_path)
path.parent.mkdir(parents=True, exist_ok=True)
total = 0
with open(path, 'wb') as f:
for chunk in resp.iter_content(chunk_size=65536):
f.write(chunk)
total += len(chunk)
return total
def tag_session(session_id: str, node: str, tags: list) -> bool:
"""Add tags to a session (for analyst notes, threat intel labels)."""
url = f"{ARKIME_URL}/api/sessions/addtags"
data = {
"ids": f"{node},{session_id}",
"tags": ",".join(tags),
}
resp = session.post(url, json=data, timeout=10)
return resp.ok
def find_and_retrieve_pcap_for_alert(
src_ip: str, dst_ip: str, dst_port: int,
alert_time: datetime, output_dir: str = "/tmp/arkime-pcaps"
) -> list:
"""
Given an alert's 5-tuple, find matching Arkime sessions and download PCAPs.
Returns list of downloaded file paths.
"""
from datetime import timedelta
# Search window: ±5 minutes around the alert time
start = alert_time - timedelta(minutes=5)
stop = alert_time + timedelta(minutes=5)
query = (f"ip.src=={src_ip} && ip.dst=={dst_ip} && port.dst=={dst_port}"
f" && protocols==tls")
print(f"Searching Arkime: {query}")
results = search_sessions(query, start, stop)
sessions_data = results.get("data", [])
print(f"Found {len(sessions_data)} matching sessions")
downloaded = []
for s in sessions_data:
sid = s.get("id")
node = s.get("node", "default")
src = s.get("source", {})
ts = s.get("starttime", "")
output_file = f"{output_dir}/{src_ip}_{dst_ip}_{dst_port}_{sid[:8]}.pcap"
print(f"Downloading session {sid[:8]} ({ts})...")
size = get_session_pcap(sid, node, output_file)
print(f" Saved {size} bytes to {output_file}")
# Tag session as investigated
tag_session(sid, node, ["investigated", "auto-retrieved"])
downloaded.append(output_file)
return downloaded
# Example: triggered by a Suricata alert
if __name__ == "__main__":
alert_time = datetime(2024, 1, 15, 14, 23, 17, tzinfo=timezone.utc)
pcaps = find_and_retrieve_pcap_for_alert(
src_ip="10.0.0.50",
dst_ip="1.2.3.4",
dst_port=443,
alert_time=alert_time,
output_dir="/tmp/incident-20240115"
)
print(f"\nDownloaded {len(pcaps)} PCAPs:")
for p in pcaps:
print(f" {p}")
SIEM Integration Workflow
Arkime + SIEM Integration Flow
═══════════════════════════════════════════════════════════════════
1. Alert fires in SIEM (Splunk/Elastic):
Suricata SID 2024218, src=10.0.0.50, dst=1.2.3.4, port=443, time=14:23
2. SIEM automation (SOAR playbook or Python webhook handler):
→ Query Arkime API for matching sessions
→ Get session IDs for the alert window
→ Generate Arkime URL for analyst:
http://arkime:8005/?expression=ip.src%3D%3D10.0.0.50%26%26ip.dst%3D%3D1.2.3.4
3. SIEM alert enrichment:
→ Add Arkime link to the alert ticket
→ Add JA3 hash, SNI, cert subject from Arkime session metadata to the alert
→ If PCAP auto-retrieval is enabled, attach PCAP download link
4. Analyst workflow:
→ Click Arkime link in SIEM alert → sessions list pre-filtered
→ Inspect session metadata inline (no PCAP download yet)
→ Download PCAP for sessions requiring payload analysis
Kibana/Elastic: Drilldown from Discovery hit → Arkime
Add to Kibana dashboards:
URL field format for community_id:
"http://arkime:8005/?expression=communityId%3D%3D{{value}}"
Clicking the community_id opens Arkime pre-filtered to that session.
Community ID is a standardized hash of the 5-tuple that Suricata, Zeek, and Arkime can all compute independently, producing identical hashes for the same flow. This makes it the ideal pivot key across tools: a Suricata alert in Elastic has a network.community_id field; searching Arkime for that same community_id returns the exact Arkime session. No need to match on IP+port+timestamp (which has edge cases around NAT, time synchronization, and log aggregation latency). To enable community_id in Arkime: add communityId=true to the appropriate section of config.ini. Then in Kibana, configure a URL field formatter for the community_id field that points to http://your-arkime:8005/?expression=communityId%3D%3D{value} — every alert in Kibana becomes a one-click pivot to the corresponding Arkime session with the full PCAP available.
Q & A
Q: The Arkime API returns sessions but the PCAP download times out for large sessions. How do I handle this?
Large PCAP downloads (multi-gigabyte sessions) require specific handling: (1) Streaming download: always use stream=True in the requests call (as shown in the code above) — never buffer the entire response in memory. Write chunks of 64 KB at a time to disk. (2) Timeout configuration: the connect timeout should be short (5-10s), but the read timeout must be long enough for the transfer to complete. Use requests.get(url, timeout=(5, 3600)) for a 1-hour read timeout. (3) Session size pre-check: before downloading, check bytes.src + bytes.dst for the session from the search result. If it exceeds your threshold (e.g., 100 MB), log a warning and skip automatic download — flag for manual retrieval by an analyst. (4) Arkime API: limit to first N packets: the PCAP endpoint accepts ?packets=1000 to return only the first 1000 packets of a session. This is useful for getting the handshake and first few transactions of a large download without waiting for the full PCAP. (5) Background download: for very large sessions, trigger the download as a background job and notify the analyst via Slack/email when complete, rather than blocking the alert workflow.