Appearance
Squert Cheat Sheet
Overview
Squert is a web-based application designed to provide a visual interface for querying and analyzing events stored in a Sguil database. Developed as a complementary tool to Sguil's traditional client interface, Squert offers security analysts an intuitive web-based platform for investigating network security incidents and performing event correlation analysis. The application leverages modern web technologies to present security event data in interactive charts, graphs, and tables, making it easier for analysts to identify patterns, trends, and anomalies in network traffic.
The core strength of Squert lies in its ability to transform raw security event data into meaningful visualizations that facilitate rapid threat analysis and incident response. Unlike traditional command-line or desktop-based security tools, Squert provides a responsive web interface that can be accessed from any modern browser, enabling distributed security teams to collaborate effectively on incident investigation and threat hunting activities. The application integrates seamlessly with existing Sguil deployments, utilizing the same MySQL database backend while providing enhanced visualization and analysis capabilities.
Squert's architecture is built around PHP and JavaScript technologies, with a focus on providing real-time access to security event data through dynamic web interfaces. The application supports advanced filtering, searching, and correlation features that allow analysts to drill down into specific events, investigate related activities, and generate comprehensive reports for management and compliance purposes. With its emphasis on usability and visual analytics, Squert has become an essential component of many security operations centers seeking to modernize their incident response workflows.
Installation
Ubuntu/Debian Installation
Installing Squert on Ubuntu/Debian systems:
bash
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install required dependencies
sudo apt install -y apache2 php php-mysql php-gd php-curl php-json \
php-xml php-mbstring libapache2-mod-php mysql-client git wget curl
# Enable Apache modules
sudo a2enmod rewrite
sudo a2enmod ssl
# Download Squert
cd /var/www
sudo git clone https://github.com/int13h/squert.git
sudo chown -R www-data:www-data squert
# Create Squert configuration directory
sudo mkdir -p /etc/squert
sudo chown www-data:www-data /etc/squert
# Set proper permissions
sudo chmod 755 /var/www/squert
sudo chmod -R 644 /var/www/squert/*
sudo chmod 755 /var/www/squert/scripts
# Create Apache virtual host
sudo cat > /etc/apache2/sites-available/squert.conf << 'EOF'
<VirtualHost *:80>
ServerName squert.local
DocumentRoot /var/www/squert
<Directory /var/www/squert>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
# Security headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
ErrorLog ${APACHE_LOG_DIR}/squert_error.log
CustomLog ${APACHE_LOG_DIR}/squert_access.log combined
</VirtualHost>
EOF
# Enable site and restart Apache
sudo a2ensite squert.conf
sudo systemctl restart apache2
# Create database user for Squert
mysql -u root -p << 'EOF'
CREATE USER 'squert'@'localhost' IDENTIFIED BY 'squertpassword';
GRANT SELECT ON sguildb.* TO 'squert'@'localhost';
FLUSH PRIVILEGES;
EOF
CentOS/RHEL Installation
bash
# Install EPEL repository
sudo yum install -y epel-release
# Install required packages
sudo yum install -y httpd php php-mysql php-gd php-curl php-json \
php-xml php-mbstring mysql git wget curl
# Start and enable Apache
sudo systemctl start httpd
sudo systemctl enable httpd
# Download Squert
cd /var/www/html
sudo git clone https://github.com/int13h/squert.git
sudo chown -R apache:apache squert
# Create configuration directory
sudo mkdir -p /etc/squert
sudo chown apache:apache /etc/squert
# Configure SELinux (if enabled)
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_connect_db 1
# Create Apache configuration
sudo cat > /etc/httpd/conf.d/squert.conf << 'EOF'
Alias /squert /var/www/html/squert
<Directory "/var/www/html/squert">
Options -Indexes
AllowOverride All
Require all granted
</Directory>
EOF
# Restart Apache
sudo systemctl restart httpd
# Configure firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Docker Installation
Running Squert in Docker containers:
bash
# Create Docker network
docker network create squert-network
# Create Squert container
cat > Dockerfile.squert << 'EOF'
FROM php:7.4-apache
# Install required PHP extensions
RUN docker-php-ext-install mysqli pdo pdo_mysql gd
# Install additional packages
RUN apt-get update && apt-get install -y \
git \
&& rm -rf /var/lib/apt/lists/*
# Clone Squert
RUN git clone https://github.com/int13h/squert.git /var/www/html/squert
# Set permissions
RUN chown -R www-data:www-data /var/www/html/squert
RUN chmod -R 755 /var/www/html/squert
# Create configuration directory
RUN mkdir -p /etc/squert && chown www-data:www-data /etc/squert
# Apache configuration
COPY squert.conf /etc/apache2/sites-available/
RUN a2ensite squert && a2enmod rewrite
EXPOSE 80
CMD ["apache2-foreground"]
EOF
# Create Apache configuration for container
cat > squert.conf << 'EOF'
<VirtualHost *:80>
DocumentRoot /var/www/html/squert
<Directory /var/www/html/squert>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/squert_error.log
CustomLog ${APACHE_LOG_DIR}/squert_access.log combined
</VirtualHost>
EOF
# Build and run Squert container
docker build -f Dockerfile.squert -t squert .
docker run -d --name squert \
--network squert-network \
-p 8080:80 \
-v squert-config:/etc/squert \
squert
# Connect to existing Sguil MySQL container
docker run -d --name squert-app \
--network squert-network \
-p 8080:80 \
-e MYSQL_HOST=sguil-mysql \
-e MYSQL_USER=squert \
-e MYSQL_PASSWORD=squertpassword \
-e MYSQL_DATABASE=sguildb \
squert
Manual Installation
bash
# Download latest Squert release
cd /tmp
wget https://github.com/int13h/squert/archive/master.tar.gz
tar -xzf master.tar.gz
# Copy to web directory
sudo cp -r squert-master /var/www/squert
sudo chown -R www-data:www-data /var/www/squert
# Set proper permissions
sudo find /var/www/squert -type f -exec chmod 644 {} \;
sudo find /var/www/squert -type d -exec chmod 755 {} \;
sudo chmod +x /var/www/squert/scripts/*
# Create symbolic link
sudo ln -s /var/www/squert /var/www/html/squert
# Create configuration directory
sudo mkdir -p /etc/squert
sudo chown www-data:www-data /etc/squert
Basic Usage
Initial Configuration
Setting up Squert configuration:
bash
# Create Squert configuration file
sudo cat > /var/www/squert/.scripts/squert.inc << 'EOF'
<?php
// Squert Configuration
// Database configuration
$dbname = 'sguildb';
$dbhost = 'localhost';
$dbuser = 'squert';
$dbpass = 'squertpassword';
$dbport = 3306;
// Application settings
$squert_title = 'Squert - Network Security Monitoring';
$refresh_time = 300; // 5 minutes
$event_limit = 1000;
// Time zone
date_default_timezone_set('America/New_York');
// Security settings
$use_https = false;
$session_timeout = 3600; // 1 hour
// Chart settings
$chart_height = 400;
$chart_width = 800;
// IP geolocation (optional)
$use_geoip = false;
$geoip_db_path = '/usr/share/GeoIP/GeoLiteCity.dat';
// External links
$whois_provider = 'https://whois.net/ip/';
$reputation_provider = 'https://www.virustotal.com/gui/ip-address/';
// Custom CSS
$custom_css = '';
// Debug mode
$debug = false;
?>
EOF
# Set proper permissions
sudo chown www-data:www-data /var/www/squert/.scripts/squert.inc
sudo chmod 600 /var/www/squert/.scripts/squert.inc
# Create database tables for Squert
mysql -u squert -p sguildb << 'EOF'
-- Create Squert-specific tables
CREATE TABLE IF NOT EXISTS mappings (
id INT AUTO_INCREMENT PRIMARY KEY,
ip_src VARCHAR(15),
ip_dst VARCHAR(15),
hostname VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS filters (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
filter_text TEXT,
user VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bookmarks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200),
url TEXT,
user VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
EOF
Web Interface Access
Accessing and using the Squert web interface:
bash
# Start Apache if not running
sudo systemctl start apache2
# Check Apache status
sudo systemctl status apache2
# Access Squert web interface
# Open browser and navigate to:
# http://your-server-ip/squert
# or
# http://localhost/squert
# Check Apache logs for any errors
sudo tail -f /var/log/apache2/squert_error.log
sudo tail -f /var/log/apache2/squert_access.log
# Test database connectivity
php -r "
\$conn = new mysqli('localhost', 'squert', 'squertpassword', 'sguildb');
if (\$conn->connect_error) {
die('Connection failed: ' . \$conn->connect_error);
}
echo 'Database connection successful\n';
\$conn->close();
"
Basic Navigation
Understanding Squert interface components:
bash
# Main dashboard components:
# 1. Summary view - Overview of recent events
# 2. Events view - Detailed event listing
# 3. Charts view - Visual analytics
# 4. Search interface - Advanced filtering
# 5. Reports section - Generated reports
# Key interface elements:
# - Time range selector
# - Event filters
# - Source/destination IP analysis
# - Signature analysis
# - Protocol breakdown
# - Geographic mapping (if enabled)
# Common workflows:
# 1. Monitor real-time events
# 2. Investigate specific incidents
# 3. Analyze traffic patterns
# 4. Generate compliance reports
# 5. Correlate related events
Advanced Features
Custom Dashboards
Creating custom dashboards and views:
php
<?php
// Custom dashboard configuration
// File: /var/www/squert/custom_dashboard.php
require_once '.scripts/squert.inc';
class CustomDashboard {
private $db;
public function __construct() {
global $dbhost, $dbuser, $dbpass, $dbname;
$this->db = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($this->db->connect_error) {
die("Connection failed: " . $this->db->connect_error);
}
}
public function getTopAttackers($limit = 10, $hours = 24) {
$query = "
SELECT
src_ip,
COUNT(*) as event_count,
COUNT(DISTINCT dst_ip) as target_count,
COUNT(DISTINCT signature_id) as signature_count,
MIN(timestamp) as first_seen,
MAX(timestamp) as last_seen
FROM event
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL ? HOUR)
GROUP BY src_ip
ORDER BY event_count DESC
LIMIT ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("ii", $hours, $limit);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
public function getTopTargets($limit = 10, $hours = 24) {
$query = "
SELECT
dst_ip,
COUNT(*) as event_count,
COUNT(DISTINCT src_ip) as attacker_count,
COUNT(DISTINCT signature_id) as signature_count,
MIN(timestamp) as first_seen,
MAX(timestamp) as last_seen
FROM event
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL ? HOUR)
GROUP BY dst_ip
ORDER BY event_count DESC
LIMIT ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("ii", $hours, $limit);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
public function getSignatureStats($hours = 24) {
$query = "
SELECT
signature,
signature_id,
COUNT(*) as event_count,
COUNT(DISTINCT src_ip) as unique_sources,
COUNT(DISTINCT dst_ip) as unique_targets
FROM event
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL ? HOUR)
GROUP BY signature, signature_id
ORDER BY event_count DESC
LIMIT 20
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("i", $hours);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
public function getHourlyEventCounts($hours = 24) {
$query = "
SELECT
DATE_FORMAT(timestamp, '%Y-%m-%d %H:00:00') as hour,
COUNT(*) as event_count
FROM event
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL ? HOUR)
GROUP BY DATE_FORMAT(timestamp, '%Y-%m-%d %H:00:00')
ORDER BY hour
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("i", $hours);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
public function generateDashboardHTML() {
$topAttackers = $this->getTopAttackers();
$topTargets = $this->getTopTargets();
$signatureStats = $this->getSignatureStats();
$hourlyStats = $this->getHourlyEventCounts();
ob_start();
?>
<!DOCTYPE html>
<html>
<head>
<title>Custom Security Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.dashboard-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.widget { border: 1px solid #ddd; padding: 15px; border-radius: 5px; }
.widget h3 { margin-top: 0; color: #333; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f2f2f2; }
.chart-container { width: 100%; height: 300px; }
</style>
</head>
<body>
<h1>Security Operations Dashboard</h1>
<p>Last updated: <?php echo date('Y-m-d H:i:s'); ?></p>
<div class="dashboard-grid">
<div class="widget">
<h3>Top Attackers (24h)</h3>
<table>
<tr><th>Source IP</th><th>Events</th><th>Targets</th><th>Signatures</th></tr>
<?php foreach ($topAttackers as $attacker): ?>
<tr>
<td><?php echo htmlspecialchars($attacker['src_ip']); ?></td>
<td><?php echo $attacker['event_count']; ?></td>
<td><?php echo $attacker['target_count']; ?></td>
<td><?php echo $attacker['signature_count']; ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<div class="widget">
<h3>Top Targets (24h)</h3>
<table>
<tr><th>Target IP</th><th>Events</th><th>Attackers</th><th>Signatures</th></tr>
<?php foreach ($topTargets as $target): ?>
<tr>
<td><?php echo htmlspecialchars($target['dst_ip']); ?></td>
<td><?php echo $target['event_count']; ?></td>
<td><?php echo $target['attacker_count']; ?></td>
<td><?php echo $target['signature_count']; ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<div class="widget">
<h3>Event Timeline (24h)</h3>
<div class="chart-container">
<canvas id="timelineChart"></canvas>
</div>
</div>
<div class="widget">
<h3>Top Signatures (24h)</h3>
<table>
<tr><th>Signature</th><th>Events</th><th>Sources</th></tr>
<?php foreach (array_slice($signatureStats, 0, 10) as $sig): ?>
<tr>
<td><?php echo htmlspecialchars(substr($sig['signature'], 0, 50)) . '...'; ?></td>
<td><?php echo $sig['event_count']; ?></td>
<td><?php echo $sig['unique_sources']; ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
</div>
<script>
// Timeline chart
const ctx = document.getElementById('timelineChart').getContext('2d');
const timelineData = <?php echo json_encode($hourlyStats); ?>;
new Chart(ctx, {
type: 'line',
data: {
labels: timelineData.map(item => item.hour),
datasets: [{
label: 'Events per Hour',
data: timelineData.map(item => item.event_count),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>
<?php
return ob_get_clean();
}
}
// Generate and display dashboard
$dashboard = new CustomDashboard();
echo $dashboard->generateDashboardHTML();
?>
Advanced Filtering and Search
Creating advanced search and filtering capabilities:
javascript
// Advanced search interface
// File: /var/www/squert/js/advanced_search.js
class AdvancedSearch {
constructor() {
this.filters = [];
this.initializeInterface();
}
initializeInterface() {
this.createSearchForm();
this.bindEvents();
}
createSearchForm() {
const searchContainer = document.getElementById('advanced-search');
searchContainer.innerHTML = `
<div class="search-form">
<h3>Advanced Event Search</h3>
<div class="filter-group">
<label>Time Range:</label>
<select id="time-range">
<option value="1h">Last Hour</option>
<option value="6h">Last 6 Hours</option>
<option value="24h">Last 24 Hours</option>
<option value="7d">Last 7 Days</option>
<option value="30d">Last 30 Days</option>
<option value="custom">Custom Range</option>
</select>
</div>
<div class="filter-group" id="custom-time" style="display:none;">
<label>From:</label>
<input type="datetime-local" id="start-time">
<label>To:</label>
<input type="datetime-local" id="end-time">
</div>
<div class="filter-group">
<label>Source IP:</label>
<input type="text" id="src-ip" placeholder="192.168.1.1 or 192.168.1.0/24">
</div>
<div class="filter-group">
<label>Destination IP:</label>
<input type="text" id="dst-ip" placeholder="10.0.0.1 or 10.0.0.0/16">
</div>
<div class="filter-group">
<label>Port Range:</label>
<input type="text" id="port-range" placeholder="80,443,8080-8090">
</div>
<div class="filter-group">
<label>Signature:</label>
<input type="text" id="signature" placeholder="Search signature text">
</div>
<div class="filter-group">
<label>Signature ID:</label>
<input type="text" id="signature-id" placeholder="1,2,3 or 100-200">
</div>
<div class="filter-group">
<label>Protocol:</label>
<select id="protocol">
<option value="">All Protocols</option>
<option value="1">ICMP</option>
<option value="6">TCP</option>
<option value="17">UDP</option>
</select>
</div>
<div class="filter-group">
<label>Event Limit:</label>
<select id="event-limit">
<option value="100">100 events</option>
<option value="500">500 events</option>
<option value="1000">1000 events</option>
<option value="5000">5000 events</option>
</select>
</div>
<div class="filter-actions">
<button id="search-btn" class="btn-primary">Search Events</button>
<button id="save-filter-btn" class="btn-secondary">Save Filter</button>
<button id="clear-btn" class="btn-secondary">Clear All</button>
</div>
<div id="saved-filters">
<h4>Saved Filters</h4>
<div id="filter-list"></div>
</div>
</div>
<div id="search-results">
<div id="results-summary"></div>
<div id="results-table"></div>
</div>
`;
}
bindEvents() {
// Time range change
document.getElementById('time-range').addEventListener('change', (e) => {
const customTime = document.getElementById('custom-time');
customTime.style.display = e.target.value === 'custom' ? 'block' : 'none';
});
// Search button
document.getElementById('search-btn').addEventListener('click', () => {
this.performSearch();
});
// Save filter button
document.getElementById('save-filter-btn').addEventListener('click', () => {
this.saveCurrentFilter();
});
// Clear button
document.getElementById('clear-btn').addEventListener('click', () => {
this.clearAllFilters();
});
// Load saved filters
this.loadSavedFilters();
}
buildSearchQuery() {
const filters = [];
// Time range
const timeRange = document.getElementById('time-range').value;
if (timeRange !== 'custom') {
filters.push(`timestamp >= DATE_SUB(NOW(), INTERVAL ${timeRange.replace(/[hd]/, timeRange.includes('h') ? ' HOUR' : ' DAY')})`);
} else {
const startTime = document.getElementById('start-time').value;
const endTime = document.getElementById('end-time').value;
if (startTime) filters.push(`timestamp >= '${startTime}'`);
if (endTime) filters.push(`timestamp <= '${endTime}'`);
}
// IP addresses
const srcIp = document.getElementById('src-ip').value.trim();
if (srcIp) {
if (srcIp.includes('/')) {
// CIDR notation
filters.push(`INET_ATON(src_ip) & INET_ATON('${srcIp.split('/')[1]}') = INET_ATON('${srcIp.split('/')[0]}')`);
} else {
filters.push(`src_ip = '${srcIp}'`);
}
}
const dstIp = document.getElementById('dst-ip').value.trim();
if (dstIp) {
if (dstIp.includes('/')) {
filters.push(`INET_ATON(dst_ip) & INET_ATON('${dstIp.split('/')[1]}') = INET_ATON('${dstIp.split('/')[0]}')`);
} else {
filters.push(`dst_ip = '${dstIp}'`);
}
}
// Ports
const portRange = document.getElementById('port-range').value.trim();
if (portRange) {
const portConditions = [];
portRange.split(',').forEach(port => {
if (port.includes('-')) {
const [start, end] = port.split('-');
portConditions.push(`(src_port BETWEEN ${start} AND ${end}) OR (dst_port BETWEEN ${start} AND ${end})`);
} else {
portConditions.push(`src_port = ${port} OR dst_port = ${port}`);
}
});
filters.push(`(${portConditions.join(' OR ')})`);
}
// Signature
const signature = document.getElementById('signature').value.trim();
if (signature) {
filters.push(`signature LIKE '%${signature}%'`);
}
// Signature ID
const signatureId = document.getElementById('signature-id').value.trim();
if (signatureId) {
if (signatureId.includes('-')) {
const [start, end] = signatureId.split('-');
filters.push(`signature_id BETWEEN ${start} AND ${end}`);
} else if (signatureId.includes(',')) {
filters.push(`signature_id IN (${signatureId})`);
} else {
filters.push(`signature_id = ${signatureId}`);
}
}
// Protocol
const protocol = document.getElementById('protocol').value;
if (protocol) {
filters.push(`ip_proto = ${protocol}`);
}
return filters.join(' AND ');
}
async performSearch() {
const whereClause = this.buildSearchQuery();
const limit = document.getElementById('event-limit').value;
const query = `
SELECT
sid, cid, timestamp, src_ip, src_port, dst_ip, dst_port,
ip_proto, signature, signature_id
FROM event
${whereClause ? 'WHERE ' + whereClause : ''}
ORDER BY timestamp DESC
LIMIT ${limit}
`;
try {
const response = await fetch('search_events.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ query: query })
});
const results = await response.json();
this.displayResults(results);
} catch (error) {
console.error('Search failed:', error);
this.displayError('Search failed: ' + error.message);
}
}
displayResults(results) {
const summaryDiv = document.getElementById('results-summary');
const tableDiv = document.getElementById('results-table');
summaryDiv.innerHTML = `
<h3>Search Results</h3>
<p>Found ${results.length} events</p>
`;
if (results.length === 0) {
tableDiv.innerHTML = '<p>No events found matching the search criteria.</p>';
return;
}
let tableHTML = `
<table class="results-table">
<thead>
<tr>
<th>Timestamp</th>
<th>Source</th>
<th>Destination</th>
<th>Protocol</th>
<th>Signature</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
`;
results.forEach(event => {
tableHTML += `
<tr>
<td>${event.timestamp}</td>
<td>${event.src_ip}:${event.src_port}</td>
<td>${event.dst_ip}:${event.dst_port}</td>
<td>${this.getProtocolName(event.ip_proto)}</td>
<td title="${event.signature}">${event.signature.substring(0, 50)}...</td>
<td>
<button onclick="viewEventDetails(${event.sid}, ${event.cid})">Details</button>
<button onclick="investigateIP('${event.src_ip}')">Investigate</button>
</td>
</tr>
`;
});
tableHTML += '</tbody></table>';
tableDiv.innerHTML = tableHTML;
}
getProtocolName(proto) {
const protocols = { 1: 'ICMP', 6: 'TCP', 17: 'UDP' };
return protocols[proto] || proto;
}
saveCurrentFilter() {
const filterName = prompt('Enter a name for this filter:');
if (!filterName) return;
const filterData = {
name: filterName,
timeRange: document.getElementById('time-range').value,
srcIp: document.getElementById('src-ip').value,
dstIp: document.getElementById('dst-ip').value,
portRange: document.getElementById('port-range').value,
signature: document.getElementById('signature').value,
signatureId: document.getElementById('signature-id').value,
protocol: document.getElementById('protocol').value,
limit: document.getElementById('event-limit').value
};
// Save to localStorage
const savedFilters = JSON.parse(localStorage.getItem('squert_filters') || '[]');
savedFilters.push(filterData);
localStorage.setItem('squert_filters', JSON.stringify(savedFilters));
this.loadSavedFilters();
}
loadSavedFilters() {
const savedFilters = JSON.parse(localStorage.getItem('squert_filters') || '[]');
const filterList = document.getElementById('filter-list');
filterList.innerHTML = '';
savedFilters.forEach((filter, index) => {
const filterDiv = document.createElement('div');
filterDiv.className = 'saved-filter';
filterDiv.innerHTML = `
<span>${filter.name}</span>
<button onclick="loadFilter(${index})">Load</button>
<button onclick="deleteFilter(${index})">Delete</button>
`;
filterList.appendChild(filterDiv);
});
}
clearAllFilters() {
document.getElementById('time-range').value = '24h';
document.getElementById('src-ip').value = '';
document.getElementById('dst-ip').value = '';
document.getElementById('port-range').value = '';
document.getElementById('signature').value = '';
document.getElementById('signature-id').value = '';
document.getElementById('protocol').value = '';
document.getElementById('event-limit').value = '1000';
document.getElementById('custom-time').style.display = 'none';
}
}
// Initialize advanced search when page loads
document.addEventListener('DOMContentLoaded', () => {
new AdvancedSearch();
});
// Global functions for event handling
function viewEventDetails(sid, cid) {
window.open(`event_details.php?sid=${sid}&cid=${cid}`, '_blank');
}
function investigateIP(ip) {
window.open(`ip_investigation.php?ip=${ip}`, '_blank');
}
function loadFilter(index) {
const savedFilters = JSON.parse(localStorage.getItem('squert_filters') || '[]');
const filter = savedFilters[index];
if (filter) {
document.getElementById('time-range').value = filter.timeRange;
document.getElementById('src-ip').value = filter.srcIp;
document.getElementById('dst-ip').value = filter.dstIp;
document.getElementById('port-range').value = filter.portRange;
document.getElementById('signature').value = filter.signature;
document.getElementById('signature-id').value = filter.signatureId;
document.getElementById('protocol').value = filter.protocol;
document.getElementById('event-limit').value = filter.limit;
}
}
function deleteFilter(index) {
if (confirm('Are you sure you want to delete this filter?')) {
const savedFilters = JSON.parse(localStorage.getItem('squert_filters') || '[]');
savedFilters.splice(index, 1);
localStorage.setItem('squert_filters', JSON.stringify(savedFilters));
// Reload the filter list
const search = new AdvancedSearch();
search.loadSavedFilters();
}
}
Automated Reporting
Creating automated reporting capabilities:
php
<?php
// Automated reporting system
// File: /var/www/squert/reports/automated_reports.php
require_once '../.scripts/squert.inc';
class AutomatedReports {
private $db;
private $config;
public function __construct() {
global $dbhost, $dbuser, $dbpass, $dbname;
$this->db = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($this->db->connect_error) {
die("Connection failed: " . $this->db->connect_error);
}
$this->config = [
'report_dir' => '/var/www/squert/reports/generated',
'email_reports' => true,
'email_to' => 'security@company.com',
'email_from' => 'squert@company.com'
];
// Create reports directory if it doesn't exist
if (!is_dir($this->config['report_dir'])) {
mkdir($this->config['report_dir'], 0755, true);
}
}
public function generateDailyReport($date = null) {
if (!$date) {
$date = date('Y-m-d', strtotime('-1 day'));
}
$report_data = [
'date' => $date,
'summary' => $this->getDailySummary($date),
'top_attackers' => $this->getTopAttackers($date),
'top_targets' => $this->getTopTargets($date),
'top_signatures' => $this->getTopSignatures($date),
'hourly_stats' => $this->getHourlyStats($date),
'protocol_breakdown' => $this->getProtocolBreakdown($date),
'geographic_analysis' => $this->getGeographicAnalysis($date)
];
$html_report = $this->generateHTMLReport($report_data, 'daily');
$pdf_report = $this->generatePDFReport($report_data, 'daily');
// Save reports
$html_file = $this->config['report_dir'] . "/daily_report_{$date}.html";
$pdf_file = $this->config['report_dir'] . "/daily_report_{$date}.pdf";
file_put_contents($html_file, $html_report);
file_put_contents($pdf_file, $pdf_report);
// Email report if configured
if ($this->config['email_reports']) {
$this->emailReport($html_file, $pdf_file, "Daily Security Report - {$date}");
}
return [
'html_file' => $html_file,
'pdf_file' => $pdf_file,
'data' => $report_data
];
}
public function generateWeeklyReport($week_start = null) {
if (!$week_start) {
$week_start = date('Y-m-d', strtotime('last monday'));
}
$week_end = date('Y-m-d', strtotime($week_start . ' +6 days'));
$report_data = [
'week_start' => $week_start,
'week_end' => $week_end,
'summary' => $this->getWeeklySummary($week_start, $week_end),
'trends' => $this->getWeeklyTrends($week_start, $week_end),
'top_incidents' => $this->getTopIncidents($week_start, $week_end),
'security_metrics' => $this->getSecurityMetrics($week_start, $week_end)
];
$html_report = $this->generateHTMLReport($report_data, 'weekly');
$html_file = $this->config['report_dir'] . "/weekly_report_{$week_start}_to_{$week_end}.html";
file_put_contents($html_file, $html_report);
return ['html_file' => $html_file, 'data' => $report_data];
}
private function getDailySummary($date) {
$query = "
SELECT
COUNT(*) as total_events,
COUNT(DISTINCT src_ip) as unique_sources,
COUNT(DISTINCT dst_ip) as unique_targets,
COUNT(DISTINCT signature_id) as unique_signatures,
MIN(timestamp) as first_event,
MAX(timestamp) as last_event
FROM event
WHERE DATE(timestamp) = ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("s", $date);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
private function getTopAttackers($date, $limit = 10) {
$query = "
SELECT
src_ip,
COUNT(*) as event_count,
COUNT(DISTINCT dst_ip) as target_count,
COUNT(DISTINCT signature_id) as signature_count,
GROUP_CONCAT(DISTINCT signature ORDER BY signature LIMIT 3) as top_signatures
FROM event
WHERE DATE(timestamp) = ?
GROUP BY src_ip
ORDER BY event_count DESC
LIMIT ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("si", $date, $limit);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
private function getTopTargets($date, $limit = 10) {
$query = "
SELECT
dst_ip,
COUNT(*) as event_count,
COUNT(DISTINCT src_ip) as attacker_count,
COUNT(DISTINCT signature_id) as signature_count
FROM event
WHERE DATE(timestamp) = ?
GROUP BY dst_ip
ORDER BY event_count DESC
LIMIT ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("si", $date, $limit);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
private function getTopSignatures($date, $limit = 15) {
$query = "
SELECT
signature,
signature_id,
COUNT(*) as event_count,
COUNT(DISTINCT src_ip) as unique_sources,
COUNT(DISTINCT dst_ip) as unique_targets
FROM event
WHERE DATE(timestamp) = ?
GROUP BY signature, signature_id
ORDER BY event_count DESC
LIMIT ?
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("si", $date, $limit);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
private function getHourlyStats($date) {
$query = "
SELECT
HOUR(timestamp) as hour,
COUNT(*) as event_count
FROM event
WHERE DATE(timestamp) = ?
GROUP BY HOUR(timestamp)
ORDER BY hour
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("s", $date);
$stmt->execute();
$results = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
// Fill in missing hours with 0 events
$hourly_stats = array_fill(0, 24, 0);
foreach ($results as $row) {
$hourly_stats[$row['hour']] = $row['event_count'];
}
return $hourly_stats;
}
private function getProtocolBreakdown($date) {
$query = "
SELECT
ip_proto,
COUNT(*) as event_count,
ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM event WHERE DATE(timestamp) = ?), 2) as percentage
FROM event
WHERE DATE(timestamp) = ?
GROUP BY ip_proto
ORDER BY event_count DESC
";
$stmt = $this->db->prepare($query);
$stmt->bind_param("ss", $date, $date);
$stmt->execute();
return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}
private function generateHTMLReport($data, $type) {
ob_start();
?>
<!DOCTYPE html>
<html>
<head>
<title><?php echo ucfirst($type); ?> Security Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background: #f4f4f4; padding: 20px; border-radius: 5px; }
.summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; }
.metric { background: #e9e9e9; padding: 15px; border-radius: 5px; text-align: center; }
.metric h3 { margin: 0; color: #333; }
.metric .value { font-size: 24px; font-weight: bold; color: #007cba; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f2f2f2; }
.chart { margin: 20px 0; }
.section { margin: 30px 0; }
.section h2 { color: #333; border-bottom: 2px solid #007cba; padding-bottom: 5px; }
</style>
</head>
<body>
<div class="header">
<h1><?php echo ucfirst($type); ?> Security Report</h1>
<?php if ($type == 'daily'): ?>
<p>Report Date: <?php echo $data['date']; ?></p>
<?php else: ?>
<p>Report Period: <?php echo $data['week_start']; ?> to <?php echo $data['week_end']; ?></p>
<?php endif; ?>
<p>Generated: <?php echo date('Y-m-d H:i:s'); ?></p>
</div>
<?php if ($type == 'daily'): ?>
<div class="summary">
<div class="metric">
<h3>Total Events</h3>
<div class="value"><?php echo number_format($data['summary']['total_events']); ?></div>
</div>
<div class="metric">
<h3>Unique Sources</h3>
<div class="value"><?php echo number_format($data['summary']['unique_sources']); ?></div>
</div>
<div class="metric">
<h3>Unique Targets</h3>
<div class="value"><?php echo number_format($data['summary']['unique_targets']); ?></div>
</div>
<div class="metric">
<h3>Unique Signatures</h3>
<div class="value"><?php echo number_format($data['summary']['unique_signatures']); ?></div>
</div>
</div>
<div class="section">
<h2>Top Attackers</h2>
<table>
<tr><th>Source IP</th><th>Events</th><th>Targets</th><th>Signatures</th></tr>
<?php foreach ($data['top_attackers'] as $attacker): ?>
<tr>
<td><?php echo htmlspecialchars($attacker['src_ip']); ?></td>
<td><?php echo number_format($attacker['event_count']); ?></td>
<td><?php echo number_format($attacker['target_count']); ?></td>
<td><?php echo number_format($attacker['signature_count']); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<div class="section">
<h2>Top Targets</h2>
<table>
<tr><th>Target IP</th><th>Events</th><th>Attackers</th><th>Signatures</th></tr>
<?php foreach ($data['top_targets'] as $target): ?>
<tr>
<td><?php echo htmlspecialchars($target['dst_ip']); ?></td>
<td><?php echo number_format($target['event_count']); ?></td>
<td><?php echo number_format($target['attacker_count']); ?></td>
<td><?php echo number_format($target['signature_count']); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<div class="section">
<h2>Top Signatures</h2>
<table>
<tr><th>Signature</th><th>Events</th><th>Sources</th><th>Targets</th></tr>
<?php foreach ($data['top_signatures'] as $sig): ?>
<tr>
<td><?php echo htmlspecialchars($sig['signature']); ?></td>
<td><?php echo number_format($sig['event_count']); ?></td>
<td><?php echo number_format($sig['unique_sources']); ?></td>
<td><?php echo number_format($sig['unique_targets']); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<?php endif; ?>
</body>
</html>
<?php
return ob_get_clean();
}
private function emailReport($html_file, $pdf_file, $subject) {
// Implementation depends on your email system
// This is a basic example using PHP mail()
$to = $this->config['email_to'];
$from = $this->config['email_from'];
$headers = "From: {$from}\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
$body = file_get_contents($html_file);
mail($to, $subject, $body, $headers);
}
}
// Usage example
if (php_sapi_name() === 'cli') {
// Command line usage
$reports = new AutomatedReports();
$action = $argv[1] ?? 'daily';
$date = $argv[2] ?? null;
switch ($action) {
case 'daily':
$result = $reports->generateDailyReport($date);
echo "Daily report generated: {$result['html_file']}\n";
break;
case 'weekly':
$result = $reports->generateWeeklyReport($date);
echo "Weekly report generated: {$result['html_file']}\n";
break;
default:
echo "Usage: php automated_reports.php [daily|weekly] [date]\n";
}
}
?>
Automation Scripts
Comprehensive Monitoring Script
bash
#!/bin/bash
# Comprehensive Squert monitoring and maintenance
# Configuration
SQUERT_DIR="/var/www/squert"
LOG_DIR="/var/log/squert"
BACKUP_DIR="/var/backups/squert"
CONFIG_FILE="/var/www/squert/.scripts/squert.inc"
APACHE_LOG="/var/log/apache2/squert_error.log"
# Database configuration
DB_HOST="localhost"
DB_USER="squert"
DB_PASS="squertpassword"
DB_NAME="sguildb"
# Monitoring thresholds
MAX_LOG_SIZE="100M"
MAX_DISK_USAGE="90"
MAX_RESPONSE_TIME="5"
# Create necessary directories
mkdir -p "$LOG_DIR" "$BACKUP_DIR"
# Logging function
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_DIR/monitor.log"
}
# Check Squert web interface availability
check_web_interface() {
log_message "Checking Squert web interface..."
local response_time
response_time=$(curl -o /dev/null -s -w '%{time_total}' http://localhost/squert/ 2>/dev/null)
if [ $? -eq 0 ]; then
log_message "Web interface is accessible (response time: ${response_time}s)"
# Check if response time is acceptable
if (( $(echo "$response_time > $MAX_RESPONSE_TIME" | bc -l) )); then
log_message "WARNING: Slow response time: ${response_time}s (threshold: ${MAX_RESPONSE_TIME}s)"
return 1
fi
return 0
else
log_message "ERROR: Web interface is not accessible"
return 1
fi
}
# Check database connectivity
check_database() {
log_message "Checking database connectivity..."
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SELECT 1;" >/dev/null 2>&1
if [ $? -eq 0 ]; then
log_message "Database connection successful"
# Check recent events
local recent_events
recent_events=$(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" -N -e "
SELECT COUNT(*) FROM event WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 1 HOUR);
" 2>/dev/null)
log_message "Recent events (last hour): $recent_events"
return 0
else
log_message "ERROR: Database connection failed"
return 1
fi
}
# Check Apache service
check_apache() {
log_message "Checking Apache service..."
if systemctl is-active --quiet apache2; then
log_message "Apache service is running"
# Check for recent errors
local error_count
error_count=$(tail -n 100 "$APACHE_LOG" 2>/dev/null | grep -c "$(date '+%Y-%m-%d')" || echo "0")
if [ "$error_count" -gt 10 ]; then
log_message "WARNING: High number of Apache errors today: $error_count"
fi
return 0
else
log_message "ERROR: Apache service is not running"
return 1
fi
}
# Check disk space
check_disk_space() {
log_message "Checking disk space..."
local usage
usage=$(df -h "$SQUERT_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
log_message "Disk usage: ${usage}%"
if [ "$usage" -gt "$MAX_DISK_USAGE" ]; then
log_message "WARNING: High disk usage: ${usage}% (threshold: ${MAX_DISK_USAGE}%)"
return 1
fi
return 0
}
# Check log file sizes
check_log_sizes() {
log_message "Checking log file sizes..."
find "$LOG_DIR" -name "*.log" -size +"$MAX_LOG_SIZE" | while read -r logfile; do
log_message "WARNING: Large log file: $logfile"
# Rotate large log files
if [ -f "$logfile" ]; then
mv "$logfile" "${logfile}.$(date +%Y%m%d-%H%M%S)"
touch "$logfile"
chown www-data:www-data "$logfile"
log_message "Rotated log file: $logfile"
fi
done
}
# Performance optimization
optimize_performance() {
log_message "Running performance optimization..."
# Clear PHP opcache if available
if command -v php >/dev/null 2>&1; then
php -r "if (function_exists('opcache_reset')) { opcache_reset(); echo 'OPcache cleared\n'; }"
fi
# Optimize database tables
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "
OPTIMIZE TABLE event;
ANALYZE TABLE event;
" >/dev/null 2>&1
log_message "Performance optimization completed"
}
# Backup configuration
backup_configuration() {
log_message "Backing up Squert configuration..."
local backup_file="$BACKUP_DIR/squert-config-$(date +%Y%m%d-%H%M%S).tar.gz"
tar -czf "$backup_file" \
"$CONFIG_FILE" \
"$SQUERT_DIR/.scripts/" \
"/etc/apache2/sites-available/squert.conf" \
2>/dev/null
if [ $? -eq 0 ]; then
log_message "Configuration backup created: $backup_file"
# Keep only last 7 days of backups
find "$BACKUP_DIR" -name "squert-config-*.tar.gz" -mtime +7 -delete
return 0
else
log_message "ERROR: Configuration backup failed"
return 1
fi
}
# Generate health report
generate_health_report() {
log_message "Generating health report..."
local report_file="$LOG_DIR/health-report-$(date +%Y%m%d-%H%M%S).html"
cat > "$report_file" << EOF
<!DOCTYPE html>
<html>
<head>
<title>Squert Health Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.status-ok { color: green; }
.status-warning { color: orange; }
.status-error { color: red; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>Squert Health Report</h1>
<p>Generated: $(date)</p>
<h2>System Status</h2>
<table>
<tr><th>Component</th><th>Status</th><th>Details</th></tr>
EOF
# Check each component and add to report
if check_web_interface >/dev/null 2>&1; then
echo " <tr><td>Web Interface</td><td class=\"status-ok\">OK</td><td>Accessible</td></tr>" >> "$report_file"
else
echo " <tr><td>Web Interface</td><td class=\"status-error\">ERROR</td><td>Not accessible</td></tr>" >> "$report_file"
fi
if check_database >/dev/null 2>&1; then
echo " <tr><td>Database</td><td class=\"status-ok\">OK</td><td>Connected</td></tr>" >> "$report_file"
else
echo " <tr><td>Database</td><td class=\"status-error\">ERROR</td><td>Connection failed</td></tr>" >> "$report_file"
fi
if check_apache >/dev/null 2>&1; then
echo " <tr><td>Apache</td><td class=\"status-ok\">OK</td><td>Running</td></tr>" >> "$report_file"
else
echo " <tr><td>Apache</td><td class=\"status-error\">ERROR</td><td>Not running</td></tr>" >> "$report_file"
fi
local disk_usage
disk_usage=$(df -h "$SQUERT_DIR" | awk 'NR==2 {print $5}')
echo " <tr><td>Disk Space</td><td class=\"status-ok\">OK</td><td>Usage: $disk_usage</td></tr>" >> "$report_file"
cat >> "$report_file" << EOF
</table>
<h2>Recent Activity</h2>
<pre>$(tail -n 20 "$LOG_DIR/monitor.log" 2>/dev/null || echo "No recent activity logged")</pre>
</body>
</html>
EOF
log_message "Health report generated: $report_file"
}
# Send alert notification
send_alert() {
local subject="$1"
local message="$2"
# Send email if mail is configured
if command -v mail >/dev/null 2>&1; then
echo "$message" | mail -s "Squert Alert: $subject" security@company.com
fi
# Log to syslog
logger -t squert-monitor "$subject: $message"
log_message "Alert sent: $subject"
}
# Main monitoring function
run_monitoring() {
log_message "Starting Squert monitoring cycle"
local issues=0
# Run all checks
check_web_interface || ((issues++))
check_database || ((issues++))
check_apache || ((issues++))
check_disk_space || ((issues++))
check_log_sizes
# Performance optimization (weekly)
if [ "$(date +%u)" -eq 1 ] && [ "$(date +%H)" -eq 2 ]; then
optimize_performance
backup_configuration
fi
# Generate health report (daily)
if [ "$(date +%H)" -eq 6 ]; then
generate_health_report
fi
# Send alerts if issues found
if [ "$issues" -gt 0 ]; then
send_alert "System Issues Detected" "Found $issues issues during monitoring. Check logs for details."
fi
log_message "Monitoring cycle completed with $issues issues"
return $issues
}
# Maintenance functions
cleanup_old_files() {
log_message "Cleaning up old files..."
# Remove old log files (older than 30 days)
find "$LOG_DIR" -name "*.log.*" -mtime +30 -delete
# Remove old health reports (older than 7 days)
find "$LOG_DIR" -name "health-report-*.html" -mtime +7 -delete
# Remove old backups (older than 30 days)
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
log_message "Cleanup completed"
}
# Update Squert
update_squert() {
log_message "Checking for Squert updates..."
cd "$SQUERT_DIR" || exit 1
# Backup current version
backup_configuration
# Check for updates
git fetch origin
local current_commit
local latest_commit
current_commit=$(git rev-parse HEAD)
latest_commit=$(git rev-parse origin/master)
if [ "$current_commit" != "$latest_commit" ]; then
log_message "Updates available, updating Squert..."
# Pull updates
git pull origin master
# Set proper permissions
chown -R www-data:www-data "$SQUERT_DIR"
find "$SQUERT_DIR" -type f -exec chmod 644 {} \;
find "$SQUERT_DIR" -type d -exec chmod 755 {} \;
# Restart Apache
systemctl restart apache2
log_message "Squert updated successfully"
else
log_message "Squert is up to date"
fi
}
# Command line interface
case "${1:-monitor}" in
"monitor")
run_monitoring
;;
"cleanup")
cleanup_old_files
;;
"update")
update_squert
;;
"backup")
backup_configuration
;;
"report")
generate_health_report
;;
"optimize")
optimize_performance
;;
*)
echo "Usage: $0 {monitor|cleanup|update|backup|report|optimize}"
echo ""
echo "Commands:"
echo " monitor - Run complete monitoring cycle (default)"
echo " cleanup - Clean up old files and logs"
echo " update - Check for and apply Squert updates"
echo " backup - Backup Squert configuration"
echo " report - Generate health report"
echo " optimize - Run performance optimization"
exit 1
;;
esac
Integration Examples
SIEM Integration
python
#!/usr/bin/env python3
# Squert SIEM integration script
import mysql.connector
import json
import requests
import time
from datetime import datetime, timedelta
class SquertSIEMIntegration:
def __init__(self, config):
self.config = config
self.db_config = config['database']
self.siem_config = config['siem']
def connect_database(self):
"""Connect to Squert/Sguil database"""
return mysql.connector.connect(**self.db_config)
def get_recent_events(self, minutes=5):
"""Get events from the last N minutes"""
connection = self.connect_database()
cursor = connection.cursor(dictionary=True)
query = """
SELECT
sid, cid, timestamp, src_ip, src_port, dst_ip, dst_port,
ip_proto, signature, signature_gen, signature_id, signature_rev
FROM event
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL %s MINUTE)
ORDER BY timestamp DESC
"""
cursor.execute(query, (minutes,))
events = cursor.fetchall()
cursor.close()
connection.close()
return events
def transform_for_splunk(self, events):
"""Transform events for Splunk HEC"""
splunk_events = []
for event in events:
splunk_event = {
"time": int(event['timestamp'].timestamp()),
"source": "squert",
"sourcetype": "sguil:event",
"index": self.siem_config.get('splunk_index', 'security'),
"event": {
"sid": event['sid'],
"cid": event['cid'],
"timestamp": event['timestamp'].isoformat(),
"src_ip": event['src_ip'],
"src_port": event['src_port'],
"dst_ip": event['dst_ip'],
"dst_port": event['dst_port'],
"protocol": event['ip_proto'],
"signature": event['signature'],
"signature_id": event['signature_id'],
"signature_gen": event['signature_gen'],
"signature_rev": event['signature_rev']
}
}
splunk_events.append(splunk_event)
return splunk_events
def send_to_splunk(self, events):
"""Send events to Splunk via HEC"""
if not events:
return True
splunk_events = self.transform_for_splunk(events)
headers = {
'Authorization': f"Splunk {self.siem_config['splunk_token']}",
'Content-Type': 'application/json'
}
for event in splunk_events:
try:
response = requests.post(
self.siem_config['splunk_hec_url'],
headers=headers,
json=event,
verify=False,
timeout=10
)
if response.status_code != 200:
print(f"Failed to send event to Splunk: {response.status_code}")
return False
except Exception as e:
print(f"Error sending to Splunk: {e}")
return False
print(f"Sent {len(splunk_events)} events to Splunk")
return True
def transform_for_elasticsearch(self, events):
"""Transform events for Elasticsearch"""
es_events = []
for event in events:
es_event = {
"@timestamp": event['timestamp'].isoformat(),
"source": {
"ip": event['src_ip'],
"port": event['src_port']
},
"destination": {
"ip": event['dst_ip'],
"port": event['dst_port']
},
"network": {
"protocol": self.get_protocol_name(event['ip_proto'])
},
"event": {
"id": f"{event['sid']}-{event['cid']}",
"category": "network",
"type": "alert",
"severity": self.get_severity(event['signature_id'])
},
"rule": {
"id": event['signature_id'],
"name": event['signature'],
"version": event['signature_rev']
},
"sguil": {
"sid": event['sid'],
"cid": event['cid'],
"signature_gen": event['signature_gen']
}
}
es_events.append(es_event)
return es_events
def send_to_elasticsearch(self, events):
"""Send events to Elasticsearch"""
if not events:
return True
es_events = self.transform_for_elasticsearch(events)
for event in es_events:
try:
index_name = f"squert-{datetime.now().strftime('%Y.%m.%d')}"
doc_id = event['event']['id']
response = requests.post(
f"{self.siem_config['elasticsearch_url']}/{index_name}/_doc/{doc_id}",
json=event,
timeout=10
)
if response.status_code not in [200, 201]:
print(f"Failed to send event to Elasticsearch: {response.status_code}")
return False
except Exception as e:
print(f"Error sending to Elasticsearch: {e}")
return False
print(f"Sent {len(es_events)} events to Elasticsearch")
return True
def get_protocol_name(self, proto_num):
"""Convert protocol number to name"""
protocols = {1: 'icmp', 6: 'tcp', 17: 'udp'}
return protocols.get(proto_num, str(proto_num))
def get_severity(self, signature_id):
"""Determine event severity based on signature ID"""
if signature_id in [1, 2, 3]:
return 'high'
elif signature_id in [4, 5, 6]:
return 'medium'
else:
return 'low'
def run_integration(self):
"""Run the SIEM integration"""
print(f"Starting SIEM integration at {datetime.now()}")
# Get recent events
events = self.get_recent_events(5) # Last 5 minutes
if not events:
print("No new events to process")
return
print(f"Processing {len(events)} events")
# Send to configured SIEM systems
if self.siem_config.get('splunk_enabled', False):
self.send_to_splunk(events)
if self.siem_config.get('elasticsearch_enabled', False):
self.send_to_elasticsearch(events)
print("SIEM integration completed")
# Configuration
config = {
'database': {
'host': 'localhost',
'user': 'squert',
'password': 'squertpassword',
'database': 'sguildb'
},
'siem': {
'splunk_enabled': True,
'splunk_hec_url': 'https://splunk.company.com:8088/services/collector/event',
'splunk_token': 'your-hec-token',
'splunk_index': 'security',
'elasticsearch_enabled': True,
'elasticsearch_url': 'http://elasticsearch.company.com:9200'
}
}
# Run integration
if __name__ == "__main__":
integration = SquertSIEMIntegration(config)
integration.run_integration()
Troubleshooting
Common Issues
Web Interface Not Loading:
bash
# Check Apache status
sudo systemctl status apache2
# Check Apache error logs
sudo tail -f /var/log/apache2/squert_error.log
# Check PHP errors
sudo tail -f /var/log/apache2/error.log | grep -i php
# Verify file permissions
sudo chown -R www-data:www-data /var/www/squert
sudo chmod -R 755 /var/www/squert
# Test PHP configuration
php -m | grep -i mysql
php -r "phpinfo();" | grep -i mysql
Database Connection Issues:
bash
# Test database connection
mysql -h localhost -u squert -p sguildb -e "SELECT COUNT(*) FROM event;"
# Check database permissions
mysql -u root -p -e "SHOW GRANTS FOR 'squert'@'localhost';"
# Verify database configuration
cat /var/www/squert/.scripts/squert.inc | grep -E "(dbhost|dbuser|dbpass|dbname)"
# Check MySQL service
sudo systemctl status mysql
sudo systemctl start mysql
Performance Issues:
bash
# Check system resources
top -p $(pgrep apache2)
free -h
df -h
# Optimize database
mysql -u squert -p sguildb -e "OPTIMIZE TABLE event;"
mysql -u squert -p sguildb -e "ANALYZE TABLE event;"
# Check slow queries
mysql -u root -p -e "SHOW PROCESSLIST;"
# Enable PHP OPcache
echo "opcache.enable=1" >> /etc/php/7.4/apache2/php.ini
sudo systemctl restart apache2
Performance Optimization
Optimizing Squert performance:
bash
# Apache optimization
cat >> /etc/apache2/conf-available/squert-performance.conf << 'EOF'
# Squert performance optimizations
# Enable compression
LoadModule deflate_module modules/mod_deflate.so
<Location "/squert">
SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \
\.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
</Location>
# Enable caching
LoadModule expires_module modules/mod_expires.so
<Location "/squert">
ExpiresActive On
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
</Location>
EOF
sudo a2enconf squert-performance
sudo systemctl restart apache2
# PHP optimization
cat >> /etc/php/7.4/apache2/conf.d/99-squert.ini << 'EOF'
; Squert PHP optimizations
memory_limit = 256M
max_execution_time = 60
max_input_vars = 3000
; OPcache settings
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 4000
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1
EOF
sudo systemctl restart apache2
# Database optimization
mysql -u root -p << 'EOF'
-- Optimize Sguil database for Squert
USE sguildb;
-- Add indexes for common Squert queries
CREATE INDEX idx_event_timestamp_src ON event (timestamp, src_ip);
CREATE INDEX idx_event_timestamp_dst ON event (timestamp, dst_ip);
CREATE INDEX idx_event_signature_timestamp ON event (signature_id, timestamp);
CREATE INDEX idx_event_src_dst ON event (src_ip, dst_ip);
-- Optimize table
OPTIMIZE TABLE event;
ANALYZE TABLE event;
EOF
Security Considerations
Access Control
Web Interface Security:
- Implement HTTPS for all Squert access
- Use strong authentication mechanisms
- Implement session timeout and management
- Regular security updates for all components
- Monitor access logs for suspicious activity
Database Security:
- Use dedicated database user with minimal privileges
- Implement database connection encryption
- Regular database security updates
- Monitor database access logs
- Implement backup encryption
Data Protection
Event Data Security:
- Encrypt sensitive event data at rest
- Implement data retention policies
- Secure access to packet capture data
- Regular cleanup of temporary files
- Implement access logging for event data
Operational Security:
- Regular security assessments of Squert infrastructure
- Monitor for unauthorized access attempts
- Implement proper backup and recovery procedures
- Regular updates of Squert and dependencies
- Incident response procedures for Squert compromise