Posted on
Artificial Intelligence

Artificial Intelligence Security Dashboards

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Build an AI-Powered Security Dashboard on Linux (with nothing but Bash and containers)

If your SSH logs suddenly explode at 3 a.m., how fast can you see it, explain it, and react? AI-assisted security dashboards turn raw logs into live, actionable insights—so spikes, anomalies, and stealthy patterns show up before they become incidents.

This guide shows you how to stand up a lightweight, AI-ready security dashboard on any modern Linux distro using Bash and containers. You’ll ingest auth logs, visualize them, and enable anomaly detection—without locking into proprietary stacks. Everything runs locally with Podman, and you can tear it down when you’re done.

What you’ll get:

  • A compact stack: Fluent Bit (log shipper) + OpenSearch (storage/ML) + OpenSearch Dashboards (UI)

  • Real-time SSH security visibility

  • A simple AI detector to flag anomalous failed logins

Note: We deliberately disable built-in security for a quick lab setup. Do not expose this stack to the internet. For production, enable TLS and authentication.

Why AI Security Dashboards are worth your time

  • You can’t defend what you can’t see: Centralizing telemetry (auth logs, sudo, kernel audit, container events) gives you an immediate view of your risk surface.

  • AI spots the weird stuff: Thresholds catch volume spikes. Anomaly detection catches novel behavior—like “a little too many failures from an unusual ASN during an odd hour.”

  • Faster MTTR: A clean dashboard + AI signals make it easier to answer: What changed? How bad is it? Where’s it coming from?

Prerequisites: Minimal tools you can install in one minute

We’ll use Podman for containers and jq for JSON parsing. Install with your package manager of choice:

  • Debian/Ubuntu (apt):

    sudo apt update
    sudo apt install -y podman jq
    
  • Fedora/RHEL/CentOS (dnf):

    sudo dnf -y install podman jq
    
  • openSUSE/SLES (zypper):

    sudo zypper refresh
    sudo zypper install -y podman jq
    

Verify:

podman --version
jq --version

Core build: From logs to AI signals in 5 steps

1) Decide what to watch first (keep it lean)

Start with signals that correlate strongly with intrusions:

  • SSH and sudo activity (/var/log/auth.log on Debian/Ubuntu; /var/log/secure on Fedora/RHEL/SUSE)

  • Authentication failures and lockouts

  • New/modified privileged accounts

  • Unexpected outbound connections (optional next step)

We’ll focus on SSH failures to keep the example concrete.

2) Stand up the dashboard stack (OpenSearch + Dashboards + Fluent Bit)

Create a working directory and a Fluent Bit config that tails SSH logs and adds a derived field to support anomaly detection.

Create fluent-bit.conf:

cat > fluent-bit.conf <<'EOF'
[SERVICE]
    Flush        1
    Daemon       Off
    Log_Level    info

# Debian/Ubuntu
[INPUT]
    Name        tail
    Path        /var/log/auth.log
    Tag         ssh.auth
    Refresh_Interval 5
    Skip_Long_Lines On

# Fedora/RHEL/SUSE
[INPUT]
    Name        tail
    Path        /var/log/secure
    Tag         ssh.secure
    Refresh_Interval 5
    Skip_Long_Lines On

# Derive a numeric field for failed SSH logins (for anomaly detection)
[FILTER]
    Name        lua
    Match       ssh.*
    Script      /fluent-bit/etc/classify.lua
    Call        classify

# Add host metadata
[FILTER]
    Name        modify
    Match       *
    Add         pipeline ai-sec
    Add         stack opensearch

# Ship to OpenSearch using Elasticsearch-compatible output
[OUTPUT]
    Name                es
    Match               *
    Host                localhost
    Port                9200
    Logstash_Format     On
    Logstash_Prefix     auth
    Replace_Dots        On
EOF

Create classify.lua to tag failed SSH attempts:

cat > classify.lua <<'EOF'
function classify(tag, ts, record)
  local msg = record["log"]
  if msg and string.find(msg, "Failed password") then
    record["failed_ssh"] = 1
  else
    record["failed_ssh"] = 0
  end
  return 1, ts, record
end
EOF

Start a Podman pod and the three containers inside it:

# Create a shared network/ports space
podman pod create --name ai-sec --publish 9200:9200 --publish 5601:5601

# OpenSearch (single-node, security disabled for local demo)
podman run -d --name opensearch --pod ai-sec \
  -e "discovery.type=single-node" \
  -e "DISABLE_SECURITY_PLUGIN=true" \
  -e "DISABLE_INSTALL_DEMO_CONFIG=true" \
  -e "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" \
  --ulimit memlock=-1:-1 \
  --ulimit nofile=65536:65536 \
  opensearchproject/opensearch:2

# OpenSearch Dashboards
podman run -d --name opensearch-dashboards --pod ai-sec \
  -e "OPENSEARCH_HOSTS=['http://localhost:9200']" \
  opensearchproject/opensearch-dashboards:2

# Fluent Bit (reads host logs and ships to OpenSearch)
podman run -d --name fluent-bit --pod ai-sec \
  -v "$PWD/fluent-bit.conf":/fluent-bit/etc/fluent-bit.conf:ro \
  -v "$PWD/classify.lua":/fluent-bit/etc/classify.lua:ro \
  -v /var/log:/var/log:ro \
  fluent/fluent-bit:2

Check health:

podman ps --pod
curl -s http://localhost:9200 | jq
curl -s http://localhost:9200/_cat/indices?v

Open the UI at:

  • OpenSearch Dashboards: http://localhost:5601

First-time UI step: Create an index pattern “auth-*” with time field “@timestamp” so you can visualize ingested logs.

Tip: Generate a few failed logins to see data:

# From another terminal or host, attempt a wrong password a few times:
ssh wronguser@your-host

3) Turn on an anomaly detector for SSH failures

We’ll use OpenSearch’s Anomaly Detection (AD) to model the normal rate of failed SSH attempts and flag outliers.

Create a detector:

cat > detector.json <<'EOF'
{
  "name": "ssh-failed-login-anomaly",
  "description": "Detect anomalous spikes of failed SSH logins",
  "time_field": "@timestamp",
  "indices": ["auth-*"],
  "feature_attributes": [
    {
      "feature_name": "failed_ssh_sum",
      "feature_enabled": true,
      "aggregation_query": {
        "failed_ssh_sum": { "sum": { "field": "failed_ssh" } }
      }
    }
  ],
  "detection_interval": { "period": { "interval": 1, "unit": "MINUTES" } },
  "window_delay": { "period": { "interval": 1, "unit": "MINUTES" } }
}
EOF

# Create the detector
DET_ID=$(curl -s -H 'content-type: application/json' \
  -X POST http://localhost:9200/_plugins/_anomaly_detection/detectors \
  -d @detector.json | jq -r '._id')

echo "Detector ID: $DET_ID"

# Start the detector job
curl -s -X POST "http://localhost:9200/_plugins/_anomaly_detection/detectors/$DET_ID/_start"

Confirm it’s running and check results after a couple of minutes:

curl -s "http://localhost:9200/_plugins/_anomaly_detection/detectors/$DET_ID/_stats" | jq
curl -s "http://localhost:9200/_plugins/_anomaly_detection/detectors/$DET_ID/_results" | jq

In OpenSearch Dashboards, open the Anomaly Detection app to visualize anomaly grades over time. Trigger a small brute-force on your lab host to see the spike.

4) Make the data useful (quick-win visualizations)

In OpenSearch Dashboards, create:

  • A time series of failed SSH logins (search: “Failed password”), split by source IP.

  • A table of top source IPs and usernames on “auth-*”.

  • A single-value visualization for “failed_ssh_sum” with last 15 minutes.

  • A dashboard that puts these together plus the anomaly line chart.

This gives you both raw counts and an AI signal in one view.

5) Hardening and next steps (don’t skip)

  • Enable security on OpenSearch and Dashboards:

    • Turn off DISABLE_SECURITY_PLUGIN, configure users/roles, and TLS.
  • Persist data:

    • Mount volumes for OpenSearch data and snapshots. Example:
    -v /srv/opensearch-data:/usr/share/opensearch/data
    
  • Add more signals:

    • sudo logs, auditd events, container runtime logs, and reverse-proxy access logs.
  • Alerting:

    • Use OpenSearch Notifications or integrate with webhooks to Slack/Matrix/Email when anomaly grade > threshold.
  • Threat intel:

    • Enrich source IPs against blocklists or ASN databases to add context to dashboards.

Real-world example: Catching a subtle brute-force

  • What you see: A small but unusual off-hours bump in failed_ssh_sum with a high anomaly grade.

  • Drill-down: Top source IP points to an untrusted ASN. The failures are spread across many usernames (spray).

  • Action: Temporarily geofence or rate-limit SSH, tighten fail2ban, and investigate any successful logins adjacent to the spike.

Troubleshooting

  • No logs in “auth-*”:

    • Ensure your system writes to /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (Fedora/RHEL/SUSE).
    • Check Fluent Bit logs:
    podman logs -f fluent-bit
    
  • Detector shows no data:

    • Wait a couple of minutes for buckets to fill.
    • Verify documents contain failed_ssh fields:
    curl -s 'http://localhost:9200/auth-*/_search?q=failed_ssh:1&size=5' | jq
    
  • Resource limits:

    • If OpenSearch restarts, increase memory:
    -e "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
    
    • Ensure memlock ulimit is set as in the run command.

Cleanup

When you’re done experimenting:

podman pod stop ai-sec
podman pod rm -f ai-sec

Conclusion and Call to Action

You just stood up an AI-enabled security dashboard on Linux using only Bash and containers. You can now see SSH activity in real time and get anomaly scores that spotlight unusual behavior—without a SaaS bill or heavyweight agents.

Your next steps:

  • Add sudo, auditd, and web access logs to the pipeline.

  • Secure the stack (TLS, auth) and persist data.

  • Generalize the Lua approach to mark other patterns (e.g., “sudo failure”, “password change”, “new key added”).

  • Wire notifications so anomalies page you before attackers escalate.

Security is a visibility problem first. Make the invisible obvious—then automate the response.