From 11c6f69c3531535fe25838385298771f883eae70 Mon Sep 17 00:00:00 2001 From: Joby Elliott Date: Wed, 23 Oct 2024 17:02:32 -0600 Subject: [PATCH] large fail2ban and other security improvements --- install/01-nginx-default-root.sh | 8 ++++ install/mysql.sh | 21 ++++----- install/nginx-cloudflare-fail2ban.sh | 55 +++++++++++++++++++++++ install/nginx-conf/10-fail2ban-check.conf | 9 ++++ site-config.conf | 6 ++- 5 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 install/nginx-cloudflare-fail2ban.sh create mode 100644 install/nginx-conf/10-fail2ban-check.conf diff --git a/install/01-nginx-default-root.sh b/install/01-nginx-default-root.sh index 66dd64c..e4b9776 100755 --- a/install/01-nginx-default-root.sh +++ b/install/01-nginx-default-root.sh @@ -39,6 +39,14 @@ server { server_name _; + # Apply general rate limit + limit_req zone=general burst=100 nodelay; + + # Check for banned IPs + if (\$is_banned) { + return 403 "Forbidden"; + } + location / { try_files \$uri \$uri/ =404; } diff --git a/install/mysql.sh b/install/mysql.sh index 24dd472..6ee9819 100755 --- a/install/mysql.sh +++ b/install/mysql.sh @@ -43,14 +43,15 @@ EOL systemctl restart mysql # Create MySQL fail2ban configuration -cat > /etc/fail2ban/jail.d/mysql.conf << EOL -[mysqld-auth] -enabled = true -filter = mysqld-auth -port = 3306 -logpath = /var/log/mysql/error.log -maxretry = 5 -bantime = 3600 +tee /etc/fail2ban/jail.d/mysql.conf << 'EOL' +[mysql] +enabled = true +filter = mysql +port = 3306 +logpath = /var/log/mysql/error.log +maxretry = 10 +findtime = 600 +bantime = 3600 EOL # Ensure fail2ban can read the MySQL log @@ -58,9 +59,9 @@ EOL # usermod -a -G adm fail2ban # Create MySQL auth filter for fail2ban -cat > /etc/fail2ban/filter.d/mysqld-auth.conf << EOL +tee /etc/fail2ban/filter.d/mysql.conf << 'EOL' [Definition] -failregex = ^%(__prefix_line)s[0-9]+ \[Warning\] Access denied for user '\w+'@'' +failregex = ^\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z\s+\d+\s+\[Warning\].*IP address ''.*$ ignoreregex = EOL diff --git a/install/nginx-cloudflare-fail2ban.sh b/install/nginx-cloudflare-fail2ban.sh new file mode 100644 index 0000000..e1bef05 --- /dev/null +++ b/install/nginx-cloudflare-fail2ban.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Check if script is run as root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root or with sudo" + exit 1 +fi + +# Install requirements +echo "Installing required packages..." +apt-get install -y nginx-extras + +# Create the fail2ban check script +echo "Creating fail2ban check script..." +tee /usr/local/bin/check_fail2ban.sh << 'SCRIPT' +#!/bin/bash +IP="$1" +# Get list of all active jails +JAILS=$(fail2ban-client status | grep "Jail list:" | sed "s/^[^:]*:[ \t]*//" | sed "s/,//g") + +# Check each jail for the IP +for JAIL in $JAILS; do + if fail2ban-client status "$JAIL" | grep -q "IP list:\s*.*$IP"; then + exit 0 # IP is banned in at least one jail + fi +done +exit 1 # IP is not banned in any jail +SCRIPT +chmod +x /usr/local/bin/check_fail2ban.sh +chown www-data:www-data /usr/local/bin/check_fail2ban.sh + +# Create the JavaScript module for NGINX +echo "Creating NGINX JavaScript module..." +mkdir -p /etc/nginx/modules-available/ +tee /etc/nginx/modules-available/check_ban.js << 'JSMODULE' +function checkBan(r) { + var ip = r.variables.http_cf_connecting_ip; + var s = require('process').spawnSync('/usr/local/bin/check_fail2ban.sh', [ip]); + return s.status === 0 ? '1' : '0'; +} + +export default {checkBan}; +JSMODULE + +# Test NGINX configuration +echo "Testing NGINX configuration..." +nginx -t + +# Restart services +echo "Restarting services..." +systemctl restart fail2ban +systemctl restart nginx + +echo "Installation complete!" +echo "Please check /var/log/nginx/error.log for any issues." diff --git a/install/nginx-conf/10-fail2ban-check.conf b/install/nginx-conf/10-fail2ban-check.conf new file mode 100644 index 0000000..f8ad430 --- /dev/null +++ b/install/nginx-conf/10-fail2ban-check.conf @@ -0,0 +1,9 @@ +load_module modules/ngx_http_js_module.so; + +js_import /etc/nginx/modules-available/check_ban.js; +js_set $exec_check_ban check_ban.checkBan; + +map $http_cf_connecting_ip $is_banned { + default 0; + "~.*" "${exec_check_ban $http_cf_connecting_ip}"; +} \ No newline at end of file diff --git a/site-config.conf b/site-config.conf index dbe9ce2..d5cf074 100644 --- a/site-config.conf +++ b/site-config.conf @@ -17,13 +17,17 @@ server { ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; include snippets/ssl.conf; + # Check for banned IPs + if ($is_banned) { + return 403 "Forbidden"; + } + # Apply general rate limit limit_req zone=general burst=100 nodelay; # Content Security Policy (needs to be per-domain) add_header Content-Security-Policy "default-src 'self' *.$DOMAIN; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.$DOMAIN; style-src 'self' 'unsafe-inline' *.$DOMAIN; img-src 'self' data: *.$DOMAIN; font-src 'self' data: *.$DOMAIN; connect-src 'self' *.$DOMAIN; frame-src 'self' *.$DOMAIN; media-src 'self' *.$DOMAIN; object-src 'none'; base-uri 'self'; form-action 'self' *.$DOMAIN" always; - # Subdomain handling set $subdomain ''; set $full_root "/var/www/$DOMAIN/_main/www";