Uncategorized

How to Fix fail2ban + Nginx Proxy Manager + Cloudflare on Docker (Synology NAS)

Fixing the Fail2Ban Nightmare: A Step-by-Step Guide to Securing Your Server with Nginx Proxy Manager and Cloudflare

If you’re like me, you’ve probably spent hours trying to get Fail2Ban to work with Nginx Proxy Manager and Cloudflare. It’s a frustrating experience, to say the least. But don’t worry, I’m here to help. In this post, I’ll share my journey of trying to fix the issue and provide a step-by-step guide on how to secure your server with these tools.

The Problem

When you’re running Nginx Proxy Manager in Docker with Cloudflare proxy enabled, you might have noticed that Fail2Ban can’t ban attackers because all traffic appears to come from Docker’s bridge IP (172.22.0.1) instead of the real client IPs. This makes Fail2Ban useless for web protection, which is a major security concern.

The Solution

The solution lies in using a custom log format that extracts the real client IP from Cloudflare’s headers and puts it at the beginning of log lines where Fail2Ban expects to find it. This way, Fail2Ban can correctly parse the IP address and ban attackers based on their real IPs.

Step-by-Step Tutorial

Here’s a step-by-step guide on how to implement this solution:

Prerequisites:

  • Fail2Ban installed and working
  • Nginx Proxy Manager running in Docker
  • Cloudflare proxy enabled (orange cloud) for your domains

Step 1: Create Custom Log Format

Create or edit /data/nginx/custom/http_top.conf in your NPM data directory with the following code:

# Custom log format that puts the real client IP at the beginning
# This allows fail2ban to correctly parse the IP address
# First, map the real client IP from various sources
map $http_cf_connecting_ip $real_client_ip {
# If CF-Connecting-IP exists (Cloudflare), use it
~^(.+)$ $1;
# Otherwise fall back to X-Forwarded-For default
default $http_x_forwarded_for;
}
# Extract just the first IP if X-Forwarded-For has multiple
map $real_client_ip $client_ip_final {
# Extract first IP from comma-separated list
~^([^,]+) $1;
# If no comma, use as-is default
default $real_client_ip;
}
# Custom log format with real IP at the beginning
log_format cloudflare_real '$client_ip_final - $remote_user [$time_local] "$request" '
'"$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_cf_ray"';

Step 2: Configure Each Proxy Host

For each Cloudflare-proxied site in NPM, add the following code to the Advanced tab:

# Replace XX with your proxy host ID number
access_log /data/logs/proxy-host-XX_cloudflare.log cloudflare_real;
# Also pass real IP to backend
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

Step 3: Create fail2ban Filter

Create /etc/fail2ban/filter.d/nginx-cloudflare.conf with the following code:

[INCLUDES]
before = common.conf
[Definition]
# Match various HTTP error codes and attack patterns
failregex = ^<HOST> - .* "\w+ [^"]+" (400|401|403|404|405|444) .*$
^<HOST> - .* "\w+ (/admin|/wp-admin|/wp-login|/xmlrpc\.php|/\.env|/\.git)[^"]*" \d+ .*$
^<HOST> - .* "\w+ [^"]*(\.\.|//|\\\\)[^"]*" \d+ .*$
# Ignore successful requests to legitimate static assets
ignoreregex = ^<HOST> - .* "\w+ [^"]+" 200 .*\.css.*$
datepattern = \[{DATE}\] {^LN-BEG}

Step 4: Configure fail2ban Jail

Add the following code to /etc/fail2ban/jail.local:

[npm-cloudflare]
enabled = true
filter = nginx-cloudflare
port = 80,443
# Monitor the custom log files with real IPs
logpath = /path/to/npm/data/logs/proxy-host-*_cloudflare.log
maxretry = 5
findtime = 600
bantime = 86400
action = iptables-multiport[name=%(__name__)s, port="%(port)s", protocol="tcp"]

Step 5: Add Cloudflare IP Whitelisting (Important!)

In your jail.local, add Cloudflare’s IP ranges to ignoreip:

ignoreip = 127.0.0.1/8 ::1
# Your local networks
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
# Cloudflare IPv4
173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/13
104.24.0.0/14
172.64.0.0/13
131.0.72.0/22

Step 6: Restart Services

# Restart NPM
docker restart nginx-proxy-manager
# Restart fail2ban
systemctl restart fail2ban

Results

After implementing these steps, you should see real client IPs in your logs instead of the Docker bridge IP. Fail2Ban should now be able to detect and ban attackers based on their real IPs, providing better security for your server.

Example Log Output

Before (Docker bridge IP):

[11/Aug/2025:18:45:33] "GET /wp-login.php" 404 [Client 172.22.0.1]

After (Real client IP):

206.130.127.71 - - [11/Aug/2025:18:51:35] "GET /wp-login.php" 404

Bonus: Cloudflare API Integration

For even better protection, you can ban IPs at Cloudflare’s edge by creating a Cloudflare API integration. This will block attackers before they even reach your server.

Troubleshooting

If you’re still having issues, here are some troubleshooting tips:

  • Still seeing 172.22.0.1? – Make sure you added the config to NPM’s Advanced tab and used the correct proxy host ID.
  • Fail2Ban not detecting attacks? – Check the filter regex matches your log format with fail2ban-regex.
  • Logs not created? – Restart NPM after adding the Advanced configuration.

Naprawienie koszmaru Fail2Ban: krok po kroku przewodnik po zabezpieczaniu serwera za pomocą Nginx Proxy Manager i Cloudflare

Jeśli jesteś jak ja, to prawdopodobnie spędziłeś godziny próbując uzyskać Fail2Ban, aby działał z Nginx Proxy Manager i Cloudflare. To frustrujące doświadczenie, aby powiedzieć najmniej. Ale nie martw się, jestem tutaj, aby pomóc. W tym poście, udostępnię moją historię prób i podzielę się krok po kroku przewodnikiem, jak zabezpieczyć swój serwer za pomocą tych narzędzi.

Problem

Gdy uruchamiasz Nginx Proxy Manager w Dockerze z włączoną funkcją proxy Cloudflare, możesz zauważyć, że Fail2Ban nie może zbanować atakujących, ponieważ cały ruch wydaje się pochodzić z adresu IP mostka Docker (172.22.0.1) zamiast z rzeczywistych adresów IP klientów. To sprawia, że Fail2Ban staje się bezużyteczny dla ochrony sieci web, co jest poważnym problemem bezpieczeństwa.

Rozwiązanie

Rozwiązanie polega na użyciu niestandardowego formatu logowania, który wyodrębnia rzeczywisty adres IP klienta z nagłówków Cloudflare i umieszcza go na początku linii logów, gdzie Fail2Ban oczekuje znalezienia go. W ten sposób Fail2Ban może poprawnie sparsować adres IP i zbanować atakujących na podstawie ich rzeczywistych adresów IP.

Samouczek krok po kroku

Oto samouczek krok po kroku, jak zaimplementować to rozwiązanie:

Wymagania wstępne:

  • Fail2Ban zainstalowany i działający
  • Nginx Proxy Manager uruchomiony w Dockerze
  • Funkcja proxy Cloudflare włączona (chmura pomarańczowa) dla Twoich domen

Krok 1: Utwórz niestandardowy format logowania

Utwórz lub edytuj /data/nginx/custom/http_top.conf w katalogu danych NPM z następującym kodem:

# Niestandardowy format logowania, który umieszcza rzeczywisty adres IP klienta na początku
# Pozwala to Fail2Ban na poprawne sparsowanie adresu IP
# Po pierwsze, mapujesz rzeczywisty adres IP klienta z różnych źródeł
map $http_cf_connecting_ip $real_client_ip {
# Jeśli CF-Connecting-IP istnieje (Cloudflare), użyj go
~^(.+)$ $1;
# W przeciwnym razie użyj domyślnie $http_x_forwarded_for
default $http_x_forwarded_for;
}
# Wyodrębnij tylko pierwszy adres IP, jeśli X-Forwarded-For ma wiele adresów
map $real_client_ip $client_ip_final {
# Wyodrębnij pierwszy adres IP z listy przecinkowej
~^([^,]+) $1;
# Jeśli nie ma przecinka, użyj jako-is domyślnie
default $real_client_ip;
}
# Niestandardowy format logowania z rzeczywistym adresem IP na początku
log_format cloudflare_real '$client_ip_final - $remote_user [$time_local] "$request" '
'"$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_cf_ray"';

Krok 2: Konfigurowanie każdego hosta proxy

Dla każdego hosta proxy Cloudflare w NPM, dodaj następujący kod do zakładki Advanced:

# Zastąp XX z Twoim numerem ID hosta proxy
access_log /data/logs/proxy-host-XX_cloudflare.log cloudflare_real;
# Przekazuj również rzeczywisty adres IP do serwera proxy
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

Krok 3: Utwórz filtr Fail2Ban

Utwórz /etc/fail2ban/filter.d/nginx-cloudflare.conf z następującym kodem:

[INCLUDES]
before = common.conf
[Definition]
# Dopasuj różne kody błędów HTTP i wzorce ataków
failregex = ^<HOST> - .* "\w+ [^"]+" (400|401|403|404|405|444) .*$
^<HOST> - .* "\w+ (/admin|/wp-admin|/wp-login|/xmlrpc\.php|/\.env|/\.git)[^"]*" \d+ .*$
^<HOST> - .* "\w+ [^"]*(\.\.|//|\\\\)[^"]*" \d+ .*$
# Ignoruj pomyślne żądania do prawidłowych zasobów statycznych
ignoreregex = ^<HOST> - .* "\w+ [^"]+" 200 .*\.css.*$
datepattern = \[{DATE}\] {^LN-BEG}

Krok 4: Konfigurowanie więzienia Fail2Ban

Dodaj następujący kod do /etc/fail2ban/jail.local:

[npm-cloudflare]
enabled = true
filter = nginx-cloudflare
port = 80,443
# Monitoruj niestandardowe pliki logów z rzeczywistymi adresami IP
logpath = /path/to/npm/data/logs/proxy-host-*_cloudflare.log
maxretry = 5
findtime = 600
bantime = 86400
action = iptables-multiport[name=%(__name__)s, port="%(port)s", protocol="tcp"]

Krok 5: Dodaj białą listę adresów IP Cloudflare (ważne!)

W swoim jail.local, dodaj zakresy adresów IP Cloudflare do ignoreip:

ignoreip = 127.0.0.1/8 ::1
# Twoje sieci lokalne
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
# Zakresy adresów IP Cloudflare
173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/13
104.24.0.0/14
172.64.0.0/13
131.0.72.0/22

Krok 6: Zrestartuj usługi

# Zrestartuj NPM
docker restart nginx-proxy-manager
# Zrestartuj Fail2Ban
systemctl restart fail2ban

Wyniki

Po zaimplementowaniu tych kroków, powinieneś zobaczyć rzeczywiste adresy IP klientów w logach zamiast adresu IP mostka Docker. Fail2Ban powinien teraz być w stanie wykryć i zbanować atakujących na podstawie ich rzeczywistych adresów IP, zapewniając lepsze zabezpieczenie serwera.

Przykładowy log

Przed (adres IP mostka Docker):

[11/Aug/2025:18:45:33] "GET /wp-login.php" 404 [Client 172.22.0.1]

Po (rzeczywisty adres IP):

206.130.127.71 - - [11/Aug/2025:18:51:35] "GET /wp-login.php" 404

Bonus: Integracja z interfejsem API Cloudflare

Dla jeszcze lepszej ochrony, możesz zbanować adresy IP na skraju Cloudflare, tworząc integrację z interfejsem API Cloudflare. To zablokuje atakujących, zanim nawet dotrą do Twojego serwera.

Rozwiązywanie problemów

Jeśli nadal masz problemy, oto kilka wskazówek dotyczących rozwiązywania problemów:

  • Czy wciąż widzisz 172.22.0.1? – Upewnij się, że dodałeś konfigurację do zakładki Advanced w NPM i użyłeś poprawnego numeru ID hosta proxy.
  • Fail2Ban nie wykrywa ataków? – Sprawdź, czy filtr regex pasuje do Twojego formatu logowania za pomocą fail2ban-regex.
  • Logi nie są tworzone? – Zrestartuj NPM po dodaniu konfiguracji w zakładce Advanced.

Leave a Reply

Your email address will not be published. Required fields are marked *

WordPress Appliance - Powered by TurnKey Linux