Błąd cURL error 28: Connection timed out after 10002 milliseconds przy podłączaniu wtyczki Furgonetka do WooCommerce w Dockerze oznacza zwykle, że kontener nie może połączyć się z własną domeną. Winny jest NAT hairpin zablokowany przez politykę DROP na hoście, a nie wtyczka. Poniżej pokazujemy, jak to rozpoznać jednym poleceniem, dlaczego drugi sklep na tym samym serwerze działał przypadkiem i co naprawia flush DNS po zmianie w Cloudflare.
Skąd bierze się cURL error 28 przy autoryzacji WooCommerce?
Podłączanie wtyczki kończyło się na adresie /wc-auth/v1/access_granted/ błędem z timeoutem 10 sekund. WooCommerce Auth przy access_granted robi POST z serwera na przekazany callback_url. U nas adres zwrotny wtyczki leżał na domenie sklepu, więc potrzebna była działająca pętla zwrotna HTTP z kontenera z PHP.
Dlaczego kontener nie łączy się z własną domeną?
DNS w kontenerze zwracał publiczny adres hosta, bo rekord w Cloudflare był w trybie bez proxy. Pakiet z sieci bridge Dockera na publiczny adres własnego hosta wraca jako ruch przychodzący na łańcuch INPUT, gdzie policy DROP go odrzuca. Ruch z zewnątrz działa, bo wchodzi łańcuchem DOCKER w FORWARD i omija INPUT.
| Ścieżka | Wynik |
|---|---|
| kontener do nginx po adresie wewnętrznym | 200, 0,16 ms |
| kontener do domeny sklepu (adres publiczny) | timeout, połączenie nigdy nie startuje |
| host do własnego adresu publicznego | 200, 2 ms |
| kontener do Google | 200 (wyjście na świat sprawne) |
W regułach zapory nie było akceptacji dla portów 80 i 443 w łańcuchu wejściowym (INPUT).
Dlaczego drugi sklep na tym serwerze działał bez problemu?
To fałszywy trop. Drugi sklep robił pętlę zwrotną bez błędu, ale tylko dlatego, że miał włączony tryb proxy w Cloudflare. Ruch szedł wtedy do Cloudflare i wracał z zewnątrz, omijając hairpin. Nie był poprawnie skonfigurowany, tylko przypadkiem omijał wadę. To też nie regresja po innych zmianach: wada sieci istniała od zawsze, tylko nic wcześniej nie potrzebowało pętli zwrotnej.
Jak szybko sprawdzić pętlę zwrotną z kontenera?
Pierwszy krok przy cURL error 28 to test z wnętrza kontenera, nie przeglądanie wtyczki:
docker exec <kontener-wp> curl -s -o /dev/null \
-w 'code:%{http_code} connect:%{time_connect} ip:%{remote_ip}\n' \
--max-time 10 https://<domena>/wp-json/Kod 000 przy connect:0.000000 oznacza, że nie udało się nawiązać połączenia TCP, więc szukaj w sieci, DNS albo zaporze, a nie w PHP. Dodaj flagę -v, żeby zobaczyć komunikat błędu, i sprawdź, na jaki adres kontener naprawdę celuje:
docker exec <kontener-wp> getent ahostsv4 <domena>Masz ten sam problem? Wtyczka nie łączy się z własną domeną w Dockerze? Sprawdzimy zaporę, DNS i sieć kontenerów. Opisz sytuację →
Jak naprawić pętlę zwrotną przez Cloudflare i flush DNS?
W jednym ze sklepów włączyliśmy tryb proxy w Cloudflare. Samo to nie wystarczyło, kontener nadal dostawał timeout, z dwóch powodów:
- resolver zwracał świeży rekord AAAA, a kontener nie miał IPv6 (brak adresu i trasy), więc był bezużyteczny,
- dla IPv4 zwracał stary, zakeszowany adres hosta, czyli wracaliśmy do hairpin.
W naszej konfiguracji (domyślny resolver Dockera przekazujący zapytania do systemd-resolved na hoście) stary rekord siedział w cache hosta. Naprawa to jedno polecenie, bez zmian w zaporze ani w compose:
resolvectl flush-cachesPo flushu kontener dostawał adresy Cloudflare, a pętla zwrotna odpowiadała 200 w 0,19 s. Callback wtyczki zwracał 401 po 0,26 s, a access_granted 401 po 0,22 s. Kod 401 jest tu poprawny: ścieżka jest drożna, a serwer odmawia bez tokenu. Wcześniej był timeout po 10 sekundach.
Na przyszłość. Jeśli kontenery używają resolvera hosta z systemd-resolved, po zmianie trybu rekordu w Cloudflare (bez proxy lub z proxy) wykonaj resolvectl flush-caches na hoście. Inaczej kontenery przez wiele minut trzymają stary rekord.
Jak naprawić hairpin na stałe na wszystkich sklepach serwera?
Drugi sklep na serwerze, bez proxy, miał ten sam problem i ujawniłby się przy WooCommerce Auth, WP-Cron po HTTP albo webhookach zwrotnych. Są dwie drogi:
- Tryb proxy w Cloudflare dla domeny, zero zmian na serwerze, najprostsze.
- Reguła zapory dla mostów tworzonych przez docker compose (
br-*), obejmująca wszystkie takie stacki na serwerze:
iptables -I INPUT -i br-+ -p tcp -m multiport --dports 80,443 -j ACCEPTReguła obejmuje mosty br-*, a nie interfejs zewnętrzny, więc nic nie wystawia na zewnątrz. Domyślny most docker0 ma inną nazwę, więc ścieżkę pakietu i interfejs sprawdź na swoim serwerze, na przykład licznikami reguł. Wymaga utrwalenia (na przykład iptables-persistent), bo inaczej ginie po restarcie serwera.
Kiedy zlecić to specjalistom?
Gdy wtyczki, które wołają własną domenę (Furgonetka, WooCommerce Auth, webhooki, WP-Cron), zgłaszają timeouty, a w logach PHP nie ma błędu. W ASAPDevs sprawdzamy sieć kontenerów, zaporę i DNS w ramach migracji i konfiguracji serwera oraz stałej opieki nad WordPressem. O innych awariach środowiska Docker piszemy w artykule o sklepie offline przez pełny dysk.
Źródła: dokumentacja Docker, Packet filtering and firewalls (łańcuchy iptables, DOCKER-USER).
