From 7946acf381da0db4d9e5dfe13033731608dcdbf6 Mon Sep 17 00:00:00 2001 From: Mitchell Krog UB1 Date: Tue, 7 Feb 2017 16:39:58 +0200 Subject: [PATCH] Added wp-login filter and jail for Fail2Ban --- .travis.yml | 8 ++ Fail2Ban/README.md | 9 ++ Fail2Ban/filter.d/nginx-limit-req.conf | 44 +++++++++ Fail2Ban/filter.d/nginx-limit-req.local | 16 ++++ Fail2Ban/jail.local | 11 ++- nginx.conf | 114 ++++++++++++++++++++++++ test-nginx.sh | 34 +++++++ 7 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 .travis.yml create mode 100644 Fail2Ban/filter.d/nginx-limit-req.conf create mode 100644 Fail2Ban/filter.d/nginx-limit-req.local create mode 100644 nginx.conf create mode 100755 test-nginx.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..b6ac1e2ee --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: python +cache: +- directories: + - nginx-cache +rvm: +- 2.1 +script: ./test-nginx-config.sh +sudo: false \ No newline at end of file diff --git a/Fail2Ban/README.md b/Fail2Ban/README.md index f27cf11d8..60c0522b6 100644 --- a/Fail2Ban/README.md +++ b/Fail2Ban/README.md @@ -1,4 +1,5 @@ # Fail2Ban Blacklist for Repeat Offenders of Nginx (action.d) +#### also includes wp-login attack filter using [nginx-limit-req] - see lower down ### Author: Mitchell Krog ### Version: 1.1 @@ -50,4 +51,12 @@ findtime = 604800 ; 1 week maxretry = 20 ``` +# Blocking wp-login.php attacks on Wordpress Sites +See the included /filter.d/nginx-limit-req.conf jail and filter for detecting and blocking wp-login attacks +The original /filter.d/nginx-limit-req.conf file from Fail2Ban is included including a /filter.d/nginx-limit-req.local file with the settings you need to detect and block wp-logins. +An example of the jail settings for your jail.local file is included in the jail.local sample. +This works great at picking up wp-login attacks. +Be sure to add the rate limiting zone to your nginx.conf as per instructions in /filter.d/nginx-limit-req.local + + ### If this helps you why not [buy me a beer](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=XP2AZ4S5HNAWQ):beer: \ No newline at end of file diff --git a/Fail2Ban/filter.d/nginx-limit-req.conf b/Fail2Ban/filter.d/nginx-limit-req.conf new file mode 100644 index 000000000..f16ecee96 --- /dev/null +++ b/Fail2Ban/filter.d/nginx-limit-req.conf @@ -0,0 +1,44 @@ +# Fail2ban filter configuration for nginx :: limit_req +# used to ban hosts, that were failed through nginx by limit request processing rate +# +# Author: Serg G. Brester (sebres) +# +# To use 'nginx-limit-req' filter you should have `ngx_http_limit_req_module` +# and define `limit_req` and `limit_req_zone` as described in nginx documentation +# http://nginx.org/en/docs/http/ngx_http_limit_req_module.html +# +# Example: +# +# http { +# ... +# limit_req_zone $binary_remote_addr zone=lr_zone:10m rate=1r/s; +# ... +# # http, server, or location: +# location ... { +# limit_req zone=lr_zone burst=1 nodelay; +# ... +# } +# ... +# } +# ... +# + +[Definition] + +# Specify following expression to define exact zones, if you want to ban IPs limited +# from specified zones only. +# Example: +# +# ngx_limit_req_zones = lr_zone|lr_zone2 +# +ngx_limit_req_zones = [^"]+ + +# Use following full expression if you should range limit request to specified +# servers, requests, referrers etc. only : +# +# failregex = ^\s*\[error\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: , server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(, referrer: "\S+")?\s*$ + +# Shortly, much faster and stable version of regexp: +failregex = ^\s*\[error\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: + +ignoreregex = \ No newline at end of file diff --git a/Fail2Ban/filter.d/nginx-limit-req.local b/Fail2Ban/filter.d/nginx-limit-req.local new file mode 100644 index 000000000..e81b9473d --- /dev/null +++ b/Fail2Ban/filter.d/nginx-limit-req.local @@ -0,0 +1,16 @@ +# Fail2ban filter for detecting abuse against wp-login.php attempts +# Author: Serg G. Brester (sebres) +# https://github.com/fail2ban/fail2ban + +# Add into your nginx.conf file the following rate limiting zone + +# limit_req_zone $binary_remote_addr zone=wp-login:10m rate=1r/s; + +# see jail.local example for the config of this jail +[Definition] + +ngx_limit_req_zones = wp-login + +failregex = ^\s*\[error\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: + +ignoreregex = \ No newline at end of file diff --git a/Fail2Ban/jail.local b/Fail2Ban/jail.local index d11a7ed25..badb1d7c8 100644 --- a/Fail2Ban/jail.local +++ b/Fail2Ban/jail.local @@ -11,4 +11,13 @@ filter = nginxrepeatoffender banaction = nginxrepeatoffender bantime = 86400 ; 1 day findtime = 604800 ; 1 week -maxretry = 20 \ No newline at end of file +maxretry = 20 + +# This is the jail setting for using the Fail2Ban limit-req filter for detecting wp-login attacks + +[nginx-limit-req] +enabled = true +port = http,https +filter = nginx-limit-req +logpath = %(nginx_error_log)s +maxretry = 1 \ No newline at end of file diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..c406fa212 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,114 @@ +user www-data; +worker_processes auto; +error_log /tmp/error.log; +pid /tmp/nginx.pid; + +events { + worker_connections 1024; + multi_accept on; + use epoll; +} + +http { + + ## + # Basic Settings + ## + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 90s; + keepalive_requests 1000; + server_tokens off; + client_body_buffer_size 32k; + client_header_buffer_size 1k; + client_max_body_size 50M; + types_hash_max_size 2048; + server_names_hash_bucket_size 64; + server_names_hash_max_size 4096; + large_client_header_buffers 4 16k; + + # Our request limiter zone for wp-login attacks + limit_req_zone $binary_remote_addr zone=wp-login:10m rate=1r/s; + + # DDos Mitigation + # *************** + # https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus/ + # Limiting the Rate of Requests + limit_req_zone $ratelimited zone=flood:50m rate=90r/s; + # Limiting the Number of Connections + limit_conn_zone $ratelimited zone=addr:50m; + # End Slow conections + #client_body_timeout 5s; + #client_header_timeout 5s; + + + # use any of the following two + real_ip_header CF-Connecting-IP; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ## + # Logging Settings + ## + log_format custom '$remote_addr - $http_x_forwarded_for $remote_user $server_port [$time_local] "$request" ' + '$status "$http_referer" "$http_user_agent" "$http_header"' + '"$body_bytes_sent" - "$gzip_ratio"'; + + log_format timedcombined '$remote_addr - $http_cf_connecting_ip $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" ' + 'PORT:$server_port $request_time $upstream_response_time $pipe "$gzip_ratio"'; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + log_format wordpress '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"' + 'PORT:$server_port $request_time $upstream_response_time $pipe "GZIP:$gzip_ratio"'; + + access_log /var/log/nginx/access.log timedcombined; + error_log /var/log/nginx/error.log; + + ## + # Gzip Settings + ## + + gzip on; + gzip_disable "MSIE [1-6]\."; + gzip_vary on; + gzip_static on; + gzip_min_length 20; + gzip_proxied expired no-cache no-store private auth; + gzip_comp_level 7; + gzip_buffers 32 4k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + #gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml; + + ## + # Virtual Host Configs + ## + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; + +} + + + + server { + listen 80; + server_name fakedomain.com; + root /var/www; + limit_conn conn_limit_per_ip 10; + limit_req zone=req_limit_per_ip burst=10 nodelay; + + } + } + +} diff --git a/test-nginx.sh b/test-nginx.sh new file mode 100755 index 000000000..84e2cfde4 --- /dev/null +++ b/test-nginx.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +NGINX_CONFIG="${DIR}/nginx.conf" +NGINX_CONFIG_LOG="/tmp/nginx-config-check.log" +NGINX_CACHE_DIR="${DIR}/nginx-cache" +NGINX_BINARY="${NGINX_CACHE_DIR}/nginx" +NGINX_DEB_URL="https://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.10.0-1~trusty_amd64.deb" +#NGINX_DEB_URL="https://launchpad.net/~ondrej/+archive/ubuntu/nginx-mainline/+build/10448355/+files/nginx-light_1.11.2-1~exp1+deb.sury.org~trusty+3_amd64.deb" + +# Download nginx binary, if necessary +mkdir -p ${NGINX_CACHE_DIR} +if [[ ! -f "${NGINX_BINARY}" ]]; then + DEB_TMP_PATH="/tmp/nginx.deb.$$" + DEB_TMP_UNPACKED="/tmp/nginx-deb/" + wget "${NGINX_DEB_URL}" -O "${DEB_TMP_PATH}" + pushd /tmp + mkdir -p "${DEB_TMP_UNPACKED}" + dpkg-deb -R "${DEB_TMP_PATH}" $(basename "${DEB_TMP_UNPACKED}") + cp "${DEB_TMP_UNPACKED}/usr/sbin/nginx" "${NGINX_BINARY}" + popd +fi + +chmod +x ${NGINX_CACHE_DIR}/nginx +${NGINX_CACHE_DIR}/nginx -t -c "${NGINX_CONFIG}" 2>${NGINX_CONFIG_LOG} + +if [[ $(cat $NGINX_CONFIG_LOG | egrep "syntax is ok" | wc -l) -eq 1 ]]; then + echo "Nginx configuration OK!" + exit 0 +else + echo -e "Nginx configuration invalid:\n" + cat ${NGINX_CONFIG_LOG} + exit 1 +fi \ No newline at end of file