vps·web
PL

Apache mod_remoteip za Nginx Proxy Managerem

Apache za Nginx Proxy Managerem — prawdziwy IP klienta w logach

Serwery · Apache mod_remoteip za Nginx Proxy Managerem

Apache mod_remoteip za Nginx Proxy Managerem — prawdziwy IP klienta w logach

Apache za Nginx Proxy Managerem zapisuje w logach adres IP proxy, a nie rzeczywistego klienta. Plik access.log wypełnia się wpisami typu 192.168.x.x, fail2ban banuje pośrednika zamiast atakujących, a $_SERVER['REMOTE_ADDR'] w PHP zwraca zły adres. Rozwiązaniem jest mod_remoteip — moduł Apache 2.4, który podmienia adres klienta połączenia na ten przekazany w nagłówku X-Forwarded-For przez zaufany serwer pośredniczący. Ten poradnik pokazuje gotową konfigurację, tłumaczy znaczenie każdej dyrektywy i wymienia pułapki, przez które przez godzinę będziesz patrzeć w niewłaściwy adres.

Generator Nginx & Apache2 Remote IP — co robi i komu się przyda

Generator Nginx & Apache2 Remote IP na vps-web.com ma jedno zadanie: wygenerować zestaw gotowych do wklejenia poleceń, które włączają mod_remoteip w Apache2 i ustawiają Nginx Proxy Managera jako zaufanego pośrednika dla X-Forwarded-For. Podajesz adres IP serwera Nginx Proxy Managera, a otrzymujesz pięć bloków do skopiowania — włączenie modułu, utworzenie pliku /etc/apache2/conf-available/remoteip.conf, aktywację konfiguracji, poprawkę domyślnego LogFormat, by w access logu pojawił się prawdziwy adres, oraz przeładowanie Apache po sprawdzeniu składni.

Generator przydaje się w trzech scenariuszach: maszyna wirtualna z Nginx Proxy Managerem przekazująca ruch do backendowego Apache (klasyczny układ w homelabie), stos Docker z Apache za kontenerem NPM oraz mały VPS, na którym NPM kończy SSL i kieruje ruch do lokalnego Apache. Generator zakłada jedno proxy o stałym adresie — dla Cloudflare lub innych CDN-ów lista zaufanych pośredników wygląda inaczej i wymaga podania zakresów IP, a nie pojedynczego hosta.

Jeśli wolisz przejrzeć wygenerowany output i edytować ręcznie, formularz ma tylko jedno pole — adres IP proxy — i przycisk Generate. Wynikiem są polecenia powłoki, a nie fragmenty konfiguracji do wklejania w edytorze. To celowe: omija najczęstszy błąd — zapomnienie o a2enconf po utworzeniu pliku.

Praktyka — konfiguracja mod_remoteip dla Apache2 za Nginx Proxy Managerem

Cała konfiguracja składa się z czterech rzeczy: włączenie modułu, wskazanie nagłówka z prawdziwym adresem, wpisanie zaufanego pośrednika oraz aktualizacja formatu logu, żeby ten adres faktycznie się pojawił. Pomiń którąkolwiek z tych czynności i konfiguracja po cichu działa nie tak, jak chcesz.

Krok 1 — włącz mod_remoteip

sudo a2enmod remoteip

mod_remoteip jest częścią Apache 2.4, ale w Debianie i Ubuntu domyślnie wyłączony. Polecenie tworzy dowiązanie symboliczne z /etc/apache2/mods-available/remoteip.load do mods-enabled/. Bez restartu — Apache przeładujesz dopiero po wgraniu pełnej konfiguracji.

Krok 2 — utwórz plik konfiguracyjny

Dwie kluczowe dyrektywy to RemoteIPHeader (który nagłówek niesie prawdziwy adres klienta) oraz RemoteIPTrustedProxy (które serwery są uprawnione do ustawiania tego nagłówka). Umieść je w dedykowanym pliku w conf-available/:

sudo tee -a /etc/apache2/conf-available/remoteip.conf > /dev/null <<'EOF'
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 192.168.23.10
EOF

192.168.23.10 zastąp faktycznym adresem Nginx Proxy Managera. Użyj adresu, którym NPM łączy się z Apache w tej samej sieci — jeśli oba kontenery siedzą w jednej sieci dockerowej, to adres NPM w tej sieci, a nie adres hosta.

RemoteIPHeader X-Forwarded-For mówi Apache, że ma sprawdzać nagłówek X-Forwarded-For i zastępować adres klienta tym, co tam zastanie. Nginx Proxy Manager domyślnie wstawia ten nagłówek (proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;), więc po stronie NPM nic nie musisz zmieniać.

RemoteIPTrustedProxy 192.168.23.10 to lista hostów, którym Apache wierzy. Bez tej dyrektywy mod_remoteip ufa każdemu klientowi wysyłającemu X-Forwarded-For — to poważny problem, bo dowolny klient może spreparować ten nagłówek i podszyć się pod inny adres. Z dyrektywą Apache rozważa zawartość nagłówka tylko wtedy, gdy faktyczne połączenie TCP przychodzi z wymienionego proxy.

Subtelna różnica — RemoteIPTrustedProxy domyślnie odrzuca adresy RFC 1918 (10/8, 172.16/12, 192.168/16) zawarte w nagłówku, traktując je jako podejrzane. Jeśli NPM stoi w prywatnej podsieci i chcesz też zalogować klienta, który naprawdę siedzi w tej samej sieci wewnętrznej, użyj zamiast tego RemoteIPInternalProxy. Pełny opis dyrektyw znajduje się w oficjalnej dokumentacji mod_remoteip.

Krok 3 — aktywuj konfigurację

sudo a2enconf remoteip

To polecenie tworzy dowiązanie z conf-available/remoteip.conf do conf-enabled/. Sam plik w conf-available/ nie jest automatycznie wczytywany — Debian i Ubuntu specjalnie rozdzielają te dwa katalogi, żeby można było szybko włączać i wyłączać fragmenty konfiguracji bez ich kasowania. To krok, o którym najczęściej zapomina się w tutorialach: tworzysz plik, restartujesz Apache i… nic się nie zmienia, bo konfiguracja nigdy nie została aktywowana.

Krok 4 — popraw LogFormat, żeby pokazywał prawdziwy adres

Domyślny combined w Apache zaczyna się od %h — nazwy hosta lub adresu klienta. Po zadziałaniu mod_remoteip zarówno %h, jak i %a powinny pokazywać podmieniony adres. W praktyce jednak istnieje znana niezgodność: w niektórych kombinacjach wersji i pakietów %h ciągle wskazuje na oryginalne połączenie, podczas gdy %a zawsze pokazuje wartość po rewrite. Pewniejsze jest przełączenie %h na %a w formacie combined:

sudo sed -i.bak 's|^LogFormat "%h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" combined|LogFormat "%a %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" combined|' /etc/apache2/apache2.conf

Sufiks .bak zachowuje kopię oryginalnego pliku obok zmienionego — przyda się, jeśli podmiana nie złapie na zmodyfikowanym apache2.conf. Jeśli chcesz mieć w logu również oryginalny adres połączenia (adres proxy), użyj %{c}a — w dokumentacji opisany jako „underlying client IP of the connection", odczytywany jako faktyczny peer TCP, z pominięciem rewrite.

Krok 5 — sprawdź składnię i przeładuj

sudo apache2ctl configtest && sudo systemctl reload apache2

configtest parsuje wszystkie aktywne pliki konfiguracji i wychwytuje literówki, zanim położą serwer. Operator && gwarantuje, że reload wykona się tylko wtedy, gdy test przejdzie — drobny nawyk, który zapobiega zepsutym przeładowaniom. Reload (a nie restart) podtrzymuje istniejące połączenia, jednocześnie wczytując nową konfigurację i moduł.

Po przeładowaniu wejdź na stronę przez Nginx Proxy Managera i obserwuj logi:

sudo tail -f /var/log/apache2/access.log

Na początku każdej linii powinien być teraz prawdziwy adres klienta, a nie adres proxy. Jeśli testujesz z tej samej sieci LAN, użyj curl --resolve z telefonu w sieci komórkowej albo z zewnętrznej powłoki — sprawdzanie tylko z poziomu sieci proxy ukryje, czy reguły RemoteIPInternalProxy faktycznie działają poprawnie dla adresów zewnętrznych.

Częste błędy i pułapki

Pomijasz a2enconf po utworzeniu pliku

Wrzucasz remoteip.conf do /etc/apache2/conf-available/, przeładowujesz Apache i nic się nie dzieje. Plik leży w conf-available/ nieprzypadkowo — jest dostępny, ale nie aktywny. Wykonaj sudo a2enconf remoteip, by utworzyć dowiązanie do conf-enabled/. Ta sama logika co a2ensite i sites-available/ — Debian i Ubuntu rozdzielają układ, żeby konfiguracje dało się szybko włączać i wyłączać.

Logi nadal pokazują adres proxy mimo poprawnej konfiguracji

Dwie przyczyny. Po pierwsze, używasz domyślnego combined, który zaczyna się od %h, a nietypowa wersja pakietu wczytuje %h z surowego połączenia — przełącz na %a (sposób opisany wyżej). Po drugie, wartość w RemoteIPTrustedProxy nie odpowiada faktycznemu źródłu połączenia. Uruchom tcpdump -i any -n port 80 w trakcie testowego żądania przez NPM i sprawdź, jaki adres TCP Apache widzi jako źródło — to ten adres musi pojawić się w RemoteIPTrustedProxy, a nie publiczny adres NPM.

mod_remoteip zwraca adres prywatny zamiast publicznego

Jeśli X-Forwarded-For zawiera 10.0.0.5, a użyłeś RemoteIPTrustedProxy, Apache odrzuci rewrite — ta dyrektywa traktuje adresy z zakresów prywatnych w nagłówku jako podejrzane i wraca do adresu proxy. Dla scenariuszy, w których klient faktycznie siedzi w sieci prywatnej (homelab, sieć firmowa), zamień dyrektywę na RemoteIPInternalProxy 192.168.23.10. Ta wersja ufa całej zawartości nagłówka, włącznie z zakresami RFC 1918.

Nginx Proxy Manager nie wysyła X-Forwarded-For

Domyślnie NPM generuje bloki serwerowe z proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; dla każdego proxy host. Jeśli logi dalej pokazują adres proxy, a konfiguracja Apache jest poprawna, edytuj proxy host w panelu NPM, otwórz zakładkę Advanced i dodaj nagłówki jawnie — niektóre starsze wersje NPM albo niestandardowe lokalizacje pomijają wartości domyślne. Przy okazji sprawdź, czy ustawione są również X-Real-IP i X-Forwarded-Proto. Wątek dyskusji na GitHubie Nginx Proxy Managera zawiera przypadki, w których domyślne bloki location nie przekazują tych nagłówków.

Łańcuch kilku pośredników

Jeśli NPM sam stoi za innym proxy (Cloudflare, load balancer), X-Forwarded-For przychodzi jako lista oddzielona przecinkami: 203.0.113.5, 192.168.23.10. Apache czyta tę listę od prawej do lewej i pomija każdy wpis, który pasuje do RemoteIPTrustedProxy — pierwszy niezaufany staje się nowym adresem klienta. Jeśli chcesz, żeby żądania z Cloudflare zwracały rzeczywisty adres odwiedzającego, dodaj zakresy IP Cloudflare do RemoteIPTrustedProxy. Cloudflare publikuje aktualne zakresy IP — lista zmienia się dość często, więc warto zautomatyzować jej aktualizację, a nie wklejać raz na zawsze.

apache2ctl configtest zwraca Syntax error on line N

Najczęściej winowajcą jest niepoprawny cudzysłów wewnątrz heredoc albo wklejka z bloga, w której zwykłe cudzysłowy zamieniły się na typograficzne („"). Powtórz polecenie tee z <<'EOF' (heredoc w pojedynczych cudzysłowach), żeby wyłączyć rozwijanie zmiennych i ucieczki znaków. Następnie sprawdź ponownie. Jeśli configtest skarży się na nieznaną dyrektywę RemoteIPHeader, moduł nie jest załadowany — wróć do a2enmod remoteip i sprawdź obecność dowiązania w /etc/apache2/mods-enabled/.

Fail2ban dalej banuje proxy

Fail2ban czyta access log. Jeśli poprawiłeś LogFormat po starcie fail2ban, ten ma w pamięci stan jaila z adresem proxy. Zrestartuj fail2ban (sudo systemctl restart fail2ban), a potem zweryfikuj poleceniem sudo fail2ban-client status apache-auth, że banowane są realne adresy klientów, a nie host NPM. Jeśli nadal nie — wyrażenie regularne w failregex jaila może być zakotwiczone w pozycji %h, więc dostosuj je do nowego układu logu.

FAQ — najczęściej zadawane pytania

Czy potrzebuję mod_remoteip, jeśli Apache działa bezpośrednio bez reverse proxy?

Nie. mod_remoteip ma sens tylko wtedy, gdy Apache stoi za czymś — Nginx, HAProxy, load balancer, CDN. Bez pośrednika dostarczającego X-Forwarded-For dyrektywy nie szkodzą, ale też nic nie zmieniają. Źródłowy adres TCP połączenia to już prawdziwy klient.

Czym różni się RemoteIPTrustedProxy od RemoteIPInternalProxy?

RemoteIPTrustedProxy odrzuca w nagłówku X-Forwarded-For adresy RFC 1918 (10/8, 172.16/12, 192.168/16, 169.254/16, 127/8) — zakłada, że klient publiczny nie powinien siedzieć w adresach prywatnych. RemoteIPInternalProxy pomija ten filtr i akceptuje dowolny adres w nagłówku. Używaj wersji „Internal", gdy sam klient siedzi w sieci prywatnej (homelab, biuro).

Czy ustawić RemoteIPHeader na X-Real-IP czy X-Forwarded-For?

Wybierz X-Forwarded-For. To de facto standard, niesie pełny łańcuch pośredników (nie tylko ostatni hop) i Nginx Proxy Manager ustawia go domyślnie. X-Real-IP jest jednowartościowy i traci informację przy więcej niż jednym proxy. Przełącz się na X-Real-IP tylko, jeśli kontrolujesz cały łańcuch i potrzebujesz ścisłej semantyki — a i wtedy dla spójności z resztą ekosystemu lepiej zostać przy X-Forwarded-For.

Czy mod_remoteip da się wykorzystać, jeśli zapomnę o RemoteIPTrustedProxy?

Tak. Bez RemoteIPTrustedProxy (lub RemoteIPInternalProxy) mod_remoteip ufa każdemu klientowi wysyłającemu X-Forwarded-For. Atakujący może spreparować nagłówek i podszyć się pod dowolny adres — omijając reguły Require ip, zatruwając logi i wprowadzając w błąd aplikacje PHP czytające $_SERVER['REMOTE_ADDR']. Zawsze wskazuj zaufane proxy. Jeśli nie znasz adresu pośrednika, modułu nie powinno się jeszcze włączać.

Jak zweryfikować konfigurację bez restartu?

Użyj sudo apache2ctl configtest — parsuje aktywną konfigurację i zgłasza błędy składni. Dla testu w czasie rzeczywistym napisz prosty plik PHP, który wypisuje $_SERVER['REMOTE_ADDR'] i $_SERVER['HTTP_X_FORWARDED_FOR'], wejdź przez NPM z sieci zewnętrznej i porównaj wynik. REMOTE_ADDR powinien wskazywać twój prawdziwy adres; HTTP_X_FORWARDED_FOR powinien zawierać przynajmniej twój adres (i nic więcej, jeśli NPM nie modyfikuje łańcucha).

Dlaczego w LogFormat %a jest lepsze od %h?

Oba powinny pokazywać tę samą wartość po działaniu mod_remoteip, ale %a jest jawnie udokumentowane jako adres klienta zapisany w rekordzie połączenia — czyli ten po rewrite. %h historycznie oznaczał „nazwę hosta lub IP, jeśli HostnameLookups jest wyłączone" i w niektórych wersjach pakietów potrafi nie nadążać za podmianą. %a usuwa dwuznaczność. Jeśli potrzebujesz w celach debugowych również oryginalnego peera TCP, użyj %{c}a.

Czy działa to z HTTPS terminowanym na Nginx Proxy Managerze?

Tak, to wręcz najczęstszy układ. NPM kończy SSL, rozmawia z Apache po zwykłym HTTP, przekazuje nagłówki X-Forwarded-For i X-Forwarded-Proto. Apache czerpie z mod_remoteip adres klienta, a opcjonalnie SetEnvIf X-Forwarded-Proto https HTTPS=on, jeśli dalsza aplikacja potrzebuje wiedzieć, że oryginalne żądanie było szyfrowane. WordPress szczególnie często tego wymaga — bez tej zmiennej generuje błędne URL-e do zasobów (mieszanka HTTP i HTTPS).

Czy mogę użyć mod_remoteip z kontenerami Docker?

Tak. Ta sama konfiguracja działa wewnątrz Apache w Dockerze. Pułapka jest taka, że „adres proxy" to teraz adres NPM w sieci dockerowej, a nie jego host czy adres publiczny. Użyj docker network inspect <nazwa-sieci>, by odczytać ten adres, ustaw RemoteIPTrustedProxy na niego i utrwal konfigurację w obrazie albo w wolumenie — zmiany w konfiguracji Apache wewnątrz kontenera nie przeżyją jego ponownego utworzenia.

Następne kroki

Wygeneruj konfigurację z preferowanym adresem Nginx Proxy Managera w generatorze Nginx & Apache2 Remote IP na vps-web.com — wynikiem jest pięć poleceń do skopiowania. Połącz to z generatorem VirtualHost na vps-web.com, żeby mieć pasującą konfigurację strony.

Filmy z konfiguracją reverse proxy i Apache krok po kroku znajdziesz na moim kanale YouTube — na żywym serwerze, z pokazaniem efektu w logach. Jeśli ten poradnik zaoszczędził ci popołudnia, subskrypcja pomaga, żeby powstawały kolejne.