Deploy ModSecurity 3 as an Apache module with OWASP Core Rule Set to protect web applications from common attacks. Configure real-time logging, custom rules, and automated threat detection for production environments.
Prerequisites
- Apache HTTP server
- Root or sudo access
- Basic knowledge of web server configuration
What this solves
ModSecurity 3 provides web application firewall (WAF) capabilities that protect your applications from SQL injection, cross-site scripting (XSS), and other OWASP Top 10 vulnerabilities. This tutorial sets up ModSecurity with Apache and deploys the OWASP Core Rule Set for comprehensive threat detection and blocking.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you have the latest security updates.
sudo apt update && sudo apt upgrade -y
Install Apache and development tools
Install Apache web server and the development tools required for building ModSecurity.
sudo apt install -y apache2 apache2-dev build-essential git libcurl4-openssl-dev libgeoip-dev libyajl-dev libpcre2-dev libxml2-dev pkgconf libmaxminddb-dev
Download and compile libmodsecurity
Clone the ModSecurity v3 library source code and compile it with optimizations for production use.
cd /opt
sudo git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity.git
cd ModSecurity
sudo git submodule init
sudo git submodule update
sudo ./build.sh
sudo ./configure --with-yajl --with-geoip --with-pcre2
sudo make -j$(nproc)
sudo make install
Download and compile ModSecurity Apache connector
Install the connector that integrates ModSecurity library with Apache HTTP server.
cd /opt
sudo git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-apache.git
cd ModSecurity-apache
sudo ./configure --with-libmodsecurity=/usr/local/modsecurity/
sudo make -j$(nproc)
sudo make install
Configure dynamic library loading
Update the library path so Apache can find the ModSecurity libraries at runtime.
echo '/usr/local/modsecurity/lib' | sudo tee /etc/ld.so.conf.d/modsecurity.conf
sudo ldconfig
Load ModSecurity Apache module
Enable the ModSecurity module in Apache configuration.
echo 'LoadModule security3_module /usr/lib/apache2/modules/mod_security3.so' | sudo tee /etc/apache2/mods-available/security3.load
sudo a2enmod security3
Create ModSecurity configuration directory
Set up the directory structure for ModSecurity configuration files with proper permissions.
sudo mkdir -p /etc/apache2/modsecurity.d
sudo chown root:root /etc/apache2/modsecurity.d
sudo chmod 755 /etc/apache2/modsecurity.d
Download OWASP Core Rule Set
Clone the latest OWASP Core Rule Set which contains rules for detecting common web application attacks.
cd /etc/apache2/modsecurity.d
sudo git clone https://github.com/coreruleset/coreruleset.git
sudo mv coreruleset/crs-setup.conf.example coreruleset/crs-setup.conf
Create main ModSecurity configuration
Configure ModSecurity with logging, rule processing, and security settings optimized for production.
# ModSecurity v3 Configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \n "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "^application/json" \n "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecRequestBodyInMemoryLimit 131072
SecRequestBodyLimitAction Reject
SecRule REQBODY_ERROR "!@eq 0" \n"id:200002, phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'Error %{REQBODY_ERROR_MSG}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \n"id:200003,phase:2,t:none,log,deny,status:400, \nmsg:'Multipart request body failed strict validation: \nPE %{REQBODY_PROCESSOR_ERROR}, \nBQ %{MULTIPART_BOUNDARY_QUOTED}, \nBW %{MULTIPART_BOUNDARY_WHITESPACE}, \nDB %{MULTIPART_DATA_BEFORE}, \nDA %{MULTIPART_DATA_AFTER}, \nHF %{MULTIPART_HEADER_FOLDING}, \nLF %{MULTIPART_LF_LINE}, \nSM %{MULTIPART_MISSING_SEMICOLON}, \nIQ %{MULTIPART_INVALID_QUOTING}, \nIP %{MULTIPART_INVALID_PART}, \nIH %{MULTIPART_INVALID_HEADER_FOLDING}, \nFL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \n"id:200004,phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml
SecResponseBodyLimit 524288
SecResponseBodyLimitAction ProcessPartial
SecTmpDir /tmp/
SecDataDir /tmp/
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABDEFHIJZ
SecAuditLogType Serial
SecAuditLog /var/log/apache2/modsec_audit.log
SecArgumentSeparator &
SecCookieFormat 0
SecUnicodeMapFile unicode.mapping 20127
SecStatusEngine On
Create custom rules file
Set up a custom rules file for application-specific security rules and exceptions.
# Custom ModSecurity Rules
Block requests with suspicious user agents
SecRule REQUEST_HEADERS:User-Agent "@contains sqlmap" \n "id:1001,phase:1,block,msg:'SQL injection tool detected',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}',severity:2,tag:'attack-sqli',tag:'OWASP_CRS'"
Rate limiting rule - max 100 requests per minute per IP
SecAction "id:1002,phase:1,initcol:ip=%{REMOTE_ADDR},pass,nolog"
SecRule IP:REQUEST_COUNT "@gt 100" \n "id:1003,phase:1,deny,status:429,msg:'Rate limit exceeded',logdata:'IP: %{REMOTE_ADDR}',setvar:ip.request_count=+1"
SecAction "id:1004,phase:5,pass,nolog,setvar:ip.request_count=+1,expirevar:ip.request_count=60"
Block known bad IPs (example - customize for your needs)
SecRule REMOTE_ADDR "@ipMatch 198.51.100.0/24" \n "id:1005,phase:1,deny,status:403,msg:'Blocked IP range',logdata:'IP: %{REMOTE_ADDR}'"
Enhanced logging for security events
SecRule TX:ANOMALY_SCORE "@gt 0" \n "id:1006,phase:5,pass,msg:'Inbound Anomaly Score Exceeded',logdata:'Total Score: %{TX.ANOMALY_SCORE}'"
Whitelist legitimate traffic patterns (customize as needed)
SecRule REQUEST_URI "@beginsWith /api/health" \n "id:1007,phase:1,pass,ctl:ruleRemoveById=920350,msg:'Allow health check endpoint'"
Block file upload to sensitive directories
SecRule FILES_NAMES "@rx \.(php|jsp|asp|aspx|sh|py|pl|rb)$" \n "id:1008,phase:2,deny,status:403,msg:'Executable file upload attempt',logdata:'File: %{MATCHED_VAR}'"
Geographic IP filtering (requires GeoIP database)
SecRule REMOTE_ADDR "@geoLookup" "id:1009,phase:1,pass,nolog,setvar:tx.country_code=%{GEO.COUNTRY_CODE}"
SecRule TX:COUNTRY_CODE "@in CN RU" "id:1010,phase:1,deny,status:403,msg:'Blocked country'"
Configure Apache virtual host with ModSecurity
Enable ModSecurity for your website by adding directives to Apache virtual host configuration.
<VirtualHost *:80>
ServerAdmin webmaster@example.com
DocumentRoot /var/www/html
ServerName example.com
# ModSecurity Configuration
SecRuleEngine On
modsecurity_rules_file /etc/apache2/modsecurity.d/modsecurity.conf
modsecurity_rules_file /etc/apache2/modsecurity.d/coreruleset/crs-setup.conf
modsecurity_rules_file /etc/apache2/modsecurity.d/coreruleset/rules/*.conf
modsecurity_rules_file /etc/apache2/modsecurity.d/custom-rules.conf
# Security headers
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'"
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Enable required Apache modules
Enable Apache modules needed for security headers and ModSecurity functionality.
sudo a2enmod headers
sudo a2enmod rewrite
Set up log rotation for ModSecurity logs
Configure logrotate to manage ModSecurity audit logs and prevent disk space issues.
/var/log/apache2/modsec_audit.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 640 www-data adm
postrotate
/bin/systemctl reload apache2 > /dev/null 2>&1 || true
endscript
}
Create ModSecurity monitoring script
Set up a monitoring script that checks for blocked attacks and sends alerts.
#!/bin/bash
ModSecurity Monitoring Script
LOG_FILE="/var/log/apache2/modsec_audit.log"
if [ -f "/var/log/httpd/modsec_audit.log" ]; then
LOG_FILE="/var/log/httpd/modsec_audit.log"
fi
ALERT_EMAIL="admin@example.com"
THRESHOLD=10
TIME_WINDOW=5
Get current timestamp
CURRENT_TIME=$(date +%s)
START_TIME=$((CURRENT_TIME - (TIME_WINDOW * 60)))
Count blocked requests in the last 5 minutes
BLOCKED_COUNT=$(awk -v start="$START_TIME" '
/^--[a-f0-9]+-A--/ {
getline;
date_line = $0;
if (match(date_line, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2})/, arr)) {
cmd = "date -d \"" arr[1] "\" +%s 2>/dev/null"
if ((cmd | getline timestamp) > 0) {
close(cmd)
if (timestamp >= start) {
while (getline line && line != "") {
if (line ~ /HTTP\/1\.[01]" [45][0-9][0-9]/) {
count++
break
}
}
}
}
}
}
END { print count+0 }
' "$LOG_FILE")
Send alert if threshold exceeded
if [ "$BLOCKED_COUNT" -gt "$THRESHOLD" ]; then
SUBJECT="ModSecurity Alert: High Attack Volume Detected"
MESSAGE="ModSecurity has blocked $BLOCKED_COUNT requests in the last $TIME_WINDOW minutes.\n\nRecent blocked IPs:\n"
# Get top attacking IPs
RECENT_IPS=$(awk -v start="$START_TIME" '
/^--[a-f0-9]+-B--/ {
getline; date_line = $0;
getline; http_line = $0;
if (match(http_line, /GET|POST|PUT|DELETE|HEAD|OPTIONS/) && match(date_line, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2})/, arr)) {
cmd = "date -d \"" arr[1] "\" +%s 2>/dev/null"
if ((cmd | getline timestamp) > 0) {
close(cmd)
if (timestamp >= start) {
getline; getline; getline;
if (match($0, /Host: ([^\r\n]+)/, host)) {
getline;
if (match($0, /X-Forwarded-For: ([^\r\n,]+)/, xff)) {
ip = xff[1]
} else {
# Extract IP from HTTP line
if (match(http_line, /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/, ip_match)) {
ip = ip_match[1]
}
}
if (ip != "") {
ips[ip]++
}
}
}
}
}
}
END {
for (ip in ips) {
print ip ": " ips[ip] " requests"
}
}
' "$LOG_FILE" | sort -k2 -nr | head -5)
echo -e "$MESSAGE\n$RECENT_IPS" | mail -s "$SUBJECT" "$ALERT_EMAIL" 2>/dev/null ||
logger -p local0.warning "ModSecurity Alert: $BLOCKED_COUNT attacks blocked in $TIME_WINDOW minutes"
fi
Log normal status
logger -p local0.info "ModSecurity monitor: $BLOCKED_COUNT requests blocked in last $TIME_WINDOW minutes"
sudo chmod 755 /usr/local/bin/modsec-monitor.sh
Set up automated monitoring with cron
Schedule the monitoring script to run every 5 minutes for real-time threat detection.
echo "/5 * /usr/local/bin/modsec-monitor.sh" | sudo crontab -
Configure SELinux policy for ModSecurity
On SELinux-enabled systems, configure policies to allow ModSecurity to function properly.
# Ubuntu/Debian typically don't have SELinux enabled by default
echo "SELinux configuration not required on Ubuntu/Debian"
Start and enable Apache
Start Apache with ModSecurity enabled and verify the configuration is valid.
sudo apache2ctl configtest
sudo systemctl enable apache2
sudo systemctl start apache2
Advanced threat detection and response
Configure anomaly scoring threshold
Tune the OWASP CRS anomaly scoring for your application's specific needs.
# Anomaly Scoring Configuration
Lower scores = more sensitive, higher false positive rate
Higher scores = less sensitive, lower false positive rate
SecAction "id:900110,phase:1,nolog,pass,t:none,setvar:tx.inbound_anomaly_score_threshold=5"
SecAction "id:900111,phase:1,nolog,pass,t:none,setvar:tx.outbound_anomaly_score_threshold=4"
Paranoia level (1-4, higher = more rules active)
SecAction "id:900000,phase:1,nolog,pass,t:none,setvar:tx.crs_setup_version=340,setvar:tx.paranoia_level=2"
Application-specific exclusions
Exclude specific rules for WordPress admin
SecRule REQUEST_URI "@beginsWith /wp-admin/" "id:1100,phase:1,pass,nolog,ctl:ruleRemoveById=920350,ctl:ruleRemoveById=921180"
SQL rule exclusions for specific parameters
SecRule ARGS_NAMES "@rx ^(search|query|filter)$" "id:1101,phase:1,pass,nolog,ctl:ruleRemoveById=942100,ctl:ruleRemoveById=942110"
XSS rule exclusions for rich text editors
SecRule ARGS_NAMES "@rx ^(content|description|body)$" "id:1102,phase:1,pass,nolog,ctl:ruleRemoveById=941100,ctl:ruleRemoveById=941160"
Set up real-time log monitoring with rsyslog
Configure centralized logging for ModSecurity events to enable real-time monitoring and alerting.
# ModSecurity logging configuration
Send ModSecurity events to remote syslog server
. @@203.0.113.100:514
Local logging with severity filtering
local0.info /var/log/modsecurity.log
local0.warning /var/log/modsecurity-alerts.log
local0.err /var/log/modsecurity-errors.log
Stop processing after ModSecurity rules
& stop
sudo systemctl restart rsyslog
Create IP blocking automation script
Implement automatic IP blocking for repeated attack attempts using iptables integration.
#!/bin/bash
Automatic IP blocking based on ModSecurity logs
LOG_FILE="/var/log/apache2/modsec_audit.log"
if [ -f "/var/log/httpd/modsec_audit.log" ]; then
LOG_FILE="/var/log/httpd/modsec_audit.log"
fi
BLOCK_THRESHOLD=5
TIME_WINDOW=10
BLOCK_DURATION=3600
Create iptables chain for ModSecurity blocks if it doesn't exist
sudo iptables -L MODSEC_BLOCK -n >/dev/null 2>&1 || {
sudo iptables -N MODSEC_BLOCK
sudo iptables -I INPUT -j MODSEC_BLOCK
}
Get timestamp for time window
CURRENT_TIME=$(date +%s)
START_TIME=$((CURRENT_TIME - (TIME_WINDOW * 60)))
Extract attacking IPs from recent logs
awk -v start="$START_TIME" -v threshold="$BLOCK_THRESHOLD" '
BEGIN { }
/^--[a-f0-9]+-A--/ {
getline; date_line = $0;
if (match(date_line, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2})/, arr)) {
cmd = "date -d \"" arr[1] "\" +%s 2>/dev/null"
if ((cmd | getline timestamp) > 0) {
close(cmd)
if (timestamp >= start) {
# Look for blocked requests (4xx/5xx responses)
while (getline line && line != "") {
if (line ~ /HTTP\/1\.[01]" [45][0-9][0-9]/) {
# Extract IP from earlier context
getline ip_line;
if (match(ip_line, /([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/, ip_match)) {
ips[ip_match[1]]++
}
break
}
}
}
}
}
}
END {
for (ip in ips) {
if (ips[ip] >= threshold && ip != "127.0.0.1" && ip != "::1") {
print ip " " ips[ip]
}
}
}
' "$LOG_FILE" | while read ip count; do
# Check if IP is already blocked
if ! sudo iptables -C MODSEC_BLOCK -s "$ip" -j DROP 2>/dev/null; then
sudo iptables -I MODSEC_BLOCK -s "$ip" -j DROP
logger -p local0.warning "ModSecurity: Automatically blocked IP $ip after $count attacks"
# Schedule unblock
echo "sudo iptables -D MODSEC_BLOCK -s $ip -j DROP 2>/dev/null || true" | at now + "$BLOCK_DURATION" seconds 2>/dev/null
fi
done
Clean up old blocks (backup mechanism)
echo "sudo iptables -F MODSEC_BLOCK" | at now + 24 hours 2>/dev/null
sudo chmod 755 /usr/local/bin/modsec-autoblock.sh
echo "/2 * /usr/local/bin/modsec-autoblock.sh" | sudo crontab -
Configure real-time logging and monitoring
For comprehensive security monitoring, consider integrating ModSecurity with centralized logging systems. You can set up centralized log aggregation with Elasticsearch, Logstash, and Kibana for advanced log analysis and visualization.
Additionally, implement Kibana alerting and monitoring with Elasticsearch Watcher for automated threat detection and response workflows.
Verify your setup
# Check Apache status and ModSecurity module
sudo systemctl status apache2 # or httpd on RHEL
sudo apache2ctl -M | grep security # or httpd -M
Test basic WAF functionality
curl -I "http://example.com/?id=1%20UNION%20SELECT%20*%20FROM%20users"
Check ModSecurity logs
tail -f /var/log/apache2/modsec_audit.log # or /var/log/httpd/
Verify rule loading
sudo grep -r "SecRuleEngine On" /etc/apache2/modsecurity.d/ # or /etc/httpd/
Test custom rules
curl -H "User-Agent: sqlmap/1.0" "http://example.com/"
Check blocked request count
grep -c "HTTP/1.1" 403" /var/log/apache2/access.log
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Apache won't start after ModSecurity installation | Missing libmodsecurity library or incorrect path | sudo ldconfig && sudo systemctl start apache2 |
| ModSecurity rules not loading | Syntax error in configuration files | Check with sudo apache2ctl configtest and fix syntax errors |
| Legitimate requests being blocked | Anomaly score threshold too low | Increase thresholds in crs-setup.conf or add rule exclusions |
| No entries in audit log | SecAuditEngine not configured properly | Set SecAuditEngine RelevantOnly in modsecurity.conf |
| High false positive rate | Paranoia level too high or missing exclusions | Lower paranoia level or add application-specific rule exclusions |
| Performance degradation | Too many rules enabled or inefficient regex | Optimize rules, disable unused rule categories, increase processing limits |
| Log files growing too large | Missing log rotation configuration | Configure logrotate and set SecResponseBodyLimit |
Next steps
- Configure Apache rate limiting and DDoS protection for additional protection layers
- Configure Apache HTTP/2 with performance optimization to enhance web server performance
- Implement HAProxy rate limiting and DDoS protection for load balancer-level security
- Setup Elasticsearch SSL encryption and security hardening for secure log storage
- Configure ModSecurity geo-IP blocking and threat intelligence integration
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# ModSecurity 3 + OWASP Core Rule Set Installation Script
# Supports Ubuntu/Debian and RHEL-based distributions
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
# Global variables
PKG_MGR=""
PKG_INSTALL=""
APACHE_SERVICE=""
APACHE_CONFIG_DIR=""
APACHE_MODULES_DIR=""
MODSEC_CONFIG_DIR=""
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
cleanup_on_error() {
print_error "Installation failed. Cleaning up..."
systemctl stop "$APACHE_SERVICE" 2>/dev/null || true
rm -rf /opt/ModSecurity /opt/ModSecurity-apache 2>/dev/null || true
exit 1
}
trap cleanup_on_error ERR
check_prerequisites() {
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root or with sudo"
exit 1
fi
if ! command -v git >/dev/null 2>&1; then
print_error "Git is required but not installed"
exit 1
fi
}
detect_distro() {
if [[ ! -f /etc/os-release ]]; then
print_error "Cannot detect distribution - /etc/os-release not found"
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
APACHE_SERVICE="apache2"
APACHE_CONFIG_DIR="/etc/apache2"
APACHE_MODULES_DIR="/usr/lib/apache2/modules"
MODSEC_CONFIG_DIR="/etc/apache2/modsecurity.d"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
APACHE_SERVICE="httpd"
APACHE_CONFIG_DIR="/etc/httpd"
APACHE_MODULES_DIR="/etc/httpd/modules"
MODSEC_CONFIG_DIR="/etc/httpd/modsecurity.d"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
APACHE_SERVICE="httpd"
APACHE_CONFIG_DIR="/etc/httpd"
APACHE_MODULES_DIR="/etc/httpd/modules"
MODSEC_CONFIG_DIR="/etc/httpd/modsecurity.d"
;;
*)
print_error "Unsupported distribution: $ID"
exit 1
;;
esac
}
update_system() {
print_status "[1/10] Updating system packages..."
case "$PKG_MGR" in
apt)
apt update && apt upgrade -y
;;
dnf|yum)
$PKG_MGR update -y
;;
esac
}
install_dependencies() {
print_status "[2/10] Installing Apache and development tools..."
case "$PKG_MGR" in
apt)
$PKG_INSTALL apache2 apache2-dev build-essential git libcurl4-openssl-dev \
libgeoip-dev libyajl-dev libpcre2-dev libxml2-dev pkgconf libmaxminddb-dev
;;
dnf|yum)
$PKG_INSTALL httpd httpd-devel gcc gcc-c++ git libcurl-devel GeoIP-devel \
yajl-devel pcre2-devel libxml2-devel pkgconfig libmaxminddb-devel
;;
esac
}
compile_modsecurity() {
print_status "[3/10] Downloading and compiling ModSecurity v3..."
cd /opt
if [[ -d ModSecurity ]]; then
rm -rf ModSecurity
fi
git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity.git
cd ModSecurity
git submodule init
git submodule update
./build.sh
./configure --with-yajl --with-geoip --with-pcre2
make -j"$(nproc)"
make install
}
compile_apache_connector() {
print_status "[4/10] Downloading and compiling ModSecurity Apache connector..."
cd /opt
if [[ -d ModSecurity-apache ]]; then
rm -rf ModSecurity-apache
fi
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-apache.git
cd ModSecurity-apache
./configure --with-libmodsecurity=/usr/local/modsecurity/
make -j"$(nproc)"
make install
}
configure_library_path() {
print_status "[5/10] Configuring dynamic library loading..."
echo '/usr/local/modsecurity/lib' > /etc/ld.so.conf.d/modsecurity.conf
ldconfig
}
enable_modsecurity_module() {
print_status "[6/10] Enabling ModSecurity Apache module..."
case "$PKG_MGR" in
apt)
echo 'LoadModule security3_module /usr/lib/apache2/modules/mod_security3.so' > "$APACHE_CONFIG_DIR/mods-available/security3.load"
a2enmod security3
;;
dnf|yum)
echo 'LoadModule security3_module modules/mod_security3.so' > "$APACHE_CONFIG_DIR/conf.modules.d/00-security3.conf"
;;
esac
}
create_modsecurity_dirs() {
print_status "[7/10] Creating ModSecurity configuration directory..."
mkdir -p "$MODSEC_CONFIG_DIR"
chown root:root "$MODSEC_CONFIG_DIR"
chmod 755 "$MODSEC_CONFIG_DIR"
}
install_owasp_rules() {
print_status "[8/10] Installing OWASP Core Rule Set..."
cd "$MODSEC_CONFIG_DIR"
if [[ -d coreruleset ]]; then
rm -rf coreruleset
fi
git clone https://github.com/coreruleset/coreruleset.git
cp coreruleset/crs-setup.conf.example coreruleset/crs-setup.conf
chown -R root:root coreruleset
chmod -R 644 coreruleset/*
find coreruleset -type d -exec chmod 755 {} \;
}
create_modsecurity_config() {
print_status "[9/10] Creating ModSecurity configuration..."
cat > "$MODSEC_CONFIG_DIR/modsecurity.conf" << 'EOF'
# ModSecurity v3 Configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
SecRequestBodyLimitAction Reject
SecRule REQBODY_ERROR "!@eq 0" \
"id:200002, phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'Error %{REQBODY_ERROR_MSG}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:200003,phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: \
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
"id:200004,phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000
SecResponseBodyAccess Off
SecDebugLog /var/log/apache2/modsec_debug.log
SecDebugLogLevel 0
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABDEFHIJZ
SecAuditLogType Serial
SecAuditLog /var/log/apache2/modsec_audit.log
SecArgumentSeparator &
SecCookieFormat 0
SecTmpDir /tmp/
SecDataDir /tmp/
Include ${MODSEC_CONFIG_DIR}/coreruleset/crs-setup.conf
Include ${MODSEC_CONFIG_DIR}/coreruleset/rules/*.conf
EOF
if [[ "$APACHE_SERVICE" == "httpd" ]]; then
sed -i 's|/var/log/apache2/|/var/log/httpd/|g' "$MODSEC_CONFIG_DIR/modsecurity.conf"
fi
chown root:root "$MODSEC_CONFIG_DIR/modsecurity.conf"
chmod 644 "$MODSEC_CONFIG_DIR/modsecurity.conf"
}
configure_apache_vhost() {
print_status "[10/10] Configuring Apache virtual host..."
if [[ "$PKG_MGR" == "apt" ]]; then
VHOST_DIR="$APACHE_CONFIG_DIR/sites-available"
VHOST_FILE="$VHOST_DIR/000-default.conf"
else
VHOST_DIR="$APACHE_CONFIG_DIR/conf.d"
VHOST_FILE="$VHOST_DIR/modsecurity.conf"
fi
cat > "$VHOST_FILE" << EOF
<VirtualHost *:80>
DocumentRoot /var/www/html
ModSecurityEnabled on
ModSecurityConfig $MODSEC_CONFIG_DIR/modsecurity.conf
LogLevel warn
ErrorLog \${APACHE_LOG_DIR}/error.log
CustomLog \${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
if [[ "$PKG_MGR" == "apt" ]]; then
a2ensite 000-default
fi
chown root:root "$VHOST_FILE"
chmod 644 "$VHOST_FILE"
}
verify_installation() {
print_status "Verifying installation..."
systemctl enable "$APACHE_SERVICE"
systemctl restart "$APACHE_SERVICE"
if systemctl is-active --quiet "$APACHE_SERVICE"; then
print_status "Apache is running successfully"
else
print_error "Apache failed to start. Check logs: journalctl -u $APACHE_SERVICE"
exit 1
fi
if apache2ctl -M 2>/dev/null | grep -q security3 || httpd -M 2>/dev/null | grep -q security3; then
print_status "ModSecurity module loaded successfully"
else
print_warning "ModSecurity module may not be loaded properly"
fi
}
main() {
print_status "Starting ModSecurity 3 + OWASP Core Rule Set installation"
check_prerequisites
detect_distro
print_status "Detected distribution: $ID"
print_status "Package manager: $PKG_MGR"
update_system
install_dependencies
compile_modsecurity
compile_apache_connector
configure_library_path
enable_modsecurity_module
create_modsecurity_dirs
install_owasp_rules
create_modsecurity_config
configure_apache_vhost
verify_installation
print_status "Installation completed successfully!"
print_status "ModSecurity is now protecting your web applications"
print_status "Check logs at: /var/log/apache2/modsec_audit.log (Debian) or /var/log/httpd/modsec_audit.log (RHEL)"
}
main "$@"
Review the script before running. Execute with: bash install.sh