Skip to content

SecurityOnion Cheatsheet

SecurityOnion is a free and open-source Linux distribution for threat hunting, enterprise security monitoring, and log management. It includes a comprehensive suite of security tools including Elasticsearch, Logstash, Kibana, Suricata, Zeek, Wazuh, TheHive, Cortex, and many other security-focused applications integrated into a cohesive platform.

Platform Overview

Architecture and Components

SecurityOnion follows a distributed architecture with different node types serving specific functions. The platform integrates multiple open-source security tools into a unified ecosystem for comprehensive network security monitoring and incident response.

Core components include network security monitoring (NSM) tools like Suricata and Zeek for traffic analysis, log management through the Elastic Stack, host-based monitoring via Wazuh agents, and case management through TheHive integration.

Key Features

bash
# Core SecurityOnion Capabilities
- Full packet capture and network security monitoring
- Intrusion detection and prevention (Suricata)
- Network traffic analysis (Zeek/Bro)
- Log aggregation and analysis (Elastic Stack)
- Host-based intrusion detection (Wazuh)
- Threat hunting and investigation tools
- Case management and incident response
- Distributed deployment architecture
- Web-based management interface (SOC)

Installation and Setup

ISO Installation

bash
# Download SecurityOnion ISO
wget https://github.com/Security-Onion-Solutions/securityonion/releases/latest/download/securityonion-2.3.x-x86_64.iso

# Verify checksum
sha256sum securityonion-2.3.x-x86_64.iso

# Create bootable USB (Linux)
sudo dd if=securityonion-2.3.x-x86_64.iso of=/dev/sdX bs=4M status=progress
sync

# Boot from USB and follow installation wizard
# Minimum requirements:
# - 16GB RAM (32GB+ recommended)
# - 200GB storage (1TB+ recommended)
# - Dual network interfaces (management + monitoring)

# Post-installation network configuration
sudo so-setup

# Initial setup wizard will configure:
# - Network interfaces
# - Node type (standalone, manager, search, forward, heavy, etc.)
# - User accounts and authentication
# - SSL certificates
# - Service configuration

Distributed Deployment

bash
# Manager Node Setup (first node)
sudo so-setup
# Select: Install
# Select: Manager
# Configure management interface
# Configure monitoring interface(s)
# Set admin credentials
# Configure grid settings

# Search Node Setup
sudo so-setup
# Select: Install
# Select: Search Node
# Enter manager IP address
# Configure network settings
# Join existing grid

# Forward Node Setup
sudo so-setup
# Select: Install
# Select: Forward Node
# Enter manager IP address
# Configure monitoring interfaces
# Set log forwarding destination

# Heavy Node Setup (combined search + forward)
sudo so-setup
# Select: Install
# Select: Heavy Node
# Enter manager IP address
# Configure all interfaces and services

# Verify grid status
sudo so-status
sudo so-grid-status

# Check node connectivity
sudo so-test

Docker-based Installation

bash
# Install Docker and Docker Compose
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

# Clone SecurityOnion repository
git clone https://github.com/Security-Onion-Solutions/securityonion.git
cd securityonion

# Configure environment
cp .env.example .env
nano .env

# Example .env configuration
SO_MANAGER_IP=192.168.1.100
SO_INTERFACE_MONITOR=eth1
SO_INTERFACE_MANAGEMENT=eth0
SO_ADMIN_USER=admin
SO_ADMIN_PASS=SecurePassword123!

# Deploy with Docker Compose
sudo docker-compose up -d

# Check deployment status
sudo docker-compose ps
sudo docker-compose logs -f

Core Tools and Services

Suricata (IDS/IPS)

bash
# Suricata configuration and management
sudo so-suricata-restart
sudo so-suricata-status

# View Suricata configuration
sudo cat /opt/so/conf/suricata/suricata.yaml

# Update Suricata rules
sudo so-rule-update

# Custom rule management
sudo nano /opt/so/rules/local.rules

# Example custom rules
alert tcp any any -> $HOME_NET 22 (msg:"SSH Connection Attempt"; sid:1000001; rev:1;)
alert http any any -> any any (msg:"Suspicious User Agent"; content:"User-Agent: BadBot"; sid:1000002; rev:1;)

# Test rule syntax
sudo suricata -T -c /opt/so/conf/suricata/suricata.yaml

# Monitor Suricata alerts
sudo tail -f /nsm/suricata/eve.json

# Suricata performance tuning
sudo nano /opt/so/conf/suricata/suricata.yaml
# Adjust:
# - af-packet workers
# - ring-size
# - block-size
# - use-mmap

# Restart Suricata with new configuration
sudo so-suricata-restart

# Check Suricata statistics
sudo suricata-sc -c stats

# Rule management commands
sudo so-rule-update --help
sudo so-rule-update --force
sudo so-rule-update --ruleset=emerging-threats

Zeek (Network Analysis)

bash
# Zeek configuration and management
sudo so-zeek-restart
sudo so-zeek-status

# View Zeek configuration
sudo cat /opt/so/conf/zeek/node.cfg

# Zeek log locations
ls -la /nsm/zeek/logs/current/

# Common Zeek logs
tail -f /nsm/zeek/logs/current/conn.log
tail -f /nsm/zeek/logs/current/dns.log
tail -f /nsm/zeek/logs/current/http.log
tail -f /nsm/zeek/logs/current/ssl.log
tail -f /nsm/zeek/logs/current/files.log

# Custom Zeek scripts
sudo nano /opt/so/conf/zeek/local.zeek

# Example custom script
@load base/protocols/http
event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) {
    if (/malware/ in original_URI) {
        print fmt("Suspicious HTTP request: %s %s", c$id$orig_h, original_URI);
    }
}

# Deploy Zeek configuration
sudo so-zeek-restart

# Zeek packet analysis
sudo zeek -r /nsm/pcap/file.pcap local.zeek

# Extract files from network traffic
sudo zeek -r traffic.pcap /opt/so/conf/zeek/extract-files.zeek

# Zeek intelligence framework
sudo nano /opt/so/conf/zeek/intel.dat
# Format: indicator<tab>indicator_type<tab>meta.source
192.168.1.100	Intel::ADDR	malicious-ip-list
evil.com	Intel::DOMAIN	suspicious-domains

# Load intelligence data
sudo so-zeek-restart

# Monitor intelligence matches
tail -f /nsm/zeek/logs/current/intel.log

Wazuh (HIDS)

bash
# Wazuh manager configuration
sudo nano /opt/so/conf/wazuh/ossec.conf

# Deploy Wazuh agent (on monitored systems)
# Download agent
wget https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.x.x-1_amd64.deb

# Install agent
sudo dpkg -i wazuh-agent_4.x.x-1_amd64.deb

# Configure agent
sudo nano /var/ossec/etc/ossec.conf
# Set manager IP:
<client>
  <server>
    <address>192.168.1.100</address>
    <port>1514</port>
    <protocol>tcp</protocol>
  </server>
</client>

# Start agent
sudo systemctl enable wazuh-agent
sudo systemctl start wazuh-agent

# Register agent on manager
sudo /var/ossec/bin/manage_agents
# Select option 'A' to add agent
# Enter agent name and IP

# Extract agent key
sudo /var/ossec/bin/manage_agents
# Select option 'E' to extract key

# Import key on agent
sudo /var/ossec/bin/manage_agents
# Select option 'I' to import key

# Restart agent
sudo systemctl restart wazuh-agent

# Check agent status
sudo /var/ossec/bin/agent_control -lc

# Custom Wazuh rules
sudo nano /opt/so/conf/wazuh/rules/local_rules.xml

# Example custom rule
<group name="local,">
  <rule id="100001" level="10">
    <if_sid>5716</if_sid>
    <srcip>!192.168.1.0/24</srcip>
    <description>SSH login from external network</description>
    <group>authentication_success,pci_dss_10.2.5,</group>
  </rule>
</group>

# Test rule configuration
sudo /var/ossec/bin/ossec-logtest

# Restart Wazuh manager
sudo systemctl restart wazuh-manager

# Monitor Wazuh alerts
sudo tail -f /var/ossec/logs/alerts/alerts.log

Elastic Stack (ELK)

bash
# Elasticsearch management
sudo so-elasticsearch-restart
sudo so-elasticsearch-status

# Check Elasticsearch cluster health
curl -X GET "localhost:9200/_cluster/health?pretty"

# View Elasticsearch indices
curl -X GET "localhost:9200/_cat/indices?v"

# Elasticsearch configuration
sudo nano /opt/so/conf/elasticsearch/elasticsearch.yml

# Logstash management
sudo so-logstash-restart
sudo so-logstash-status

# Logstash configuration
ls -la /opt/so/conf/logstash/conf.d/

# Custom Logstash pipeline
sudo nano /opt/so/conf/logstash/conf.d/custom.conf

# Example Logstash configuration
input {
  beats {
    port => 5044
  }
}

filter {
  if [fields][log_type] == "custom" {
    grok {
      match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
    }
    date {
      match => [ "timestamp", "ISO8601" ]
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "custom-logs-%{+YYYY.MM.dd}"
  }
}

# Test Logstash configuration
sudo /usr/share/logstash/bin/logstash --config.test_and_exit -f /opt/so/conf/logstash/conf.d/custom.conf

# Kibana management
sudo so-kibana-restart
sudo so-kibana-status

# Access Kibana web interface
# https://manager-ip/kibana

# Create custom Kibana dashboard
# 1. Navigate to Kibana > Dashboard
# 2. Create new dashboard
# 3. Add visualizations
# 4. Save dashboard

# Export/Import Kibana objects
# Export
curl -X POST "localhost:5601/api/saved_objects/_export" \
  -H "Content-Type: application/json" \
  -H "kbn-xsrf: true" \
  -d '{"type": "dashboard"}' > dashboards.ndjson

# Import
curl -X POST "localhost:5601/api/saved_objects/_import" \
  -H "kbn-xsrf: true" \
  -F file=@dashboards.ndjson

TheHive (Case Management)

bash
# TheHive configuration
sudo nano /opt/so/conf/thehive/application.conf

# Example configuration
play.http.secret.key = "your-secret-key"
db.janusgraph {
  storage.backend = berkeleyje
  storage.directory = /opt/thehive/db
}

# Start TheHive
sudo so-thehive-restart
sudo so-thehive-status

# Access TheHive web interface
# https://manager-ip:9000

# Create organization and users
# 1. Login as admin
# 2. Navigate to Admin > Organizations
# 3. Create organization
# 4. Add users to organization

# API usage examples
THEHIVE_URL="https://localhost:9000"
API_KEY="your-api-key"

# Create case
curl -X POST "$THEHIVE_URL/api/case" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Suspicious Network Activity",
    "description": "Detected unusual traffic patterns",
    "severity": 2,
    "tlp": 2,
    "tags": ["network", "suspicious"]
  }'

# Add observable to case
curl -X POST "$THEHIVE_URL/api/case/{case-id}/artifact" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dataType": "ip",
    "data": "192.168.1.100",
    "message": "Suspicious IP address",
    "tags": ["malicious"]
  }'

# Search cases
curl -X POST "$THEHIVE_URL/api/case/_search" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "_and": [
        {"_field": "status", "_value": "Open"},
        {"_field": "severity", "_gte": 2}
      ]
    }
  }'

Cortex (Analysis Engine)

bash
# Cortex configuration
sudo nano /opt/so/conf/cortex/application.conf

# Example configuration
play.http.secret.key = "cortex-secret-key"
cortex.storage {
  provider = localfs
  localfs.location = /opt/cortex/files
}

# Start Cortex
sudo so-cortex-restart
sudo so-cortex-status

# Access Cortex web interface
# https://manager-ip:9001

# Install analyzers
sudo docker pull cortexneurons/virustotal_3_0
sudo docker pull cortexneurons/shodan_host_1_0
sudo docker pull cortexneurons/abuse_finder_1_0

# Configure analyzers
# 1. Login to Cortex
# 2. Navigate to Organization > Analyzers
# 3. Enable and configure analyzers
# 4. Add API keys for external services

# API usage examples
CORTEX_URL="https://localhost:9001"
API_KEY="your-cortex-api-key"

# Submit analysis job
curl -X POST "$CORTEX_URL/api/analyzer/VirusTotal_GetReport_3_0/run" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "malicious-hash",
    "dataType": "hash",
    "tlp": 2
  }'

# Get job results
curl -X GET "$CORTEX_URL/api/job/{job-id}" \
  -H "Authorization: Bearer $API_KEY"

# List available analyzers
curl -X GET "$CORTEX_URL/api/analyzer" \
  -H "Authorization: Bearer $API_KEY"

Network Security Monitoring

Packet Capture and Analysis

bash
# Full packet capture configuration
sudo nano /opt/so/conf/stenographer/config

# Example stenographer configuration
{
  "Threads": [
    { "PacketsDirectory": "/nsm/pcap", "MaxDirectoryFiles": 30000, "DiskFreePercentage": 10 }
  ],
  "StenotypePath": "/usr/bin/stenotype",
  "Interface": "eth1",
  "Port": 1234,
  "Host": "127.0.0.1",
  "Flags": [],
  "CertPath": "/opt/so/conf/stenographer/certs"
}

# Start packet capture
sudo so-stenographer-restart

# Query packet capture
sudo stenoread 'host 192.168.1.100' -w output.pcap

# Time-based queries
sudo stenoread 'host 192.168.1.100 and after 2023-01-01T00:00:00Z and before 2023-01-01T23:59:59Z' -w output.pcap

# Protocol-specific queries
sudo stenoread 'tcp and port 80' -w http-traffic.pcap
sudo stenoread 'udp and port 53' -w dns-traffic.pcap

# Advanced packet analysis with tcpdump
sudo tcpdump -i eth1 -w capture.pcap
sudo tcpdump -r capture.pcap 'host 192.168.1.100'
sudo tcpdump -r capture.pcap -A 'port 80'

# Packet analysis with tshark
sudo tshark -i eth1 -w capture.pcap
sudo tshark -r capture.pcap -Y "ip.addr == 192.168.1.100"
sudo tshark -r capture.pcap -Y "http.request.method == GET" -T fields -e http.host -e http.request.uri

# Extract files from packet capture
sudo tcpflow -r capture.pcap -o extracted_files/
sudo foremost -i capture.pcap -o carved_files/

# Network statistics
sudo capinfos capture.pcap
sudo editcap -A '2023-01-01 00:00:00' -B '2023-01-01 23:59:59' input.pcap output.pcap

Traffic Analysis and Hunting

bash
# Zeek-based traffic analysis
# Connection analysis
sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut id.orig_h id.resp_h id.resp_p proto duration bytes | sort | uniq -c | sort -nr

# DNS analysis
sudo zcat /nsm/zeek/logs/*/dns.log.gz | zeek-cut query answers | grep -v "^-" | sort | uniq -c | sort -nr

# HTTP analysis
sudo zcat /nsm/zeek/logs/*/http.log.gz | zeek-cut host uri user_agent | grep -E "(exe|zip|rar)" | sort | uniq

# SSL/TLS analysis
sudo zcat /nsm/zeek/logs/*/ssl.log.gz | zeek-cut server_name subject issuer | sort | uniq

# File analysis
sudo zcat /nsm/zeek/logs/*/files.log.gz | zeek-cut mime_type filename md5 | grep -E "(exe|pdf|doc)" | sort | uniq

# Custom Zeek analysis scripts
cat > /tmp/analyze_traffic.zeek << 'EOF'
@load base/protocols/http
@load base/protocols/dns

global suspicious_domains: set[string] = {
    "evil.com",
    "malware.net",
    "phishing.org"
};

event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) {
    if (query in suspicious_domains) {
        print fmt("Suspicious DNS query: %s -> %s", c$id$orig_h, query);
    }
}

event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) {
    if (/\.(exe|zip|rar)$/ in original_URI) {
        print fmt("Suspicious file download: %s -> %s", c$id$orig_h, original_URI);
    }
}
EOF

# Run analysis on packet capture
sudo zeek -r capture.pcap /tmp/analyze_traffic.zeek

# Threat hunting queries
# Long connections
sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut duration id.orig_h id.resp_h | awk '$1 > 3600' | sort -nr

# Large data transfers
sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut orig_bytes resp_bytes id.orig_h id.resp_h | awk '$1+$2 > 1000000' | sort -nr

# Unusual ports
sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut id.resp_p proto | sort | uniq -c | sort -nr | head -20

# Beaconing detection
sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut id.orig_h id.resp_h ts | awk '{print $1, $2, strftime("%H:%M", $3)}' | sort | uniq -c | awk '$1 > 10'

Alert Management and Investigation

bash
# Suricata alert analysis
# Real-time alert monitoring
sudo tail -f /nsm/suricata/eve.json | jq 'select(.event_type=="alert")'

# Alert statistics
sudo cat /nsm/suricata/eve.json | jq 'select(.event_type=="alert") | .alert.signature' | sort | uniq -c | sort -nr

# High-priority alerts
sudo cat /nsm/suricata/eve.json | jq 'select(.event_type=="alert" and .alert.severity<=2)'

# Alert correlation with Zeek logs
# Extract IPs from alerts
sudo cat /nsm/suricata/eve.json | jq -r 'select(.event_type=="alert") | "\(.src_ip) \(.dest_ip)"' | sort | uniq > alert_ips.txt

# Find corresponding Zeek connections
while read src_ip dest_ip; do
    sudo zcat /nsm/zeek/logs/*/conn.log.gz | zeek-cut id.orig_h id.resp_h | grep "$src_ip.*$dest_ip"
done < alert_ips.txt

# Custom alert processing script
cat > /usr/local/bin/process_alerts.py << 'EOF'
#!/usr/bin/env python3
import json
import sys
from datetime import datetime

def process_alert(alert):
    if alert.get('event_type') == 'alert':
        severity = alert.get('alert', {}).get('severity', 0)
        signature = alert.get('alert', {}).get('signature', '')
        src_ip = alert.get('src_ip', '')
        dest_ip = alert.get('dest_ip', '')
        timestamp = alert.get('timestamp', '')
        
        if severity <= 2:  # High priority alerts
            print(f"HIGH PRIORITY: {timestamp} - {signature}")
            print(f"  Source: {src_ip} -> Destination: {dest_ip}")
            print(f"  Severity: {severity}")
            print("-" * 50)

if __name__ == "__main__":
    for line in sys.stdin:
        try:
            alert = json.loads(line.strip())
            process_alert(alert)
        except json.JSONDecodeError:
            continue
EOF

chmod +x /usr/local/bin/process_alerts.py

# Process alerts
sudo tail -f /nsm/suricata/eve.json | /usr/local/bin/process_alerts.py

# Alert enrichment with threat intelligence
cat > /usr/local/bin/enrich_alerts.py << 'EOF'
#!/usr/bin/env python3
import json
import requests
import sys

def check_virustotal(ip):
    # Placeholder for VirusTotal API integration
    # Replace with actual API key and implementation
    return {"reputation": "unknown"}

def enrich_alert(alert):
    if alert.get('event_type') == 'alert':
        src_ip = alert.get('src_ip', '')
        dest_ip = alert.get('dest_ip', '')
        
        # Enrich with threat intelligence
        src_intel = check_virustotal(src_ip)
        dest_intel = check_virustotal(dest_ip)
        
        alert['enrichment'] = {
            'src_intel': src_intel,
            'dest_intel': dest_intel
        }
        
        return alert
    return alert

if __name__ == "__main__":
    for line in sys.stdin:
        try:
            alert = json.loads(line.strip())
            enriched = enrich_alert(alert)
            print(json.dumps(enriched))
        except json.JSONDecodeError:
            continue
EOF

chmod +x /usr/local/bin/enrich_alerts.py

System Administration

Service Management

bash
# SecurityOnion service management
sudo so-status                    # Check all services
sudo so-restart                   # Restart all services
sudo so-stop                      # Stop all services
sudo so-start                     # Start all services

# Individual service management
sudo so-elasticsearch-restart
sudo so-logstash-restart
sudo so-kibana-restart
sudo so-suricata-restart
sudo so-zeek-restart
sudo so-wazuh-restart
sudo so-thehive-restart
sudo so-cortex-restart

# Check service logs
sudo so-elasticsearch-logs
sudo so-logstash-logs
sudo so-kibana-logs

# Docker container management
sudo docker ps                   # List running containers
sudo docker logs container_name  # View container logs
sudo docker exec -it container_name /bin/bash  # Access container shell

# System resource monitoring
sudo so-top                       # SecurityOnion-specific top
htop                             # System resource usage
iotop                            # I/O monitoring
nethogs                          # Network usage by process

# Disk space management
df -h                            # Check disk usage
sudo du -sh /nsm/*               # Check NSM data usage
sudo find /nsm -name "*.log.gz" -mtime +30 -delete  # Clean old logs

# Log rotation configuration
sudo nano /etc/logrotate.d/securityonion

# Example logrotate configuration
/nsm/*/logs/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    create 644 root root
    postrotate
        /usr/bin/killall -HUP rsyslog
    endscript
}

Configuration Management

bash
# Backup SecurityOnion configuration
sudo so-backup

# Custom backup script
cat > /usr/local/bin/so-custom-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/opt/so/backup/$(date +%Y%m%d-%H%M%S)"
mkdir -p $BACKUP_DIR

# Backup configurations
cp -r /opt/so/conf $BACKUP_DIR/
cp -r /etc/nsm $BACKUP_DIR/
cp /etc/hostname $BACKUP_DIR/
cp /etc/hosts $BACKUP_DIR/

# Backup Elasticsearch indices list
curl -X GET "localhost:9200/_cat/indices?v" > $BACKUP_DIR/elasticsearch_indices.txt

# Backup Kibana objects
curl -X POST "localhost:5601/api/saved_objects/_export" \
  -H "Content-Type: application/json" \
  -H "kbn-xsrf: true" \
  -d '{"type": ["dashboard", "visualization", "search"]}' > $BACKUP_DIR/kibana_objects.ndjson

# Create archive
tar -czf $BACKUP_DIR.tar.gz -C /opt/so/backup $(basename $BACKUP_DIR)
rm -rf $BACKUP_DIR

echo "Backup created: $BACKUP_DIR.tar.gz"
EOF

chmod +x /usr/local/bin/so-custom-backup.sh

# Schedule regular backups
echo "0 2 * * * /usr/local/bin/so-custom-backup.sh" | sudo crontab -

# Configuration validation
sudo so-test                      # Test configuration
sudo so-checklist                 # Security checklist

# Update SecurityOnion
sudo so-update                    # Update packages
sudo so-upgrade                   # Upgrade to new version

# Network interface configuration
sudo nano /etc/netplan/01-netcfg.yaml

# Example netplan configuration
network:
  version: 2
  ethernets:
    eth0:  # Management interface
      dhcp4: true
    eth1:  # Monitoring interface
      dhcp4: false
      dhcp6: false

# Apply network configuration
sudo netplan apply

# Firewall configuration
sudo ufw status
sudo ufw allow from 192.168.1.0/24 to any port 443
sudo ufw allow from 192.168.1.0/24 to any port 9000
sudo ufw allow from 192.168.1.0/24 to any port 5601

# SSL certificate management
sudo so-ssl-update                # Update SSL certificates
sudo openssl x509 -in /etc/ssl/certs/so.crt -text -noout  # View certificate details

Performance Tuning

bash
# Elasticsearch performance tuning
sudo nano /opt/so/conf/elasticsearch/elasticsearch.yml

# Key performance settings
cluster.name: securityonion
node.name: so-node-1
path.data: /nsm/elasticsearch
path.logs: /var/log/elasticsearch
bootstrap.memory_lock: true
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node

# JVM heap size (50% of available RAM, max 32GB)
sudo nano /opt/so/conf/elasticsearch/jvm.options
-Xms16g
-Xmx16g

# Logstash performance tuning
sudo nano /opt/so/conf/logstash/logstash.yml

# Key performance settings
pipeline.workers: 8
pipeline.batch.size: 1000
pipeline.batch.delay: 50
path.queue: /nsm/logstash/queue
queue.type: persisted
queue.max_bytes: 10gb

# Suricata performance tuning
sudo nano /opt/so/conf/suricata/suricata.yaml

# AF_PACKET configuration
af-packet:
  - interface: eth1
    threads: 8
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    mmap-locked: yes
    tpacket-v3: yes
    ring-size: 200000
    block-size: 32768

# Threading configuration
threading:
  set-cpu-affinity: yes
  cpu-affinity:
    - management-cpu-set:
        cpu: [ "0-1" ]
    - receive-cpu-set:
        cpu: [ "2-5" ]
    - worker-cpu-set:
        cpu: [ "6-15" ]

# Zeek performance tuning
sudo nano /opt/so/conf/zeek/node.cfg

# Worker configuration
[worker-1]
type=worker
host=localhost
interface=eth1
lb_method=pf_ring
lb_procs=8
pin_cpus=2,3,4,5,6,7,8,9

# System-level optimizations
# Increase file descriptor limits
echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf

# Optimize network buffers
echo "net.core.rmem_max = 134217728" | sudo tee -a /etc/sysctl.conf
echo "net.core.wmem_max = 134217728" | sudo tee -a /etc/sysctl.conf
echo "net.core.netdev_max_backlog = 5000" | sudo tee -a /etc/sysctl.conf

# Apply sysctl changes
sudo sysctl -p

# Disk I/O optimization
# Use deadline scheduler for SSDs
echo deadline | sudo tee /sys/block/sda/queue/scheduler

# Mount options for performance
sudo nano /etc/fstab
# Add noatime,nodiratime options to reduce disk writes
/dev/sda1 /nsm ext4 defaults,noatime,nodiratime 0 2

Automation and Integration

API Integration

python
#!/usr/bin/env python3
# SecurityOnion API integration examples

import requests
import json
from datetime import datetime, timedelta

class SecurityOnionAPI:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.session = requests.Session()
        self.login(username, password)
    
    def login(self, username, password):
        """Authenticate with SecurityOnion"""
        login_data = {
            'username': username,
            'password': password
        }
        response = self.session.post(f"{self.base_url}/auth/login", json=login_data)
        if response.status_code == 200:
            print("Authentication successful")
        else:
            raise Exception("Authentication failed")
    
    def search_alerts(self, query, start_time=None, end_time=None):
        """Search for alerts in Elasticsearch"""
        if not start_time:
            start_time = datetime.now() - timedelta(hours=24)
        if not end_time:
            end_time = datetime.now()
        
        search_query = {
            "query": {
                "bool": {
                    "must": [
                        {"match": {"event_type": "alert"}},
                        {"query_string": {"query": query}},
                        {"range": {
                            "@timestamp": {
                                "gte": start_time.isoformat(),
                                "lte": end_time.isoformat()
                            }
                        }}
                    ]
                }
            },
            "sort": [{"@timestamp": {"order": "desc"}}],
            "size": 1000
        }
        
        response = self.session.post(
            f"{self.base_url}/elasticsearch/_search",
            json=search_query
        )
        return response.json()
    
    def get_zeek_logs(self, log_type, start_time=None, end_time=None):
        """Retrieve Zeek logs"""
        if not start_time:
            start_time = datetime.now() - timedelta(hours=1)
        if not end_time:
            end_time = datetime.now()
        
        query = {
            "query": {
                "bool": {
                    "must": [
                        {"match": {"event_type": log_type}},
                        {"range": {
                            "@timestamp": {
                                "gte": start_time.isoformat(),
                                "lte": end_time.isoformat()
                            }
                        }}
                    ]
                }
            },
            "sort": [{"@timestamp": {"order": "desc"}}],
            "size": 1000
        }
        
        response = self.session.post(
            f"{self.base_url}/elasticsearch/_search",
            json=query
        )
        return response.json()
    
    def create_case(self, title, description, severity=2):
        """Create case in TheHive"""
        case_data = {
            "title": title,
            "description": description,
            "severity": severity,
            "tlp": 2,
            "tags": ["automated"]
        }
        
        response = self.session.post(
            f"{self.base_url}/thehive/api/case",
            json=case_data
        )
        return response.json()

# Usage example
if __name__ == "__main__":
    so_api = SecurityOnionAPI("https://so-manager", "admin", "password")
    
    # Search for high-severity alerts
    alerts = so_api.search_alerts("alert.severity:[1 TO 2]")
    print(f"Found {len(alerts['hits']['hits'])} high-severity alerts")
    
    # Get recent DNS logs
    dns_logs = so_api.get_zeek_logs("dns")
    print(f"Found {len(dns_logs['hits']['hits'])} DNS events")
    
    # Create case for investigation
    case = so_api.create_case(
        "Automated Alert Investigation",
        "High-severity alerts detected requiring investigation"
    )
    print(f"Created case: {case.get('id')}")

Automated Response Scripts

bash
#!/bin/bash
# Automated incident response script

LOG_FILE="/var/log/so-automated-response.log"
ALERT_THRESHOLD=10
TIME_WINDOW=300  # 5 minutes

log_message() {
    echo "$(date): $1" >> $LOG_FILE
}

check_alert_volume() {
    RECENT_ALERTS=$(sudo tail -n 1000 /nsm/suricata/eve.json | \
        jq -r 'select(.event_type=="alert") | .timestamp' | \
        awk -v threshold=$(date -d "-${TIME_WINDOW} seconds" +%s) \
        'BEGIN{count=0} {
            gsub(/[TZ]/, " ", $1); 
            if(mktime(gensub(/-/, " ", "g", $1)) > threshold) count++
        } END{print count}')
    
    if [ "$RECENT_ALERTS" -gt "$ALERT_THRESHOLD" ]; then
        log_message "High alert volume detected: $RECENT_ALERTS alerts in last $TIME_WINDOW seconds"
        return 0
    else
        log_message "Normal alert volume: $RECENT_ALERTS alerts"
        return 1
    fi
}

block_suspicious_ip() {
    local IP=$1
    log_message "Blocking suspicious IP: $IP"
    
    # Add to firewall
    sudo iptables -I INPUT -s $IP -j DROP
    
    # Add to Suricata block list
    echo "$IP" | sudo tee -a /opt/so/rules/block.rules
    
    # Restart Suricata to apply new rules
    sudo so-suricata-restart
    
    log_message "IP $IP blocked successfully"
}

analyze_top_alerting_ips() {
    TOP_IPS=$(sudo tail -n 10000 /nsm/suricata/eve.json | \
        jq -r 'select(.event_type=="alert") | .src_ip' | \
        sort | uniq -c | sort -nr | head -5 | awk '$1 > 5 {print $2}')
    
    for IP in $TOP_IPS; do
        log_message "Analyzing suspicious IP: $IP"
        
        # Check if IP is external
        if [[ ! $IP =~ ^192\.168\. ]] && [[ ! $IP =~ ^10\. ]] && [[ ! $IP =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]]; then
            block_suspicious_ip $IP
        fi
    done
}

send_notification() {
    local MESSAGE=$1
    log_message "Sending notification: $MESSAGE"
    
    # Send email notification (configure sendmail/postfix)
    echo "$MESSAGE" | mail -s "SecurityOnion Alert" admin@company.com
    
    # Send Slack notification (configure webhook)
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"$MESSAGE\"}" \
        $SLACK_WEBHOOK_URL
}

main() {
    log_message "Starting automated response check"
    
    if check_alert_volume; then
        analyze_top_alerting_ips
        send_notification "High alert volume detected - automated response activated"
    fi
    
    log_message "Automated response check completed"
}

# Run main function
main

Integration with SOAR Platforms

python
#!/usr/bin/env python3
# Integration with external SOAR platforms

import requests
import json
from datetime import datetime

class SOARIntegration:
    def __init__(self, soar_url, api_key):
        self.soar_url = soar_url
        self.api_key = api_key
        self.headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json'
        }
    
    def create_incident(self, title, description, severity, artifacts):
        """Create incident in SOAR platform"""
        incident_data = {
            'name': title,
            'description': description,
            'severity': severity,
            'artifacts': artifacts,
            'source': 'SecurityOnion',
            'created_time': datetime.now().isoformat()
        }
        
        response = requests.post(
            f"{self.soar_url}/api/incidents",
            headers=self.headers,
            json=incident_data
        )
        return response.json()
    
    def add_artifact(self, incident_id, artifact_type, value, description):
        """Add artifact to existing incident"""
        artifact_data = {
            'type': artifact_type,
            'value': value,
            'description': description
        }
        
        response = requests.post(
            f"{self.soar_url}/api/incidents/{incident_id}/artifacts",
            headers=self.headers,
            json=artifact_data
        )
        return response.json()
    
    def run_playbook(self, incident_id, playbook_name):
        """Execute playbook for incident"""
        playbook_data = {
            'playbook': playbook_name,
            'incident_id': incident_id
        }
        
        response = requests.post(
            f"{self.soar_url}/api/playbooks/run",
            headers=self.headers,
            json=playbook_data
        )
        return response.json()

# Example usage
def process_security_alert(alert_data):
    soar = SOARIntegration("https://soar-platform", "api-key")
    
    # Extract relevant information
    title = f"Security Alert: {alert_data.get('alert', {}).get('signature', 'Unknown')}"
    description = f"Alert detected at {alert_data.get('timestamp')}"
    severity = alert_data.get('alert', {}).get('severity', 3)
    
    # Create artifacts
    artifacts = []
    if alert_data.get('src_ip'):
        artifacts.append({
            'type': 'ip',
            'value': alert_data['src_ip'],
            'description': 'Source IP address'
        })
    
    if alert_data.get('dest_ip'):
        artifacts.append({
            'type': 'ip',
            'value': alert_data['dest_ip'],
            'description': 'Destination IP address'
        })
    
    # Create incident
    incident = soar.create_incident(title, description, severity, artifacts)
    
    # Run appropriate playbook based on alert type
    if 'malware' in title.lower():
        soar.run_playbook(incident['id'], 'malware-investigation')
    elif 'phishing' in title.lower():
        soar.run_playbook(incident['id'], 'phishing-response')
    else:
        soar.run_playbook(incident['id'], 'generic-investigation')
    
    return incident

Resources