SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-21

Jak ominąć CGNAT przy użyciu VPS i tunelu zwrotnego

Brak publicznego adresu IP w sieciach CGNAT uniemożliwia przekierowanie portów. Dowiedz się, jak skonfigurować frp na VPS, aby bezpiecznie wystawić usługi domowe do sieci.

Dlaczego przekierowanie portów nie działa w sieciach CGNAT

W sieciach CGNAT (carrier-grade network address translation) adres WAN routera jest współdzielony z innymi abonentami. Użytkownik nie posiada publicznego adresu IP, więc nie ma możliwości przekierowania portów. Rozwiązaniem jest tunel zwrotny: tani VPS z publicznym adresem IP. Serwer domowy nawiązuje połączenie wychodzące do VPS, a żądania przychodzące są przesyłane zwrotnie przez to otwarte połączenie. Pozwala to na zachowanie posiadanego sprzętu. Wynajmuje się jedynie brakujący element, którego dostawca internetu nie oferuje: routowalny adres IP.

Każde poniższe polecenie jest oznaczone nazwą maszyny, na której należy je wykonać. Wymagane są dwie maszyny: VPS z publicznym adresem IP oraz serwer domowy, na którym działa udostępniana usługa.

Jak sprawdzić, czy korzystasz z CGNAT

Otwórz stronę administracyjną routera i odczytaj adres WAN, który raportuje urządzenie. Następnie sprawdź w zewnętrznym serwisie, jaki adres IP widzi Internet.

# on the home box
curl -4 -s https://ifconfig.me; echo

Jeśli oba adresy są identyczne, posiadasz publiczny adres IP i nie potrzebujesz żadnego z tych rozwiązań. Przekieruj port i zakończ lekturę. Jeśli adres WAN routera znajduje się wewnątrz 100.64.0.0/10, oznacza to, że korzystasz z CGNAT. Ten zakres to przestrzeń adresowa współdzielona zgodnie z RFC 6598, zarezerwowana dokładnie do tego celu. Niektórzy dostawcy usług internetowych przypisują 10.0.0.0/8 po stronie WAN, co w praktyce oznacza tę samą sytuację pod inną nazwą.

Przed wynajęciem jakiejkolwiek usługi sprawdź jedną rzecz. Wielu dostawców stosujących CGNAT przydziela rzeczywisty prefiks IPv6. Jeśli Twoje urządzenie domowe posiada globalny adres IPv6, możesz otworzyć zaporę sieciową dla tego adresu i całkowicie pominąć tunel. Rozwiązanie to przestaje działać w momencie, gdy użytkownik odwiedzający korzysta wyłącznie z sieci IPv4, dlatego większość osób ostatecznie decyduje się na opisywane tutaj metody.

Jak działa odwrotny tunel VPS, inicjowany od wewnątrz

CGNAT oraz standardowe routery domowe blokują niechciane połączenia przychodzące. Podobnie działają zapory korporacyjne. Żadne z nich nie blokuje połączeń wychodzących, ponieważ są one niezbędne do działania przeglądarek oraz klientów aktualizacji. Urządzenie NAT, wykrywając wychodzące połączenie TCP, tworzy dla niego mapowanie i zezwala na ruch zwrotny w ramach tego samego połączenia. Żaden podmiot z zewnątrz nie może zainicjować połączenia z serwerem domowym. Dlatego to serwer domowy inicjuje połączenie, a tunel przesyła ruch z powrotem w przeciwnym kierunku.

To cały mechanizm. Serwer domowy łączy się z VPS na wybranym porcie i utrzymuje to połączenie. VPS przyjmuje żądania publiczne i przekazuje je przez istniejące połączenie. Nikt nie próbuje łączyć się bezpośrednio z domowym adresem IP, więc nie jest to wymagane.

Wynikają z tego dwie użyteczne konsekwencje. Rekord DNS wskazuje na VPS, a nie na domową sieć. Publicznym adresem staje się adres VPS, więc obserwator wykonujący sprawdzenie IP uzyskuje informacje o wynajętym serwerze, a nie o domowym łączu.

Trzy sposoby na budowę rozwiązania

  1. ssh -R: narzędzie zainstalowane domyślnie po obu stronach, odpowiednie dla pojedynczej usługi lub tymczasowej demonstracji. Nie oferuje panelu sterowania ani zaawansowanej logiki wznawiania połączeń.
  2. frp: niewielki serwer w języku Go (frps) oraz pasujący do niego klient (frpc). Rozwiązanie właściwe dla stałej konfiguracji z wieloma usługami pod jedną nazwą hosta. Ta metoda stanowi główną część niniejszego przewodnika.
  3. Mesh VPN: Tailscale lub samodzielnie utrzymywany serwer WireGuard. Rozwiązanie właściwe, gdy celem jest prywatna komunikacja między własnymi urządzeniami, a nie publikowanie usług w publicznym Internecie.

Wybierz mesh VPN, jeśli celem jest prywatny dostęp z urządzeń, którymi zarządzasz. Tailscale Serve and Funnel opisuje publikowanie usług z sieci tailnet, a samodzielny serwer WireGuard VPN na tym samym VPS pozwala uzyskać ten sam efekt bez udziału zewnętrznego serwera koordynującego. Zapoznaj się z jednym z tych materiałów i pomiń resztę strony. Dalsza część poradnika zakłada, że celem jest uzyskanie publicznej nazwy hosta HTTPS dostępnej dla każdego użytkownika.

Wersja skrócona: ssh -R dla jednej usługi

Załóżmy, że serwer domowy uruchamia aplikację na 127.0.0.1:3000, a użytkownik posiada już dostęp SSH do VPS.

# on the home box
ssh -N \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -R 127.0.0.1:8080:127.0.0.1:3000 \
  tunnel@vps.example.com

-R 127.0.0.1:8080:127.0.0.1:3000 instruuje proces sshd na VPS, aby nasłuchiwał na własnym 127.0.0.1:8080 i przesyłał wszystko, co tam dotrze, do 127.0.0.1:3000 na serwerze domowym. -N oznacza brak uruchamiania powłoki. Dwie opcje ServerAlive sprawiają, że SSH wykrywa zerwane połączenie w ciągu około dziewięćdziesięciu sekund, zamiast zawieszać się na nieistniejącym połączeniu.

Teraz część, która sprawia najwięcej trudności. Ten punkt nasłuchu znajduje się na interfejsie loopback, więc curl http://vps.example.com:8080 z dowolnego innego miejsca zakończy się niepowodzeniem. Oprogramowanie sshd jest dostarczane z GatewayPorts no, co oznacza, że zdalne przekierowanie wiąże się wyłącznie z interfejsem loopback. Nie należy tego naprawiać poprzez ustawienie GatewayPorts yes. Należy pozostawić przekierowanie na loopback i umieścić przed nim nginx, w ten sam sposób, jak w poniższej konfiguracji frp, tak aby port publiczny 443 posiadał certyfikat, a port tunelu nigdy nie był wystawiony na działanie Internetu. Jeśli nie ma pewności, co aktualnie nasłuchuje i na jakim interfejsie, krótki przegląd portów i procesów nasłuchujących w systemie Linux jest wart dziesięciu minut lektury.

Jeśli port jest już zajęty na VPS, ssh wyświetli poniższy komunikat, a ExitOnForwardFailure=yes spowoduje przerwanie działania zamiast nawiązania połączenia bez działającego tunelu:

Warning: remote port forwarding failed for listen port 8080

Typową przyczyną jest poprzednia sesja, która została przerwana bez wykrycia tego przez sshd. Należy ustawić ClientAliveInterval 30 oraz ClientAliveCountMax 3 w pliku /etc/ssh/sshd_config na VPS, aby martwe sesje były usuwane i zwalniały port. Całe polecenie warto umieścić w jednostce systemd z Restart=always i dedykowanym kluczem lub użyć autossh. W przypadku obsługi więcej niż jednej usługi należy przerwać w tym miejscu i użyć frp.

Instalacja frp na VPS z przypięciem wersji

Oprogramowanie frp jest dostarczane jako statyczny plik binarny Go i nie znajduje się w repozytoriach Ubuntu ani Debian, dlatego należy pobrać wydanie i samodzielnie zweryfikować jego sumy kontrolne. Wersję należy przypiąć. Format konfiguracji zmienił się w wersji v0.52.0, a nazwy opcji uległy zmianie, więc przestarzałe poradniki mogą zawierać klucze, których dany plik binarny nie rozpoznaje. Niniejszy przewodnik wykorzystuje v0.71.0, opublikowaną 14 sierpnia 2026.

# on the VPS
FRP_VERSION=0.71.0
ARCH=amd64   # use arm64 if `uname -m` prints aarch64
cd /tmp
curl -fsSLO "https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_${ARCH}.tar.gz"
curl -fsSLO "https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_sha256_checksums.txt"
sha256sum --check --ignore-missing frp_sha256_checksums.txt

sha256sum powinno wyświetlić dokładnie jedną linię:

frp_0.71.0_linux_amd64.tar.gz: OK

--ignore-missing jest wymagane, ponieważ plik sumy kontrolnej obejmuje wszystkie osiemnaście zasobów wydania, a pobrano tylko jeden z nich. Bez tej flagi sha256sum zgłasza brak pozostałych siedemnastu plików i kończy działanie z kodem błędu, co jest interpretowane jako nieudana weryfikacja, mimo że proces przebiegł poprawnie.

# on the VPS
tar xzf "frp_${FRP_VERSION}_linux_${ARCH}.tar.gz"
sudo install -m 755 "frp_${FRP_VERSION}_linux_${ARCH}/frps" /usr/local/bin/frps
sudo useradd --system --no-create-home --shell /usr/sbin/nologin frp
sudo install -d -m 750 -o root -g frp /etc/frp
frps --version

frps --version wyświetla 0.71.0. Na VPS należy umieścić wyłącznie frps. frpc jest przeznaczone dla urządzenia domowego. Instalowanie obu plików binarnych wszędzie prowadzi do sytuacji, w których użytkownicy przypadkowo uruchamiają serwer tunelu w sieci domowej.

Konfiguracja VPS: token, wymuszone TLS, nasłuchiwanie na loopback

Najpierw wygeneruj token. Jest to jedyne zabezpieczenie tunelu przed skanowaniem portów VPS-a przez osoby niepowołane.

# on the VPS
openssl rand -base64 32

Zapisz tę wartość w /etc/frp/frps.toml:

bindAddr = "0.0.0.0"
bindPort = 7000

# Every listener frp creates for a proxy, including the HTTP vhost, stays on loopback.
proxyBindAddr = "127.0.0.1"
vhostHTTPPort = 8080

auth.method = "token"
auth.token = "PASTE_THE_OPENSSL_OUTPUT_HERE"

transport.tls.force = true

webServer.addr = "127.0.0.1"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "PASTE_A_SECOND_SECRET_HERE"

log.level = "info"

Cztery z tych linii odpowiadają za bezpieczeństwo, dlatego przeanalizuj je po kolei.

auth.token musi być zgodne z auth.token po stronie klienta. Bez tego frps zaakceptuje każdego klienta, który znajdzie port 7000, a klient ten będzie mógł opublikować dowolną usługę przez Twój VPS i Twój certyfikat.

transport.tls.force = true odrzuca każde połączenie sterujące, które nie korzysta z TLS (transport layer security). Klienci mają włączone TLS domyślnie od wersji v0.50.0, więc w praktyce nie wiąże się to z żadnym kosztem, a eliminuje ryzyko połączenia starego lub niestandardowego klienta otwartym tekstem bez Twojej wiedzy.

proxyBindAddr = "127.0.0.1" to linia, którą pomija większość poradników, a dzięki której to rozwiązanie jest bezpieczne w ciągłym działaniu. Przenosi ona każdy punkt nasłuchiwania, który frp otwiera w imieniu proxy – zarówno vhost HTTP, jak i każdy remotePort, o który poprosi klient – na interfejs loopback. Internet nie ma bezpośredniego dostępu do tych punktów nasłuchiwania. Jedyną publiczną bramą jest nginx na porcie 443, który konfigurujesz i kontrolujesz.

webServer.addr = "127.0.0.1" chroni panel sterowania przed wystawieniem na interfejs publiczny. Panel ten stanowi pełną mapę Twoich prywatnych usług oraz ruchu w nich występującego, a ponieważ jest zabezpieczony jedynie hasłem HTTP basic auth, nie powinien znajdować się na 0.0.0.0.

Ustaw uprawnienia tak, aby token nie był dostępny do odczytu dla wszystkich, a następnie sprawdź składnię przed uruchomieniem czegokolwiek:

# on the VPS
sudo chown root:frp /etc/frp/frps.toml
sudo chmod 640 /etc/frp/frps.toml
sudo -u frp frps verify -c /etc/frp/frps.toml

Poprawny plik zwróci komunikat:

frps: the configuration file /etc/frp/frps.toml syntax is ok

Uwaga dotycząca formatu, która zaoszczędzi Ci czasu. frp wybiera parser na podstawie rozszerzenia pliku i obsługuje .toml, .yaml, .yml oraz .json. Stare pliki .ini nadal wczytują się przez ścieżkę konwersji legacy, jednak format INI jest uznawany za przestarzały, a nowe opcje są dokumentowane wyłącznie dla TOML. Jeśli poradnik pokazuje sekcję [common] oraz server_addr = x.x.x.x, oznacza to, że powstał przed wersją v0.52.0, a nazwy kluczy nie będą zgodne z zainstalowanym plikiem binarnym.

Uruchamianie frps jako usługa bez uprawnień roota

bindPort to 7000, a vhostHTTPPort to 8080. Obie wartości przekraczają 1024, więc frps nie wymaga uprawnień roota ani użycia CAP_NET_BIND_SERVICE. Z tego powodu nie należy przypisywać vhost do portu 80, lecz przekazać to zadanie do nginx.

Utwórz /etc/systemd/system/frps.service:

[Unit]
Description=frp reverse tunnel server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=frp
Group=frp
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5s
LimitNOFILE=65535
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true

[Install]
WantedBy=multi-user.target
# on the VPS
sudo systemctl daemon-reload
sudo systemctl enable --now frps
sudo journalctl -u frps -n 20 --no-pager

Dziennik powinien wykazać oba nasłuchujące porty; adresy są istotniejsze niż same numery portów:

frps tcp listen on 0.0.0.0:7000
http service listen on 127.0.0.1:8080

ProtectSystem=strict ustawia cały system plików w tryb tylko do odczytu dla tej usługi. frps obsługuje to poprawnie, ponieważ domyślnie wysyła logi na standardowe wyjście, skąd przechwytuje je journald. Jeśli ustawisz log.to na ścieżkę do pliku, usługa nie będzie mogła w nim zapisywać, dopóki nie dodasz odpowiedniej linii ReadWritePaths=, dlatego należy pozostawić ustawienie domyślne.

Firewall: otwórz jeden port, nie zakres

# on the VPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 7000/tcp
sudo ufw enable
sudo ufw status numbered

Cztery reguły, z których jedna istnieje wyłącznie na potrzeby odnawiania certyfikatów. 22 to SSH. 80 przekierowuje na 443 i odpowiada na wyzwanie ACME (automatic certificate management environment). 443 obsługuje każdą tunelowaną aplikację. 7000 to port sterujący frp i jest to jedyny port, z którym klient musi się połączyć.

Poradniki zalecające otwarcie zakresu, takiego jak sudo ufw allow 20000:30000/tcp, opisują inny model projektowy, w którym każda usługa zajmuje własny publiczny port TCP. Tutaj nie jest to potrzebne, ponieważ cały ruch trafia na 443, a frp kieruje go na podstawie nazwy hosta. Jeśli w przyszłości zaistnieje potrzeba udostępnienia jednego, faktycznie publicznego portu TCP, przywróć proxyBindAddr do 0.0.0.0 i dodaj ograniczenia, aby klient mógł korzystać wyłącznie z nazwanych portów:

allowPorts = [
  { start = 20000, end = 20010 }
]
maxPortsPerClient = 5

Większość dostawców oferuje również sieciowy firewall w panelu sterowania, niezależny od ufw na serwerze. Reguła, która wygląda poprawnie w sudo ufw status, a mimo to powoduje przekroczenie czasu oczekiwania, jest zazwyczaj blokowana właśnie tam. Reguły ufw faktycznie wymagane przez VPS omawiają konfigurację typu default-deny, którą zakłada ta sekcja.

Terminacja HTTPS na VPS przy użyciu rzeczywistego certyfikatu

Skieruj rekord A dla home.example.com na publiczny adres IP serwera VPS. Nie na adres domowy. Dom nie posiada adresu, na który można wskazać, co stanowi problem, który właśnie rozwiązujesz.

# on the VPS
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx

Utwórz /etc/nginx/sites-available/home.example.com, zaczynając od bloku dla portu 80, aby certbot miał odpowiedni server_name do pracy:

server {
    listen 80;
    listen [::]:80;
    server_name home.example.com;
    location / { return 404; }
}
# on the VPS
sudo ln -s /etc/nginx/sites-available/home.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot certonly --nginx -d home.example.com

nginx -t wyświetla nginx: configuration file /etc/nginx/nginx.conf test is successful, jeśli pliki zostały poprawnie przeanalizowane. Uruchamiaj to polecenie przed każdym przeładowaniem. nginx utrzymuje działanie starej konfiguracji w przypadku niepowodzenia przeładowania, więc błędna edycja wygląda jak zmiana, która nie przyniosła żadnego efektu.

Obsługa WebSocket wymaga mapowania na poziomie http. Umieść je w /etc/nginx/conf.d/upgrade.conf:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Teraz zastąp plik witryny właściwą konfiguracją:

server {
    listen 80;
    listen [::]:80;
    server_name home.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name home.example.com;

    ssl_certificate     /etc/letsencrypt/live/home.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/home.example.com/privkey.pem;

    client_max_body_size 512m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        $connection_upgrade;
        proxy_read_timeout 3600s;
    }
}

proxy_set_header Host $host; nie jest tutaj opcjonalny. HTTP vhost w frp kieruje ruch na podstawie nagłówka Host, dopasowując go do listy customDomains w konfiguracji klienta. Pominięcie tego nagłówka spowoduje, że nginx wyśle Host: 127.0.0.1, frp nie znajdzie proxy dla tej nazwy, a użytkownik otrzyma błąd 404 z frp zamiast strony z aplikacji. Wyjaśnienie każdej linii bloku reverse proxy w nginx opisuje funkcje pozostałych nagłówków.

# on the VPS
sudo nginx -t && sudo systemctl reload nginx
sudo certbot renew --dry-run

Testowe uruchomienie (dry run) potwierdza, że odnawianie zadziała za dziewięćdziesiąt dni, kiedy nie będziesz już monitorować procesu. Wymaga to dostępności portu 80, dlatego konieczna jest odpowiednia reguła ufw.

Strona domowa: frpc jako usługa bez uprawnień roota

Zainstaluj frpc na domowym serwerze dokładnie w taki sam sposób, jak zainstalowano frps, zachowując tę samą wersję oraz krok weryfikacji sumy kontrolnej. Następnie utwórz użytkownika frp oraz katalog /etc/frp. Utwórz plik /etc/frp/frpc.toml:

serverAddr = "vps.example.com"
serverPort = 7000

auth.method = "token"
auth.token = "PASTE_THE_SAME_TOKEN_HERE"

transport.tls.enable = true
loginFailExit = false

proxies = [
  { name = "home-app", type = "http", localIP = "127.0.0.1", localPort = 3000, customDomains = ["home.example.com"] }
]

Kolejność kluczy w tym pliku ma znaczenie, nie tylko estetyczne. Format TOML przypisuje każdy klucz znajdujący się po nagłówku sekcji do tej sekcji, więc ustawienie globalne, takie jak serverAddr, umieszczone pod nagłówkiem proxy, zostanie potraktowane jako ustawienie proxy i zignorowane przez frp. Zapisanie listy proxy jako tablicy w jednej linii, tak jak pokazano powyżej, pozwala uniknąć tego błędu: każdy klucz najwyższego poziomu pozostaje jednoznacznie przypisany do konfiguracji globalnej.

type = "http" kieruje ten ruch przez vhost listener zamiast zajmować osobny publiczny port TCP, dlatego zapora sieciowa wymagała tylko czterech reguł. customDomains musi zawierać nazwę hosta, którą nginx przekazuje w nagłówku Host, zatem jest to home.example.com, a nigdy adres IP serwera VPS.

loginFailExit = false ma większe znaczenie, niż się wydaje. Wartością domyślną jest true, co powoduje wyjście frpc, jeśli pierwsza próba logowania zakończy się niepowodzeniem. Na domowym serwerze, który kończy proces uruchamiania przed nawiązaniem połączenia z dostawcą internetu, usługa ta pozostanie wyłączona do czasu ręcznej interwencji. Ustawienie wartości na false sprawia, że frpc ponawia próby do momentu, aż VPS odpowie.

Utwórz plik /etc/systemd/system/frpc.service:

[Unit]
Description=frp reverse tunnel client
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=frp
Group=frp
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
Restart=always
RestartSec=10s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
# on the home box
sudo chown root:frp /etc/frp/frpc.toml
sudo chmod 640 /etc/frp/frpc.toml
sudo -u frp frpc verify -c /etc/frp/frpc.toml
sudo systemctl daemon-reload
sudo systemctl enable --now frpc
sudo journalctl -u frpc -n 20 --no-pager

Klient, który nawiązał połączenie, loguje identyfikator uruchomienia:

login to server success, get run id [3a1f9c2b7d4e5f60]

Otwórz https://home.example.com w przeglądarce, aby uzyskać dostęp do aplikacji działającej na 127.0.0.1:3000 w sieci domowej. Użycie Restart=always na kliencie jest celowe: połączenia domowe bywają zrywane, a usługa powinna powrócić do działania automatycznie.

Ograniczenie dostępu do panelu sterowania

Dzięki webServer.addr = "127.0.0.1" panel sterowania odpowiada wyłącznie na poziomie serwera VPS. Dostęp do niego z poziomu laptopa uzyskuje się poprzez lokalne przekierowanie portu, zamiast otwierania portu publicznego:

# on your laptop
ssh -N -L 7500:127.0.0.1:7500 you@vps.example.com

Otwórz http://127.0.0.1:7500 i zaloguj się przy użyciu webServer.user oraz webServer.password z frps.toml. Strona wyświetla listę wszystkich połączonych klientów oraz liczniki ruchu dla każdego proxy, co stanowi najszybszy sposób na sprawdzenie, czy serwer domowy jest aktualnie połączony. Po zamknięciu sesji ssh panel sterowania ponownie staje się niedostępny.

Czego tunel nie robi

Należy przeczytać tę sekcję dwukrotnie, ponieważ dotyczy ona najczęstszych przyczyn naruszeń bezpieczeństwa. Tunel sprawia, że prywatna usługa staje się dostępna z publicznego Internetu. Nie uwierzytelnia on osób, które uzyskują do niej dostęp. Gdy https://home.example.com zostanie rozwiązane, skanery znajdą usługę w ciągu kilku dni, niezależnie od tego, czy nazwa hosta została komukolwiek udostępniona. Rejestry przejrzystości certyfikatów (Certificate Transparency logs) publikują każdą nazwę hosta, dla której wystawiono certyfikat, więc nazwa staje się publiczna w momencie, gdy certbot zakończy działanie sukcesem.

Każda wystawiona usługa musi posiadać własny mechanizm uwierzytelniania. Jeśli aplikacja posiada poprawny system logowania z ograniczeniem liczby żądań (rate limiting), jest to rozwiązanie wystarczające. Jeśli logowanie opiera się na jednym współdzielonym haśle lub w ogóle nie istnieje, należy umieścić przed aplikacją proxy uwierzytelniające na serwerze VPS. Rozwiązanie typu oauth2-proxy umieszczone przed aplikacją jest standardową praktyką; integruje się ono między nginx a vhost frp bez konieczności wprowadzania zmian w obu końcach tunelu.

Token w frps.toml chroni tunel, a nie same aplikacje. Zapobiega on rejestracji własnego proxy przez osoby trzecie na serwerze VPS. Nie ma on wpływu na żądania przychodzące na port 443 dla nazwy hosta, która została celowo opublikowana.

Warto wyrobić sobie dwa nawyki. Należy okresowo zmieniać token poprzez edycję obu plików i restart obu usług, ponieważ token nie wygasa automatycznie. Należy również dbać o aktualność frp: ten plik binarny stanowi publiczny punkt wejścia, a informacje o wydaniu v0.71.0 wspominają o błędzie typu panic w serwerze wywoływanym przez nieprawidłową wartość przesłaną z klienta. Jest to typ błędu, który należy wyeliminować poprzez aktualizację, zamiast próbować go analizować.

Tryby awarii i towarzyszące im komunikaty

Klient nie nawiązuje połączenia. journalctl -u frpc powtarza connect to server error:, po czym następuje przekroczenie czasu oczekiwania na połączenie (dial timeout). Żaden ruch nie dociera do portu 7000. Należy sprawdzić ufw na VPS, następnie zaporę sieciową dostawcy w panelu sterowania, a na końcu potwierdzić poprawność rozpoznawania nazwy za pomocą getent hosts vps.example.com.

Token jest nieprawidłowy. Klient zgłasza to bezpośrednio:

login to the server failed: token in login doesn't match token from configuration

Należy ponownie skopiować token. Znaki nowej linii na końcu lub $ w niecytowanym ciągu znaków powłoki, który został rozwinięty do wartości pustej, są przyczyną niemal wszystkich takich przypadków. Dlatego dane wyjściowe openssl rand -base64 32 powinny być ujęte w cudzysłów w pliku TOML.

Tunel działa, ale przeglądarka zwraca błąd 404. frpc odnotował poprawne logowanie, a panel sterowania wyświetla proxy, mimo to strona zwraca błąd 404 bez załadowania stylów aplikacji. Oznacza to, że frp nie posiada konfiguracji proxy dla danego nagłówka Host. Należy przetestować vhost bezpośrednio na VPS, z pominięciem nginx oraz TLS:

# on the VPS
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: home.example.com' http://127.0.0.1:8080/

Błąd 404 po wykonaniu tego polecenia oznacza, że customDomains jest nieprawidłowe. Każdy inny kod oznacza, że żądanie nie otrzymało poprawnego nagłówka Host od nginx.

Błąd 502 z nginx. nginx odpowiada, natomiast frp nie. sudo ss -lntp | grep 8080 na VPS powinno wykazać, że frps nasłuchuje na 127.0.0.1:8080. Brak danych wyjściowych oznacza, że frps jest zatrzymany lub vhostHTTPPort nie jest ustawione w frps.toml.

Aplikacja rozpoznaje każdego odwiedzającego jako użytkownika lokalnego. Aplikacja rejestruje 127.0.0.1 dla każdego żądania. frp ustawia X-Forwarded-For, a nginx dopisuje do niego kolejne wartości, dlatego rzeczywisty adres klienta znajduje się w tym nagłówku. Należy skonfigurować aplikację tak, aby ufała temu nagłówkowi. Nie należy pomijać tego kroku, jeśli aplikacja stosuje limitowanie szybkości (rate limiting) według adresu IP, ponieważ w przeciwnym razie wszyscy użytkownicy z Internetu będą traktowani jako jeden podmiot.

Długie żądania są przerywane po 60 sekundach. Przesyłanie plików lub strumieniowanie odpowiedzi zostaje przerwane w trakcie. Jest to domyślny limit proxy_read_timeout w nginx, a nie w tunelu. Powyższy blok zwiększa ten limit do 3600 sekund. client_max_body_size to odpowiadający mu limit rozmiaru przesyłanych danych; domyślna wartość 1 MB powoduje odrzucanie większych treści z błędem 413.

Wszystko działa, ale przestaje po restarcie routera. Restart=always w jednostce frpc wraz z loginFailExit = false rozwiązują ten problem. Należy zweryfikować stan za pomocą sudo systemctl is-enabled frpc, które musi zwrócić enabled.

FAQ

Skąd mam wiedzieć, czy jestem za CGNAT?

Porównaj adres WAN na stronie administracyjnej routera z tym, co wskazuje curl -4 -s https://ifconfig.me wewnątrz tej samej sieci. Jeśli adresy są różne, a adres WAN routera znajduje się w zakresie 100.64.0.0/10, dostawca internetu stosuje carrier-grade NAT. Ten zakres to przestrzeń adresowa współdzielona zgodnie z RFC 6598, przeznaczona właśnie do tego celu. Niektórzy dostawcy używają po stronie WAN zakresu 10.0.0.0/8, co oznacza to samo. Jeśli oba adresy są identyczne, posiadasz publiczny adres IP: przekieruj port i gotowe.

Czy do tunelu zwrotnego potrzebuję nazwy domeny?

Dla opisanej tutaj konfiguracji HTTPS – tak. Certyfikat jest wystawiany dla nazwy hosta, a wirtualny host HTTP w frp kieruje żądania na podstawie nagłówka Host, więc obie strony muszą uzgodnić wspólną nazwę. Surowe proxy TCP na wybranym porcie działa z samym adresem IP serwera VPS bez domeny, ale wtedy nie ma certyfikatu ani routingu po nazwie hosta, więc jeden port publiczny obsługuje dokładnie jedną usługę.

Czy uruchomienie frp na publicznym VPS jest bezpieczne?

Jest bezpieczne, gdy jedynym wystawionym elementem jest port kontrolny, który wymaga uwierzytelnienia. Ustaw auth.token na losową wartość po obu stronach oraz transport.tls.force = true na serwerze. Następnie skonfiguruj proxyBindAddr = "127.0.0.1" tak, aby żaden port otwierany przez frp dla proxy nie był wystawiony bezpośrednio do Internetu, a panel sterowania (dashboard) utrzymuj na webServer.addr = "127.0.0.1", dostępnym wyłącznie przez lokalne przekierowanie SSH. Aktualizuj plik binarny, gdy pojawiają się nowe wersje, ponieważ jest to proces nasłuchujący na publicznym adresie.

Dlaczego nikt nie może połączyć się z moim przekierowanym portem ssh -R?

sshd domyślnie używa GatewayPorts no, więc zdalne przekierowanie wiąże się tylko z interfejsem loopback serwera VPS. curl uruchomione na samym VPS działa, natomiast curl z dowolnego innego miejsca kończy się przekroczeniem czasu oczekiwania. Poprawnym rozwiązaniem jest pozostawienie przekierowania na loopback i umieszczenie nginx na porcie 443 przed nim. Ustawienie GatewayPorts yes publikuje surowy port bez certyfikatu i bez TLS, co jest gorszym rozwiązaniem niż problem, który ma rozwiązać.

Czy powinienem użyć frp, czy sieci VPN typu mesh, takiej jak Tailscale lub WireGuard?

Użyj sieci VPN typu mesh, gdy dostęp potrzebny jest tylko Twoim urządzeniom, ponieważ wtedy nic nie jest publikowane i nie istnieje publiczna nazwa hosta, którą można przeskanować. Użyj frp, gdy potrzebujesz publicznego adresu HTTPS, który może otworzyć dowolna przeglądarka, na przykład dla odbiornika webhook lub strony udostępnianej osobom, które nie zainstalują klienta VPN. Oba rozwiązania mogą współistnieć na jednym serwerze VPS, na różnych portach, realizując odmienne zadania.

#frp#cgnat#nat#tunnel#reverse-proxy