Implement Apache web application firewall with ModSecurity 3 and OWASP Core Rule Set

Intermediate 45 min Apr 08, 2026 124 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

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
sudo dnf update -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
sudo dnf install -y httpd httpd-devel gcc gcc-c++ git libcurl-devel GeoIP-devel yajl-devel pcre2-devel libxml2-devel pkgconfig libmaxminddb-devel

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
echo 'LoadModule security3_module modules/mod_security3.so' | sudo tee /etc/httpd/conf.modules.d/00-security3.conf

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
sudo mkdir -p /etc/httpd/modsecurity.d
sudo chown root:root /etc/httpd/modsecurity.d
sudo chmod 755 /etc/httpd/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
cd /etc/httpd/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
# 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/httpd/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'"

# 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>
<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    DocumentRoot /var/www/html
    ServerName example.com

    # ModSecurity Configuration
    SecRuleEngine On
    modsecurity_rules_file /etc/httpd/modsecurity.d/modsecurity.conf
    modsecurity_rules_file /etc/httpd/modsecurity.d/coreruleset/crs-setup.conf
    modsecurity_rules_file /etc/httpd/modsecurity.d/coreruleset/rules/*.conf
    modsecurity_rules_file /etc/httpd/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 /var/log/httpd/error.log
    CustomLog /var/log/httpd/access.log combined
</VirtualHost>

Enable required Apache modules

Enable Apache modules needed for security headers and ModSecurity functionality.

sudo a2enmod headers
sudo a2enmod rewrite
echo 'LoadModule headers_module modules/mod_headers.so' | sudo tee -a /etc/httpd/conf.modules.d/00-base.conf
echo 'LoadModule rewrite_module modules/mod_rewrite.so' | sudo tee -a /etc/httpd/conf.modules.d/00-base.conf

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
}
/var/log/httpd/modsec_audit.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 640 apache apache
    postrotate
        /bin/systemctl reload httpd > /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"
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_setrlimit 1
sudo semanage fcontext -a -t httpd_exec_t "/usr/local/modsecurity/lib/.*"
sudo restorecon -Rv /usr/local/modsecurity/lib/

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
sudo httpd -t
sudo systemctl enable httpd
sudo systemctl start httpd

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"
# 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 -
Warning: Test the IP blocking script thoroughly before deploying to production. Ensure you have alternative access methods in case you accidentally block your own IP address.

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

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.