SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-28

Jak skonfigurować własny serwer VPN WireGuard na VPS

Instrukcja konfiguracji WireGuard na Linux VPS. Dowiedz się jak poprawnie wygenerować klucze, ustawić wg0.conf, włączyć IP forwarding oraz uniknąć błędów handshake i problemów z DNS.

Co budujesz

Własny serwer VPN WireGuard wymaga około czterdziestu linii konfiguracji: jednej pary kluczy, jednego pliku interfejsu, jednego wpisu sysctl, jednej reguły NAT oraz otwarcia portu w zaporze sieciowej. Instalacja jest trywialna, dlatego większość tego przewodnika poświęcono kwestiom awarii, uprawnieniom do kluczy, AllowedIPs, przekazywaniu pakietów oraz DNS.

WireGuard to tunel warstwy 3 działający w jądrze systemu, obecny w głównej linii kodu od wersji Linux 5.6, dzięki czemu Ubuntu 24.04 i Debian 13 dostarczają go bez konieczności instalacji zewnętrznych modułów. Brak negocjacji szyfrów, urzędów certyfikacji oraz etapów logowania hasłem: peer jest definiowany przez klucz publiczny oraz adresy IP, z których może korzystać. Pakiet, który nie przejdzie weryfikacji MAC, jest odrzucany bez odpowiedzi, co sprawia, że port nie reaguje na skanowanie. Druga strona medalu: nie istnieje serwer autoryzacji, więc odebranie dostępu wymaga usunięcia peera z serwera.

Sprawdź najpierw wirtualizację

WireGuard wymaga jądra, do którego można załadować moduł; na VPS z KVM działa to bez dodatkowej konfiguracji. W przypadku wirtualizacji kontenerowej współdzielącej jądro hosta, takiej jak OpenVZ czy LXC, pierwsze polecenie kończy się błędem RTNETLINK answers: Operation not supported, a rozwiązaniem zastępczym jest implementacja przestrzeni użytkownika wireguard-go. Sprawdź to najpierw za pomocą sudo modprobe wireguard && echo ok.

Generowanie kluczy bez ryzyka ich ujawnienia

Plik /etc/wireguard/server.key dostępny do odczytu dla wszystkich jest równoznaczny z brakiem VPN. Powszechnie stosowana linia umask 077 && wg genkey | sudo tee ... jest zawodna, ponieważ sudo stosuje własną maskę umask do pliku tworzonego przez tee. Należy ustawić uprawnienia w sposób jawny.

sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.key

Wygeneruj parę kluczy klienta w ten sam sposób. wg genpsk dodaje opcjonalny klucz współdzielony (pre-shared key), który należy umieścić w jednej linii w każdej z konfiguracji.

Interfejs serwera: /etc/wireguard/wg0.conf

[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>

[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32

chmod 600 go; ostrzeżenie przy starcie informujące, że plik jest dostępny dla wszystkich, oznacza pominięcie tego kroku. Address to adres serwera wewnątrz tunelu, zawierający maskę całej podsieci VPN. Należy wybrać zakres, który nie występuje w użyciu; 192.168.1.0/24 koliduje z połową domowych routerów, za którymi znajdują się klienci, co powoduje cichą utratę łączności tunelowej na rzecz trasy lokalnej.

AllowedIPs peera po stronie serwera to /32, czyli jeden adres tunelu przypisany do danego klienta. Przypisanie dwóm peerom tego samego adresu AllowedIPs spowoduje, że ruch będzie kierowany do tego, który został skonfigurowany jako ostatni, a pierwszy przestanie otrzymywać pakiety bez żadnego komunikatu o błędzie. Pozostaw SaveConfig nieustawione, w przeciwnym razie wg-quick down nadpisze ten plik na podstawie bieżącego stanu.

Przekształcenie serwera w router

Serwer z systemem Linux domyślnie odrzuca pakiety, które nie są do niego zaadresowane. Domyślnie wyłączone jest zarówno przekazywanie pakietów (forwarding), jak i mechanizm source NAT.

printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
  | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward

Samo polecenie sysctl -w działa tylko do następnego restartu, po czym przestaje funkcjonować. Mechanizm NAT wymaga wskazania interfejsu wyjściowego (egress), czyli karty sieciowej zapewniającej dostęp do Internetu, a nie wg0. Nie należy zakładać użycia eth0; nazwę interfejsu należy pobrać z ip route show default, ponieważ współczesne obrazy systemów używają nazw takich jak enp1s0 lub ens3.

Firewall: port i ścieżka przekierowania

Jeden plik nftables obsługuje filtry oraz NAT. Wykonanie /etc/nftables.conf powoduje usunięcie istniejącego zestawu reguł, więc należy pominąć ten krok na serwerze zarządzanym już przez ufw lub Docker.

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    ct state established,related accept
    iif lo accept
    tcp dport 22 accept
    udp dport 51820 accept
  }
  chain forward {
    type filter hook forward priority filter; policy drop;
    ct state established,related accept
    iifname "wg0" oifname "enp1s0" accept
  }
}

table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
  }
}

Zastosuj konfigurację za pomocą sudo systemctl enable --now nftables, utrzymując otwartą drugą sesję SSH: policy drop wraz z literówką w regule SSH spowoduje utratę dostępu do serwera. Zwróć uwagę, czego nie zezwala łańcuch forward, wg0 do wg0. Węzły uzyskują dostęp do Internetu, ale nie do siebie nawzajem; dodaj iifname "wg0" oifname "wg0" accept w przypadku VPN typu peer-to-peer. Ten sam łańcuch określa, do czego węzeł może uzyskać dostęp na samym serwerze, co ma znaczenie, gdy maszyna pełni jednocześnie rolę zdalnego środowiska programistycznego z Claude Code w tmux i nie chcesz wystawiać tej części publicznie.

W systemie z ufw: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" w /etc/default/ufw oraz reguła *nat POSTROUTING MASQUERADE na początku pliku /etc/ufw/before.rules.

Uruchamianie za pomocą systemd

sudo systemctl enable --now wg-quick@wg0
sudo wg show

wg-quick tworzy interfejs, dodaje adresy oraz instaluje trasy wywiedzione z AllowedIPs. enable --now stanowi istotną część: ręcznie uruchomione wg-quick up wg0 znika po kolejnym restarcie, a aktualizacje jądra wymuszają restarty. Jednostka, która nie uruchomi się ponownie po takim restarcie, pozostaje w stanie uśpienia, dopóki ktoś nie spróbuje nawiązać połączenia. Dlatego plik typu drop-in OnFailure= w wg-quick@wg0, wskazujący na własny serwer ntfy, jest najtańszym sposobem na otrzymanie powiadomienia na telefon, zamiast dowiadywać się o awarii od zablokowanego użytkownika.

Konfiguracja klienta i ustawienie, w którym wszyscy popełniają błędy

[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

AllowedIPs wykonuje jednocześnie dwa różne zadania, a ich mylenie jest źródłem większości problemów z WireGuard.

W ruchu wychodzącym jest to tablica routingu. Pakiet, którego miejsce docelowe pasuje do AllowedIPs peer-a, jest szyfrowany i wysyłany do tego peer-a. 0.0.0.0/0, ::/0 kieruje wszystko przez tunel, tworząc pełny tunel, w którym serwer staje się domyślną bramą. Tunel dzielony (split tunnel) to węższa lista: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 przesyła ruch VPN oraz ruch do jednej sieci prywatnej za serwerem, podczas gdy cały pozostały ruch zachowuje swoje lokalne trasy. Ta ograniczona lista pozwala całkowicie odizolować usługi od publicznego Internetu, dzięki czemu prywatna instancja Nextcloud na VPS powiązana z adresem tunelu lub maszyny wirtualne w laboratorium z zagnieżdżoną wirtualizacją działające na tym samym hoście pozostają dostępne dla peer-ów i niewidoczne dla nikogo innego.

W ruchu przychodzącym jest to lista kontroli dostępu (ACL). Odszyfrowany pakiet od peer-a, którego adres źródłowy nie znajduje się w AllowedIPs tego peer-a, jest odrzucany. Dlatego serwer zawiera 10.8.0.2/32 dla laptopa: wpis 0.0.0.0/0 w tym miejscu pozwoliłby klientowi na podszycie się pod dowolny adres wewnątrz tunelu.

PersistentKeepalive służy klientom znajdującym się za NAT, gdzie router utrzymuje mapowanie UDP tylko podczas przepływu pakietów. Po wygaśnięciu mapowania serwer nie może już nawiązać połączenia z klientem. PersistentKeepalive = 25 utrzymuje mapowanie otwarte; należy ustawić tę opcję na kliencie, a nie na serwerze z publicznym adresem IP.

DNS i wyciek, którego nikt nie zauważa

Przy użyciu AllowedIPs = 0.0.0.0/0 i braku linii DNS =, klient zachowuje resolver uzyskany z sieci lokalnej, czyli routera w kawiarni pod adresem 192.168.1.1. Ta trasa jest bardziej szczegółowa niż trasa domyślna, więc zapytania DNS opuszczają urządzenie przez łącze lokalne w postaci otwartego tekstu, podczas gdy cały pozostały ruch jest tunelowany. Ruch jest prywatny, ale lista nazw – nie.

Istnieją dwie uczciwe opcje. Skieruj DNS na publiczny resolver (DNS = 9.9.9.9), a zapytania przejdą przez tunel i wyjdą z serwera, choć resolver nadal będzie je widział. Alternatywnie uruchom unbound lub dnsmasq powiązane z 10.8.0.1, ustaw DNS = 10.8.0.1 i dodaj udp dport 53 iifname "wg0" accept do łańcucha wejściowego (input chain), ustaw tę linię i zapomnij o resolverze – wtedy nic nie zostanie rozwiązane.

Na klientach Linux wg-quick stosuje DNS poprzez resolvconf; jeśli jej brakuje, otrzymasz resolvconf: command not found. Zainstaluj openresolv lub ustaw PostUp = resolvectl dns %i 10.8.0.1 na kliencie korzystającym z systemd-resolved.

Dodawanie i usuwanie partnerów (peers) bez przerywania tunelu

Restartowanie interfejsu w celu dodania użytkownika rozłącza wszystkich aktualnie połączonych. Należy dopisać blok [Peer] do pliku wg0.conf, a następnie przeładować zestaw partnerów w działającym tunelu.

sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'

Polecenie wg-quick strip wyświetla konfigurację bez kluczy specyficznych dla wg-quick (Address, DNS, PostUp), a syncconf nanosi różnice bez przerywania aktywnych sesji. Aktualizowane są wyłącznie dane partnerów: zmiana Address nadal wymaga pełnego cyklu down/up. Aby cofnąć dostęp, należy użyć sudo wg set wg0 peer <public key> remove, a następnie usunąć odpowiedni blok z pliku, w przeciwnym razie powróci on przy następnym przeładowaniu.

Tryby awarii i towarzyszące im komunikaty

Uścisk dłoni (handshake) nigdy się nie kończy. wg show wyświetla partnera bez latest handshake, a klient loguje:

Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)

Pakiety nie docierają lub nie są akceptowane. W kolejności: czy port UDP 51820 jest otwarty na firewallu VPS oraz na firewallu sieciowym dostawcy (osobne ustawienie w większości paneli); czy adres Endpoint i port są poprawne; czy klucze nie zostały pomylone. Klucz w bloku [Peer] klienta musi być kluczem publicznym serwera, i odwrotnie. Wklejenie klucza prywatnego lub własnego klucza publicznego klienta daje dokładnie ten objaw. sudo tcpdump -ni any udp port 51820 na serwerze pokazuje, czy pakiety w ogóle docierają. Moduł jądra domyślnie nic nie loguje; komunikaty WireGuard pojawiają się w dmesg dopiero po włączeniu dynamicznego debugowania (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control). Po jego włączeniu niedopasowanie kluczy objawia się jako odrzucenie z powodu nieprawidłowego MAC.

Uścisk dłoni działa, brak dostępu do Internetu. ping 10.8.0.1 kończy się sukcesem, ale ping 1.1.1.1 przekracza limit czasu: brakuje przekierowania (forwarding) lub NAT. Sprawdź, czy sysctl net.ipv4.ip_forward ma wartość 1, a następnie obserwuj liczniki podczas pingowania przez klienta za pomocą sudo nft list ruleset lub sudo iptables -t nat -L POSTROUTING -n -v. Zerowa liczba pakietów na regule maskarady oznacza błędną nazwę interfejsu wyjściowego; rosnący licznik przy braku odpowiedzi wskazuje na politykę łańcucha forward.

Internet działa, nazwy nie są rozwiązywane. ping 1.1.1.1 kończy się sukcesem, a curl https://example.com zwraca Could not resolve host. Brakuje linii DNS lub wskazuje ona na resolver nieosiągalny z wnętrza tunelu.

Niektóre strony HTTPS zawieszają się. SSH i ping działają poprawnie; duże strony ładują się w nieskończoność. To problem z MTU ścieżki: tunel dodaje narzut, a któryś z węzłów pośrednich odrzuca zbyt duże pakiety bez wysłania komunikatu ICMP. Zmniejsz MTU w sekcji [Interface] klienta, wypróbuj 1420, następnie 1380, a potem 1280. Jeśli obniżenie MTU eliminuje zawieszanie, ale przepustowość nadal jest niesatysfakcjonująca, przestań zgadywać wartości i wykonaj wyznaczenie rzeczywistego MTU ścieżki metodą bisekcji i ograniczenie TCP MSS, co pozwoli również wykluczyć przyczyny niezwiązane z tunelem.

Interfejs odmawia uruchomienia. Address already in use oznacza, że inny proces zajmuje port UDP 51820. Cannot find device wg0 po nieudanym up zazwyczaj oznacza, że konfiguracja została odrzucona; sprawdź journalctl -u wg-quick@wg0 -n 50.

Migracja ze Streisand lub OpenVPN

Projekt Streisand nie jest już utrzymywany, a jego repozytorium zostało zarchiwizowane. Uruchamianie VPN przy użyciu porzuconych narzędzi automatyzacji stanowi narastający problem bezpieczeństwa. Nie istnieje ścieżka aktualizacji w miejscu, a infrastruktura klucza publicznego (PKI) OpenVPN nie podlega konwersji: WireGuard nie korzysta z certyfikatów, urzędów certyfikacji (CA) ani dat wygaśnięcia, dlatego każdy klient otrzymuje nową parę kluczy.

Migrację należy przeprowadzić równolegle; WireGuard działający na UDP 51820 może współistnieć na tym samym serwerze z OpenVPN na porcie 1194. Należy uruchomić wg0, przenosić klientów pojedynczo, a następnie zatrzymać starą usługę. Model uwierzytelniania użytkowników i haseł oraz mechanizm unieważniania certyfikatów z OpenVPN nie mają zastosowania w WireGuard; jeśli wymagane są konta lub ścieżka audytu, należy zaimplementować te funkcje w warstwie powyżej WireGuard.

Kopie zapasowe, aktualizacje i ograniczenia skalowalności

/etc/wireguard to serwer. Wykonaj jego kopię zapasową (sudo tar czf wg-backup.tgz -C /etc wireguard, tryb 600, przechowywana poza serwerem), a w razie awarii odtworzysz go na nowym VPS w kilka minut. Utrata klucza prywatnego serwera wymusza ponowne wydanie konfiguracji wszystkich klientów, ponieważ klienci weryfikują klucz publiczny serwera. Aktualizacje to standardowe apt upgrade oraz restart w przypadku zmian w jądrze systemu, a wg-quick@wg0 uruchamia się automatycznie, jeśli zostało wcześniej włączone.

Stan każdego z węzłów jest niewielki, a operacje kryptograficzne odbywają się w jądrze systemu, więc ograniczeniem jest wydajność procesora i przepustowość łącza VPS, a nie parametry tej konfiguracji. Wartość tę należy zmierzyć za pomocą iperf3 wewnątrz tunelu, zamiast polegać na deklarowanych danych. Problemem przy dużej skali jest administracja. Każdy węzeł wymaga unikalnego adresu IP w tunelu, a ręczna edycja sześćdziesięciu bloków [Peer] prowadzi do powstawania duplikatów AllowedIPs: konfiguracje należy generować skryptem. Jeden serwer to jeden punkt końcowy UDP i jeden punkt awarii, a WireGuard nie posiada wbudowanego klastrowania: redundancja wymaga drugiego serwera z własnymi kluczami. Rotacja kluczy pozostaje procesem ręcznym, dlatego należy prowadzić ewidencję posiadaczy kluczy oraz procedurę ich unieważniania. Gdy taka ewidencja przestaje mieścić się w pliku tekstowym, standardowym rozwiązaniem jest wdrożenie płaszczyzny sterowania (control plane) nad tą samą płaszczyzną danych w jądrze, a samodzielnie hostowany serwer NetBird automatyzuje przydzielanie adresów, dystrybucję węzłów i klucze instalacyjne, które w przeciwnym razie trzeba konfigurować ręcznie. Jeśli uruchomienie własnej płaszczyzny sterowania to zbyt duże obciążenie, Tailscale oferuje usługę hostowaną, a jej darmowy plan obejmuje sześciu użytkowników z nielimitowaną liczbą urządzeń, co w większości zastosowań prywatnych jest wystarczające. Powyżej tego limitu opłaty zależą od liczby osób, a nie maszyn, więc koszt dla gospodarstwa domowego lub małego zespołu zależy od liczby użytkowników posiadających loginy, a nie od liczby węzłów, które trzeba by ręcznie wpisywać do wg0.conf. W takim modelu ewidencja AllowedIPs dla split-tunnelingu sprowadza się do ogłaszania prywatnych zakresów adresowych przez router podsieci, co konfiguruje się raz na jednym VPS i zatwierdza centralnie, zamiast wklejać do każdego pliku klienta. Decyzja o tym, czy warto przejść na takie rozwiązanie, zależy od zakresu dostępu hostowanej płaszczyzny sterowania; nie posiada ona kluczy szyfrujących ruch, choć decyduje o tym, które węzły nawiązują ze sobą połączenia.

Wszystko to wymaga systemu Linux pod pełną kontrolą, publicznego adresu IP, jądra z możliwością ładowania modułów oraz własnej konfiguracji zapory sieciowej.

FAQ

Dlaczego uzgadnianie połączenia WireGuard nigdy się nie kończy?

wg show wyświetlenie peera bez latest handshake oznacza, że pakiety nie docierają lub nie są akceptowane. Sprawdź port UDP 51820 na firewallu VPS oraz na zewnętrznym firewallu sieciowym dostawcy, potwierdź Endpoint hosta i portu, a następnie sprawdź, czy klucze nie zostały zamienione – blok [Peer] klienta musi zawierać publiczny klucz serwera. sudo tcpdump -ni any udp port 51820 na serwerze pokazuje, czy pakiety w ogóle docierają; dmesg raportuje błędy uzgadniania WireGuard tylko po włączeniu dynamicznego debugowania (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control), wtedy niedopasowanie kluczy pojawi się jako odrzucenie z powodu invalid-MAC.

Tunel łączy się, ale nie mam dostępu do Internetu. Czego brakuje?

ping 10.8.0.1 działające przy jednoczesnym przekroczeniu czasu oczekiwania ping 1.1.1.1 wskazuje na problemy z forwardingiem lub NAT. Potwierdź, że sysctl net.ipv4.ip_forward zawiera 1 i że ustawienie to jest zapisane w /etc/sysctl.d/, a nie tylko wprowadzone przez sysctl -w, które znika po restarcie. Następnie sprawdź regułę maskarady, wskazując właściwy interfejs wyjściowy z ip route show default, enp1s0 lub ens3, rzadziej eth0.

Czy potrzebuję linii DNS = w konfiguracji klienta?

Przy pełnym tunelu bez linii DNS = klient zachowuje resolver poznany z sieci lokalnej, a zapytania te wychodzą otwartym tekstem przez łącze lokalne, podczas gdy cała reszta ruchu jest tunelowana. Skieruj DNS na publiczny resolver lub uruchom unbound/dnsmasq powiązane z 10.8.0.1 i otwórz udp dport 53 iifname "wg0" w łańcuchu input.

Co tak naprawdę kontroluje AllowedIPs?

Pełni dwie funkcje. W ruchu wychodzącym jest to tablica routingu: ruch pasujący do AllowedIPs peera jest szyfrowany i wysyłany do tego peera. W ruchu przychodzącym jest to lista kontroli dostępu: odszyfrowany pakiet, którego źródło znajduje się poza AllowedIPs danego peera, jest odrzucany. Dlatego po stronie serwera wymienia się /32 dla każdego klienta, podczas gdy po stronie klienta można użyć 0.0.0.0/0.

Czy WireGuard zadziała na każdym VPS?

Na VPS typu KVM działa z modułem wewnątrz jądra bez dodatkowej konfiguracji. W wirtualizacji kontenerowej współdzielącej jądro hosta, takiej jak OpenVZ lub LXC, modprobe wireguard kończy się błędem Operation not supported, a rozwiązaniem zastępczym jest implementacja przestrzeni użytkownika wireguard-go. Uruchom sudo modprobe wireguard && echo ok przed wykonaniem jakichkolwiek innych czynności.