189 lines
6.4 KiB
Python
189 lines
6.4 KiB
Python
|
|
"""
|
||
|
|
Command-builder module for managing ModSecurity WAF with OWASP CRS on Nginx.
|
||
|
|
Each function returns a bash command string for execution on a Linux VPS.
|
||
|
|
"""
|
||
|
|
|
||
|
|
MODSEC_CONF = "/etc/nginx/modsec/modsecurity.conf"
|
||
|
|
CRS_SETUP_CONF = "/etc/nginx/modsec/owasp-crs/crs-setup.conf"
|
||
|
|
CRS_RULES_DIR = "/etc/nginx/modsec/owasp-crs/rules"
|
||
|
|
MAIN_CONF = "/etc/nginx/modsec/main.conf"
|
||
|
|
EXCLUSIONS_CONF = "/etc/nginx/modsec/exclusions.conf"
|
||
|
|
AUDIT_LOG = "/var/log/modsec_audit.log"
|
||
|
|
DEBUG_LOG = "/var/log/modsecurity/modsec_debug.log"
|
||
|
|
CRS_DIR = "/etc/nginx/modsec/owasp-crs"
|
||
|
|
|
||
|
|
|
||
|
|
def status_cmd():
|
||
|
|
"""Check if modsecurity module is loaded in nginx, rules active, and SecRuleEngine setting."""
|
||
|
|
return (
|
||
|
|
"echo '=== Nginx ModSecurity Module ===' && "
|
||
|
|
"nginx -V 2>&1 | grep -i modsecurity && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== SecRuleEngine Setting ===' && "
|
||
|
|
f"grep -i 'SecRuleEngine' {MODSEC_CONF} 2>/dev/null || echo 'modsecurity.conf not found' && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== Active Rules ===' && "
|
||
|
|
f"ls {CRS_RULES_DIR}/*.conf 2>/dev/null | wc -l && "
|
||
|
|
"echo 'rule files loaded' && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== Nginx Config Test ===' && "
|
||
|
|
"nginx -t 2>&1"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def install_cmd():
|
||
|
|
"""Install libmodsecurity3, nginx module, download OWASP CRS, configure."""
|
||
|
|
return (
|
||
|
|
"apt-get update && "
|
||
|
|
"apt-get install -y libmodsecurity3 libmodsecurity-dev libnginx-mod-http-modsecurity && "
|
||
|
|
"mkdir -p /etc/nginx/modsec && "
|
||
|
|
f"cd /etc/nginx/modsec && "
|
||
|
|
"if [ ! -d owasp-crs ]; then "
|
||
|
|
"git clone https://github.com/coreruleset/coreruleset.git owasp-crs; "
|
||
|
|
"fi && "
|
||
|
|
f"cp {CRS_DIR}/crs-setup.conf.example {CRS_SETUP_CONF} && "
|
||
|
|
"cp /etc/modsecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf && "
|
||
|
|
f"sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' {MODSEC_CONF} && "
|
||
|
|
f"touch {EXCLUSIONS_CONF} && "
|
||
|
|
f"cat > {MAIN_CONF} << 'MAINEOF'\n"
|
||
|
|
f"Include {MODSEC_CONF}\n"
|
||
|
|
f"Include {CRS_SETUP_CONF}\n"
|
||
|
|
f"Include {CRS_RULES_DIR}/*.conf\n"
|
||
|
|
f"Include {EXCLUSIONS_CONF}\n"
|
||
|
|
"MAINEOF\n"
|
||
|
|
"&& nginx -t && systemctl reload nginx"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def enable_cmd():
|
||
|
|
"""Set SecRuleEngine On in modsecurity.conf."""
|
||
|
|
return (
|
||
|
|
f"sed -i 's/^SecRuleEngine .*/SecRuleEngine On/' {MODSEC_CONF} && "
|
||
|
|
f"grep 'SecRuleEngine' {MODSEC_CONF} && "
|
||
|
|
"nginx -t && systemctl reload nginx"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def disable_cmd():
|
||
|
|
"""Set SecRuleEngine to DetectionOnly (log only, don't block)."""
|
||
|
|
return (
|
||
|
|
f"sed -i 's/^SecRuleEngine .*/SecRuleEngine DetectionOnly/' {MODSEC_CONF} && "
|
||
|
|
f"grep 'SecRuleEngine' {MODSEC_CONF} && "
|
||
|
|
"nginx -t && systemctl reload nginx"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def audit_log_cmd(lines=50):
|
||
|
|
"""Tail the modsecurity audit log."""
|
||
|
|
return f"tail -n {int(lines)} {AUDIT_LOG}"
|
||
|
|
|
||
|
|
|
||
|
|
def debug_log_cmd(lines=50):
|
||
|
|
"""Tail the modsecurity debug log."""
|
||
|
|
return f"tail -n {int(lines)} {DEBUG_LOG}"
|
||
|
|
|
||
|
|
|
||
|
|
def rules_list_cmd():
|
||
|
|
"""List rule files in the OWASP CRS rules directory."""
|
||
|
|
return f"ls -la {CRS_RULES_DIR}/*.conf 2>/dev/null"
|
||
|
|
|
||
|
|
|
||
|
|
def rule_disable_cmd(rule_id):
|
||
|
|
"""Add SecRuleRemoveById to the custom exclusion conf."""
|
||
|
|
rid = str(int(rule_id))
|
||
|
|
return (
|
||
|
|
f"if grep -q 'SecRuleRemoveById {rid}' {EXCLUSIONS_CONF} 2>/dev/null; then "
|
||
|
|
f"echo 'Rule {rid} is already disabled'; "
|
||
|
|
"else "
|
||
|
|
f"echo 'SecRuleRemoveById {rid}' >> {EXCLUSIONS_CONF} && "
|
||
|
|
f"echo 'Disabled rule {rid}' && "
|
||
|
|
"nginx -t && systemctl reload nginx; "
|
||
|
|
"fi"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def rule_enable_cmd(rule_id):
|
||
|
|
"""Remove a rule ID from the exclusion conf (re-enable it)."""
|
||
|
|
rid = str(int(rule_id))
|
||
|
|
return (
|
||
|
|
f"if grep -q 'SecRuleRemoveById {rid}' {EXCLUSIONS_CONF} 2>/dev/null; then "
|
||
|
|
f"sed -i '/SecRuleRemoveById {rid}/d' {EXCLUSIONS_CONF} && "
|
||
|
|
f"echo 'Re-enabled rule {rid}' && "
|
||
|
|
"nginx -t && systemctl reload nginx; "
|
||
|
|
"else "
|
||
|
|
f"echo 'Rule {rid} is not in exclusions'; "
|
||
|
|
"fi"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def exclusions_cmd():
|
||
|
|
"""Show current rule exclusions."""
|
||
|
|
return (
|
||
|
|
f"echo '=== Rule Exclusions ({EXCLUSIONS_CONF}) ===' && "
|
||
|
|
f"cat {EXCLUSIONS_CONF} 2>/dev/null || echo 'No exclusions file found'"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def crs_update_cmd():
|
||
|
|
"""Git pull in the OWASP CRS directory to update rules."""
|
||
|
|
return (
|
||
|
|
f"cd {CRS_DIR} && "
|
||
|
|
"git pull && "
|
||
|
|
"echo '' && echo '=== Updated CRS Rules ===' && "
|
||
|
|
"nginx -t && systemctl reload nginx"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def config_cmd():
|
||
|
|
"""Show modsecurity.conf."""
|
||
|
|
return f"cat {MODSEC_CONF}"
|
||
|
|
|
||
|
|
|
||
|
|
def config_crs_cmd():
|
||
|
|
"""Show crs-setup.conf."""
|
||
|
|
return f"cat {CRS_SETUP_CONF}"
|
||
|
|
|
||
|
|
|
||
|
|
def test_cmd():
|
||
|
|
"""Curl localhost with test XSS and SQLi payloads to verify WAF is working."""
|
||
|
|
return (
|
||
|
|
"echo '=== XSS Test ===' && "
|
||
|
|
"curl -s -o /dev/null -w 'HTTP %{http_code}' "
|
||
|
|
"'http://localhost/?q=<script>alert(1)</script>' && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== SQLi Test ===' && "
|
||
|
|
"curl -s -o /dev/null -w 'HTTP %{http_code}' "
|
||
|
|
"\"http://localhost/?id=1' OR '1'='1\" && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== Path Traversal Test ===' && "
|
||
|
|
"curl -s -o /dev/null -w 'HTTP %{http_code}' "
|
||
|
|
"'http://localhost/../../etc/passwd' && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '(403 = blocked by WAF, 200 = WAF not blocking)'"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def nginx_status_cmd():
|
||
|
|
"""Check which nginx server blocks have modsecurity enabled."""
|
||
|
|
return (
|
||
|
|
"echo '=== Server Blocks with ModSecurity ===' && "
|
||
|
|
"grep -rn 'modsecurity' /etc/nginx/sites-enabled/ /etc/nginx/conf.d/ 2>/dev/null || "
|
||
|
|
"echo 'No modsecurity directives found in server blocks' && "
|
||
|
|
"echo '' && "
|
||
|
|
"echo '=== Nginx Module Status ===' && "
|
||
|
|
"nginx -V 2>&1 | grep -io 'modsecurity[^ ]*' || echo 'ModSecurity module not found'"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def uninstall_cmd():
|
||
|
|
"""Remove modsec configs and apt remove packages."""
|
||
|
|
return (
|
||
|
|
"echo 'Removing ModSecurity configuration...' && "
|
||
|
|
"rm -rf /etc/nginx/modsec && "
|
||
|
|
"apt-get remove -y libmodsecurity3 libmodsecurity-dev libnginx-mod-http-modsecurity && "
|
||
|
|
"apt-get autoremove -y && "
|
||
|
|
"echo 'Removed modsec configs and packages' && "
|
||
|
|
"echo 'NOTE: Check nginx server blocks and remove modsecurity directives manually' && "
|
||
|
|
"nginx -t && systemctl reload nginx"
|
||
|
|
)
|