- Posted on
- • Artificial Intelligence
Artificial Intelligence SOC Workflows
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence SOC Workflows: Bash-First Playbooks You Can Ship Today
If you’ve ever stared at a blinking pager at 02:17, you know the feeling: too many alerts, not enough humans. The gap between “alert” and “actionable context” is where SOC time goes to die. The promise of AI for SOCs isn’t hype when you translate it into small, reliable workflows that reduce toil, speed up triage, and standardize reporting—without spraying sensitive data into the cloud. This post shows how to build AI-augmented SOC workflows in plain Bash with local models, so you can keep data in your perimeter and move faster.
What you’ll get:
Why AI in SOCs is worth your time (and where it’s not).
3–5 actionable Bash-first workflows you can deploy today.
Installation instructions for common Linux package managers (apt, dnf, zypper).
Copy-pasteable scripts using jq, curl, and a local LLM via Ollama.
Why AI in the SOC (and why Bash)?
Volume is the villain. Modern SIEMs surface thousands of events; most require enrichment, dedup, and prioritization. AI compresses “context gathering” from minutes to seconds.
Bash is the lingua franca. Every SOC has shells, logs, and APIs. You don’t need new platforms to get value; small scripts can glue your stack together.
Data stays inside. With a local model (e.g., Ollama), you can keep telemetry on-prem while still getting natural-language summarization and transformation.
Known pitfalls have straightforward mitigations. Privately run models, prompt with guardrails, log model I/O, and never auto-apply risky actions.
Install the prerequisites
We’ll use:
- curl (HTTP), jq (JSON), whois, dig, git (to fetch playbooks), and a local LLM via Ollama.
On Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq whois dnsutils git
On Fedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq whois bind-utils git
On openSUSE/SLE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq whois bind-utils git
Install Ollama (local LLM runtime):
curl -fsSL https://ollama.com/install.sh | sh
Pull a model (pick one you like; llama3 is a solid default):
ollama pull llama3
Optional: verify the service is running (some systems auto-start it):
systemctl status ollama
# or run it manually for this session:
ollama serve
Environment hint:
export LLM_MODEL=llama3
Note: Running models locally uses CPU/GPU and disk space. Keep telemetry minimal and redact secrets before sending any data to the model.
Workflow 1: IOC Enrichment + AI Triage Summary
Problem: Triage takes too long. You get an IP or domain and need “Is it bad? Why? What next?”
Approach: Query a couple of intel sources, add whois/dig context, then ask a local LLM for a concise, structured summary with confidence, rationale, and next steps.
Create enrich.sh:
#!/usr/bin/env bash
set -euo pipefail
IOC="${1:-}"
if [[ -z "$IOC" ]]; then
echo "Usage: $0 <ip|domain|sha256>"
exit 1
fi
: "${LLM_MODEL:=llama3}"
# Optional API keys (export before running if you have them)
# export OTX_API_KEY="..."
# export VT_API_KEY="..."
# export ABUSEIPDB_API_KEY="..."
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
is_ip() { [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; }
is_domain() { [[ "$1" =~ ^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$ ]]; }
is_sha256() { [[ "$1" =~ ^[A-Fa-f0-9]{64}$ ]]; }
whois_out="$tmpdir/whois.txt"
dig_out="$tmpdir/dig.txt"
intel_out="$tmpdir/intel.json"
if is_ip "$IOC" || is_domain "$IOC"; then
whois "$IOC" > "$whois_out" 2>/dev/null || true
dig +short A "$IOC" > "$dig_out" 2>/dev/null || true
fi
# Build a JSON blob of enrichment (OTX example; guard for missing key)
jq -n \
--arg ioc "$IOC" \
--arg whois "$(tr -d '\000' < "$whois_out" | jq -Rs '.')" \
--arg dig "$(tr -d '\000' < "$dig_out" | jq -Rs '.')" \
'
{
ioc: $ioc,
whois: ($whois | fromjson? // ""),
dig: ($dig | fromjson? // ""),
intel: {}
}
' > "$intel_out"
# Pull from OTX if available
if [[ -n "${OTX_API_KEY:-}" && ( is_ip "$IOC" || is_domain "$IOC" ) ]]; then
OTX_URL="https://otx.alienvault.com/api/v1/indicators"
if is_ip "$IOC"; then
curl -s -H "X-OTX-API-KEY: $OTX_API_KEY" "$OTX_URL/IPv4/$IOC/general" \
| jq '. | {pulse_info, reputation: .reputation, sections}' \
| jq -c > "$tmpdir/otx.json" || true
else
curl -s -H "X-OTX-API-KEY: $OTX_API_KEY" "$OTX_URL/domain/$IOC/general" \
| jq '. | {alexa: .alexa, sections}' \
| jq -c > "$tmpdir/otx.json" || true
fi
if [[ -s "$tmpdir/otx.json" ]]; then
jq --slurpfile otx "$tmpdir/otx.json" '.intel.otx = $otx[0]' "$intel_out" > "$tmpdir/x" && mv "$tmpdir/x" "$intel_out"
fi
fi
# Add placeholder for VirusTotal if you have VT_API_KEY (domain/ip/file)
# See API docs; remember to redact and respect ToS.
prompt="$(jq -c '.' "$intel_out")"
# Ask the local LLM for a concise triage summary in JSON
read -r -d '' system_msg <<'SYS'
You are a SOC analyst. Return ONLY strict JSON with fields:
- severity: one of ["info","low","medium","high","critical"]
- confidence: 0-100
- findings: array of short strings
- recommended_actions: array of imperative steps
- reasoning: one paragraph max
Avoid speculation. If unsure, say so.
SYS
curl -s http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d "$(jq -n --arg model "$LLM_MODEL" --arg sys "$system_msg" --arg ctx "$prompt" \
'{model:$model, prompt: ($sys + "\n\nContext:\n" + $ctx), stream:false}')" \
| jq -r '.response' \
| jq .
Usage:
chmod +x enrich.sh
./enrich.sh 203.0.113.42
./enrich.sh suspicious.example.com
./enrich.sh 9f2c...sha256...
Real-world example: Weekend SSH brute-force on a public server. This script pulls whois/dig and OTX context locally, then produces a JSON with severity “medium”, confidence “75”, key findings (ASN, hosting provider, seen in N pulses), and action items (block at edge WAF, check auth logs, hunt for lateral movement). That JSON can be sent to your ticketing system as-is.
Workflow 2: Summarize Noisy Logs Into Analyst-Ready Notes
Problem: Journald or auth logs are noisy. Analysts want “What happened? What stands out? What should I check next?” quickly.
Approach: Filter for relevant events with jq, batch them, and ask the LLM for a 10-line summary with concrete next steps.
Summarize recent SSH failures:
#!/usr/bin/env bash
set -euo pipefail
: "${LLM_MODEL:=llama3}"
SINCE="${1:-2 hours ago}"
# Collect recent SSH auth failures from journald as JSON lines
events="$(journalctl -u ssh --since "$SINCE" -o json | jq -c 'select(.MESSAGE? | test("Failed password|Invalid user"; "i")) | {ts:.__REALTIME_TIMESTAMP, msg:.MESSAGE, pid:._PID, src:.REMOTE_ADDR?}' | head -n 200)"
if [[ -z "$events" ]]; then
echo "No matching events."
exit 0
fi
read -r -d '' system_msg <<'SYS'
You are a SOC analyst. Summarize SSH authentication failures from Linux journald.
Output:
- top_sources: array of {ip, count}
- top_usernames: array of {user, count}
- anomalies: array of short strings
- timeline: 3-6 bullets of notable bursts or patterns
- recommended_actions: 4-8 concrete steps
Return ONLY JSON. Avoid guessing.
SYS
curl -s http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d "$(jq -n --arg model "$LLM_MODEL" --arg sys "$system_msg" --arg ctx "$events" \
'{model:$model, prompt: ($sys + "\n\nEvents:\n" + $ctx), stream:false}')" \
| jq -r '.response' \
| jq .
Tip:
Cap batches to a few hundred lines to keep responses fast.
Redact usernames or IPs if policy requires.
For repeatability, you can add options to control generation:
{"options":{"temperature":0.1}}in the body.
Workflow 3: Natural-Language to jq Filter (Safely)
Problem: Analysts often think in natural language: “Show failed SSH logins from a single IP over 5 minutes.” Writing the jq by hand takes time.
Approach: Use the LLM to propose a jq filter, validate it with a simple allowlist, then run it. Keep the model local and forbid shell metacharacters.
ask-jq.sh:
#!/usr/bin/env bash
set -euo pipefail
: "${LLM_MODEL:=llama3}"
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <json_log_file> <natural language question>"
exit 1
fi
file="$1"; shift
question="$*"
# Ask model for ONLY a jq filter (no prose).
read -r -d '' sys <<'SYS'
You translate natural language questions into jq filters for JSON log lines.
Rules:
- Output ONLY the jq program on a single line (no backticks, no prose).
- Assume input is line-delimited JSON.
- Prefer safe selectors and pipes; avoid external functions.
- Use strings and regex carefully.
SYS
resp="$(curl -s http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d "$(jq -n --arg model "$LLM_MODEL" --arg sys "$sys" --arg q "$question" \
'{model:$model, prompt: ($sys + "\n\nQuestion:\n" + $q), stream:false, options:{temperature:0.1}}')" \
| jq -r '.response' \
| head -n1 \
| tr -d '\r')"
# Basic allowlist: letters, numbers, spaces, punctuation typical for jq.
if [[ ! "$resp" =~ ^[[:print:][:space:]]+$ ]]; then
echo "Refusing suspicious jq filter."
exit 2
fi
echo "jq filter: $resp" >&2
jq -c "$resp" < "$file"
Example:
journalctl -u ssh -o json --since "1 hour ago" > ssh.json
./ask-jq.sh ssh.json "Show failed password attempts and count them per source IP"
Safety notes:
Never let the model emit shell; only a jq expression.
Keep a human in the loop for high-impact queries.
Consider timeboxing and regex restrictions for production.
Workflow 4: Incident Report Drafting From Artifacts
Problem: Writing the first draft of an incident summary is slow; important details get missed.
Approach: Feed sanitized artifacts (IOC list, detection hits, timelines) to a local model and ask for a concise Markdown report. Humans review and finalize.
draft-report.sh:
#!/usr/bin/env bash
set -euo pipefail
: "${LLM_MODEL:=llama3}"
ARTIFACT_DIR="${1:-artifacts}"
if [[ ! -d "$ARTIFACT_DIR" ]]; then
echo "Usage: $0 <artifact_directory>"
exit 1
fi
# Gather small text artifacts (limit size to avoid overloading the model)
context="$(find "$ARTIFACT_DIR" -type f -maxdepth 1 -size -256k -print0 \
| xargs -0 -I{} sh -c 'echo "===== {} ====="; sed -n "1,500p" "{}"' \
| jq -Rs '.')"
read -r -d '' sys <<'SYS'
You are a SOC incident handler. Produce a concise Markdown report with:
# Summary
- Incident ID, date range, systems affected, severity, confidence
# Timeline
- Bulleted key events with timestamps
# Findings
- What happened and how we know (cite filenames)
# Impact
# Containment & Eradication
# Recommendations
Keep to ~400-600 words. If data is missing, clearly state assumptions. Do not fabricate.
SYS
body="$(jq -n --arg model "$LLM_MODEL" --arg sys "$sys" --arg ctx "$context" \
'{model:$model, prompt: ($sys + "\n\nContext:\n" + ($ctx | fromjson)), stream:false, options:{temperature:0.2}}')"
curl -s http://localhost:11434/api/generate \
-H "Content-Type: application/json" \
-d "$body" \
| jq -r '.response' \
> incident-draft.md
echo "Wrote incident-draft.md"
Usage:
mkdir -p artifacts
cp /var/log/auth.log artifacts/auth.log # Example; sanitize before copying
echo "IOC: 198.51.100.23" > artifacts/iocs.txt
./draft-report.sh artifacts
Guardrails and Ops Tips
Data handling
- Default to local models (Ollama). If you must use cloud, redact aggressively.
- Avoid sending raw secrets, credentials, or full PCAPs to any model.
Prompt discipline
- Ask for strict JSON when you need structure.
- Set low temperature (0.0–0.2) for repeatable outputs.
- Bound inputs: cap lines, truncate large logs, and batch.
Logging and auditing
- Log prompts and responses to a secure location with timestamps.
- Tag outputs with model name + version for repeatability.
Human-in-the-loop
- Treat model outputs as analyst assistance, not ground truth.
- Require human approval for containment actions.
Evaluate
- Keep a sandbox corpus of known incidents; compare model summaries over time.
- Track false positives/negatives in triage scoring to tune prompts.
What to Do Next (CTA)
Pilot one workflow this week:
- Install prerequisites and Ollama.
- Wire up Workflow 1 (IOC enrichment + triage JSON).
- Measure time saved and analyst satisfaction on real tickets.
Productionize:
- Add redaction, logging, and prompt templates under version control.
- Wrap scripts into your SOAR or ticketing routines.
- Document “when to use” and “when not to use” each workflow.
Iterate:
- Add your SIEM/API specifics.
- Build a small library of prompts and playbooks in git.
- Share improvements across the team.
AI won’t replace analysts—but analysts who automate boring work with AI will outpace the ones who don’t. Start with Bash, keep data local, and ship small wins that pay off immediately.