- Posted on
- • Artificial Intelligence
Artificial Intelligence Network Observability
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
AI-Powered Network Observability on Linux (with Bash): From Packets to Anomalies
Packets don’t lie—but they do overwhelm. If you’ve ever tailed a pcap at 2 a.m., you know the pain: too many events, too little signal. AI-driven network observability flips the script. Instead of hand-sifting, let the machine summarize behavior and flag anomalies. In this post, you’ll build a minimal, Linux-first pipeline that:
Collects packet/flow metadata with standard CLI tools
Featurizes traffic into CSV
Trains a tiny anomaly detector
Scores new traffic and surfaces “weird” events automatically
Automates the whole thing with Bash
No proprietary black boxes. Just open tools, scripts you can read, and knobs you can tune.
What problem are we solving?
Networks are fast, encrypted, and dynamic; humans can’t reliably spot subtle anomalies at scale.
Traditional alert rules miss new/unknown behaviors.
Full packet capture is heavy; metadata + ML is often enough to detect scans, exfiltration, beaconing, and lateral movement.
AI network observability brings:
Unsupervised detection of “odd” behavior without exhaustive rule-writing
Faster triage by ranking suspicious flows
Repeatable workflows you can automate and audit
What you’ll build (high level)
1) Collect flow-like metadata with tshark
2) Turn it into features (CSV)
3) Train a lightweight anomaly detector (Isolation Forest)
4) Score new captures and print top anomalies
5) Automate with a Bash wrapper and cron/systemd
Everything runs locally on Linux with common packages.
1) Install the toolkit
You’ll need packet capture and Python ML bits. Use one of the following based on your distro.
- Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y tcpdump tshark bpftrace nfdump python3 python3-pip python3-venv
- Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y tcpdump wireshark-cli bpftrace nfdump python3 python3-pip
# If you need virtualenv support explicitly:
# sudo dnf install -y python3-virtualenv
- openSUSE Leap/Tumbleweed (zypper):
sudo zypper refresh
sudo zypper install -y tcpdump wireshark bpftrace nfdump python3 python3-pip
Verify you have TShark:
tshark -v
Note:
You generally need root/capabilities to capture. Using sudo is fine for a lab.
To capture as non-root, configure dumpcap capabilities and/or the wireshark group per your distro’s guidance.
Create a Python virtual environment for the AI bits:
python3 -m venv ~/ai-netobs-venv
. ~/ai-netobs-venv/bin/activate
pip install --upgrade pip
pip install pandas scikit-learn joblib
2) Capture and featurize traffic (CSV)
Pick an interface:
ip -br link show up
Export it (replace with your active interface, e.g., eth0 or enp0s31f6):
export IFACE=eth0
Capture 60 seconds of IP traffic and extract useful fields into CSV:
sudo tshark -i "$IFACE" -a duration:60 -Y "ip" -T fields \
-e frame.time_epoch -e ip.proto -e ip.src -e ip.dst \
-e tcp.srcport -e tcp.dstport -e udp.srcport -e udp.dstport \
-e frame.len -e tcp.flags \
-E header=y -E separator=, -E quote=d -E occurrence=f \
> flows.csv
What you just did:
Limited capture to 60s for a small, rotating baseline
Output fields that work even with encrypted payloads (metadata still shines)
Produced
flows.csv, which we’ll use for training and detection
Optional: quick NetFlow/IPFIX if you already have an exporter. nfdump can read flow files or listen, but that’s outside this minimal pipeline.
3) Train a tiny anomaly detector (unsupervised)
First collect a “baseline” capture during a quiet, known-good period—e.g., a few 1–5 minute samples concatenated. Then train:
Save as train_isoforest.py:
#!/usr/bin/env python3
import sys, os, math
import pandas as pd
from sklearn.ensemble import IsolationForest
from joblib import dump
if len(sys.argv) != 3:
print("Usage: train_isoforest.py <baseline_flows.csv> <model_out.joblib>")
sys.exit(1)
in_csv, out_model = sys.argv[1], sys.argv[2]
df = pd.read_csv(in_csv)
# Normalize port fields: pick TCP port if present, else UDP, else 0
def to_int(x):
try:
if isinstance(x, str) and x.startswith("0x"):
return int(x, 16)
return int(float(x))
except Exception:
return 0
for col in ["ip.proto","tcp.srcport","tcp.dstport","udp.srcport","udp.dstport","frame.len","tcp.flags"]:
if col in df.columns:
df[col] = df[col].apply(to_int)
else:
df[col] = 0
df["src_port"] = df[["tcp.srcport","udp.srcport"]].max(axis=1)
df["dst_port"] = df[["tcp.dstport","udp.dstport"]].max(axis=1)
df["is_tcp"] = (df["ip.proto"] == 6).astype(int)
df["is_udp"] = (df["ip.proto"] == 17).astype(int)
features = df[["ip.proto","src_port","dst_port","frame.len","tcp.flags","is_tcp","is_udp"]].fillna(0)
# Isolation Forest: great default for unsupervised anomaly detection
model = IsolationForest(
n_estimators=200,
contamination=0.01, # tweak: expected fraction of anomalies in baseline
random_state=42
)
model.fit(features)
os.makedirs(os.path.dirname(out_model) or ".", exist_ok=True)
dump(model, out_model)
print(f"Trained model saved to {out_model}")
Train it (assuming your venv is active):
python3 train_isoforest.py flows.csv model.joblib
Tip:
Re-train occasionally as your “normal” evolves.
Adjust
contaminationif you see too many/too few anomalies.
4) Detect anomalies in near real time
Create a scoring script to run on fresh captures.
Save as score_flows.py:
#!/usr/bin/env python3
import sys
import pandas as pd
from joblib import load
if len(sys.argv) != 3:
print("Usage: score_flows.py <flows.csv> <model.joblib>")
sys.exit(1)
in_csv, in_model = sys.argv[1], sys.argv[2]
df = pd.read_csv(in_csv)
def to_int(x):
try:
if isinstance(x, str) and x.startswith("0x"):
return int(x, 16)
return int(float(x))
except Exception:
return 0
for col in ["ip.proto","tcp.srcport","tcp.dstport","udp.srcport","udp.dstport","frame.len","tcp.flags"]:
if col in df.columns:
df[col] = df[col].apply(to_int)
else:
df[col] = 0
df["src_port"] = df[["tcp.srcport","udp.srcport"]].max(axis=1)
df["dst_port"] = df[["tcp.dstport","udp.dstport"]].max(axis=1)
df["is_tcp"] = (df["ip.proto"] == 6).astype(int)
df["is_udp"] = (df["ip.proto"] == 17).astype(int)
features = df[["ip.proto","src_port","dst_port","frame.len","tcp.flags","is_tcp","is_udp"]].fillna(0)
model = load(in_model)
# Predictions: -1 = anomaly, 1 = normal
pred = model.predict(features)
scores = model.decision_function(features) # lower = more anomalous
df_out = df.copy()
df_out["anomaly"] = pred
df_out["score"] = scores
# Show top 20 most anomalous rows (lowest scores)
res = df_out.sort_values(by="score").head(20)
cols = ["frame.time_epoch","ip.src","ip.dst","src_port","dst_port","frame.len","ip.proto","tcp.flags","anomaly","score"]
cols = [c for c in cols if c in res.columns]
print(res[cols].to_csv(index=False))
Test it against a fresh short capture:
sudo tshark -i "$IFACE" -a duration:15 -Y "ip" -T fields \
-e frame.time_epoch -e ip.proto -e ip.src -e ip.dst \
-e tcp.srcport -e tcp.dstport -e udp.srcport -e udp.dstport \
-e frame.len -e tcp.flags \
-E header=y -E separator=, -E quote=d -E occurrence=f \
> new_flows.csv
python3 score_flows.py new_flows.csv model.joblib
You’ll get a CSV of the most “weird” flows first.
5) Automate with a Bash wrapper (cron/systemd friendly)
Save as ai-net-observe.sh:
#!/usr/bin/env bash
set -euo pipefail
# Config
IFACE="${IFACE:-$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')}"
DURATION="${DURATION:-30}" # seconds per sample
WORKDIR="${WORKDIR:-/var/tmp/ai-netobs}"
MODEL="${MODEL:-$WORKDIR/model.joblib}"
VENV="${VENV:-$HOME/ai-netobs-venv}"
mkdir -p "$WORKDIR"
if [[ ! -f "$MODEL" ]]; then
echo "Model not found at $MODEL"
echo "Train once with: python3 train_isoforest.py <baseline.csv> $MODEL"
exit 2
fi
STAMP=$(date +%Y%m%d-%H%M%S)
CSV="$WORKDIR/flows-$STAMP.csv"
# Capture slice
sudo tshark -i "$IFACE" -a "duration:$DURATION" -Y "ip" -T fields \
-e frame.time_epoch -e ip.proto -e ip.src -e ip.dst \
-e tcp.srcport -e tcp.dstport -e udp.srcport -e udp.dstport \
-e frame.len -e tcp.flags \
-E header=y -E separator=, -E quote=d -E occurrence=f \
> "$CSV"
# Score
. "$VENV/bin/activate"
python3 -u - <<'PY' "$CSV" "$MODEL" | tee "$WORKDIR/alerts-$STAMP.csv" | head -n 25 | sed 's/^/ANOMALY: /' | logger -t ai-netobs
import sys
import pandas as pd
from joblib import load
in_csv, in_model = sys.argv[1], sys.argv[2]
df = pd.read_csv(in_csv)
def to_int(x):
try:
if isinstance(x, str) and x.startswith("0x"):
return int(x, 16)
return int(float(x))
except Exception:
return 0
for col in ["ip.proto","tcp.srcport","tcp.dstport","udp.srcport","udp.dstport","frame.len","tcp.flags"]:
if col in df.columns:
df[col] = df[col].apply(to_int)
else:
df[col] = 0
df["src_port"] = df[["tcp.srcport","udp.srcport"]].max(axis=1)
df["dst_port"] = df[["tcp.dstport","udp.dstport"]].max(axis=1)
df["is_tcp"] = (df["ip.proto"] == 6).astype(int)
df["is_udp"] = (df["ip.proto"] == 17).astype(int)
features = df[["ip.proto","src_port","dst_port","frame.len","tcp.flags","is_tcp","is_udp"]].fillna(0)
model = load(in_model)
pred = model.predict(features)
scores = model.decision_function(features)
df_out = df.copy()
df_out["anomaly"] = pred
df_out["score"] = scores
res = df_out[df_out["anomaly"] == -1].sort_values(by="score").head(50)
cols = ["frame.time_epoch","ip.src","ip.dst","src_port","dst_port","frame.len","ip.proto","tcp.flags","anomaly","score"]
cols = [c for c in cols if c in res.columns]
print(res[cols].to_csv(index=False))
PY
echo "Done. Check journalctl -t ai-netobs for recent anomalies."
Make it executable:
chmod +x ai-net-observe.sh
Run it ad-hoc:
./ai-net-observe.sh
journalctl -t ai-netobs -n 50 --no-pager
Cron it every 5 minutes:
( crontab -l 2>/dev/null; echo "*/5 * * * * IFACE=$IFACE DURATION=30 $PWD/ai-net-observe.sh >/dev/null 2>&1" ) | crontab -
Or create a simple systemd unit for steadier scheduling.
Real-world patterns this catches
Port scans: bursts of small TCP SYNs to many destination ports. Features: many flows with small
frame.len, varyingdst_port,tcp.flagsset for SYN, unusual for baseline.Data exfiltration spikes: large
frame.lenout to rare external IPs or ports not common for your environment.Beaconing/malware callbacks: low-volume but periodic traffic to a single external
ip.dst/dst_port, atypical relative to learned normal.
This pipeline won’t replace a full IDS, but it’s a strong, explainable signal booster.
Why this approach works (and trade-offs)
Encryption-proof: it uses headers and sizes, not payloads.
Lightweight: no full packet retention required for detection.
Unsupervised: flags “new” things without labels.
Trade-offs: false positives early on; model needs periodic retraining; metadata features are coarser than full DPI.
Tuning tips:
Adjust
contaminationin Isolation Forest.Engineer more features (e.g., rolling rates per src/dst, entropy of dst_port, inter-arrival time deltas).
Maintain separate baselines for weekdays vs weekends, or office vs data center segments.
Optional: quick bpftrace sanity view
If you want a fast kernel-level sanity check (root required), this view counts connects by process:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_connect { @[comm] = count(); } interval:s:10 { print(@); clear(@); }'
Note: Tracepoint args vary by kernel and arch; keep this as a quick pulse, not a core dependency.
Conclusion and Call to Action
You just built a practical, Bash-first AI observability loop:
Capture metadata with TShark
Train a compact unsupervised model
Score new traffic and log anomalies
Automate it on your Linux host
Next steps:
Baseline on known-good periods and re-train weekly.
Add features (rates per IP, unique port counts, time-of-day buckets).
Push results to your SIEM, or alert with email/Slack from the wrapper script.
Roll this to a sensor on each segment for east–west visibility.
If you try this on your network, start small—one interface, one host—and iterate. Packets don’t lie, but they do appreciate good company: a little AI.