Wyszukiwarka BigData - cz. 3 Tunele i VPN-y... czyli o tym jak pokonać NAT

Słowem wstępu...

Zapewne wielu z was zastanawiało się, jak wystawić usługę z naszego domowego komputera na świat, tak by inni mogli się z nią połączyć, a jednocześnie nie korzystać przy tym z zewnętrznego adresu IP. 

Zbierzmy więc w jednym miejscu nasze początkowe założenia:

  • nasz komputer domowy - znajduje się za nat-em, nie posiada zewnętrznego ip, ma wypasioną specyfikację i służy nam jako worker
  • serwer zewnętrzny - posiada minimalne zasoby (nawet 512MB ramu, 1x cpu) i służy nam jako proxy pośredniczące do naszego komputera

(Nie)problem a wyzwanie:

Wystawienie "ciężkiej" usługi znajdującej się na naszym domowym komputerze (do celów developerskich), bez konfiguracji całego środowiska produkcyjnego do testów dla klienta.


Pomysł 1

Polega na utworzenie tunelu z lokalnego serwera znajdującego się za nat-em, do serwera z zewnątrz i tym samym wystawieniem usługi z lokalnego portu 8080, na zewnętrzny port 80 znajdujący się na serwerze.


Kolejne kroki:

Na samym początku w /etc/ssh/sshd_config ustawiamy możliwość przekierowywania portów:

GatewayPorts yes

Teraz przyszła pora na zestawienie tunelu:

ssh -R <remote_ip>:<remote_port>:localhost:<local_port> <remote_user>@<remote_ip> -p <ssh_service_remote_port>

Studium przypadku:

ssh -R 10.0.0.4:80:localhost:8080 root@10.0.0.4 -p 22

 (w następnych przykładach zastosujemy podaną powyżej adresację)

Pomysł 2

A właściwie 1.2 :) jest wariacją pierwszego pomysłu. Zakłada że zamiast za każdym razem ręcznego logowania, tworzymy usługę systemową która to zrobi za nas oraz utrzyma tunel mimo problemów sieciowych.


Kolejne kroki:

Na samym początku umożliwimy naszemu klientowi na logowanie do serwera bez użycia haseł, wykonując na serwerze polecenie:

ssh-keygen -t rsa

Z kolei na kliencie wykonujemy pobranie zdalnych kluczy:

ssh-copy-id  -p 22 remote@10.0.0.4

Po czym testujemy nasze połączenie bez hasła, po kluczu między serwerami:

ssh remote@10.0.0.4 -p 22

W razie problemów możemy użyć "ssh-keygen -p" do usunięcia hasła z klucza.


Również jak w pkt 1, ustawimy w /etc/ssh/sshd_config możliwość przekierowywania portów:

GatewayPorts yes

Teraz zajmiemy się utworzeniem naszej usługi:

/etc/systemd/system/tunel.service

[Unit]
Description=Reverse SSH connection
After=network.target
[Service]
Type=simple
User=remote
Group=remote

ExecStart=/usr/bin/ssh -vvv -g -N -T -o "ServerAliveInterval 10" -o "ExitOnForwardFailure yes" -R 10.0.0.1:80:localhost:8080 root@10.0.0.4 -p 22
Restart=always
RestartSec=5s
[Install]
WantedBy=default.target

Po czym wykonujemy przeładowanie naszych usług na serwerze oraz restartujemy tunel.

systemctl daemon-reload 
systemctl restart tunel.service
systemctl status tunel.service

Niestety, oba powyższe pomysły mają wady takie jak:

  • niska wydajność
  • trudność w zarządzaniu w przypadku większej ilości tuneli
  • są bardzo nieeleganckie :)
  • tunelowanie potrafi naraz przekierować tylko jeden port


Pomysł 3

Właściwy, który pozwoli nam stworzyć połączenie vpn, pomiędzy dwiema naszymi maszynami, tak by widziały się w pełni wzajemnie. Co pozwoli na efektywne utworzenie własnej wirtualnej sieci, odpornej na zakłócenia oraz wyeliminuje wszystkie problemy wyżej opisane. Więcej informacji


Kolejne kroki na serwerze:

Zaczniemy od skonfigurowania serwera, wykonując instalację wymaganych pakietów:

apt install wireguard resolvconf

Po czym zezwalamy na naszym serwerze na przekierowywanie pakietów:

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

Następnie tworzymy na naszym serwerze, parę klucza prywatnego oraz publicznego które posłużą nam do zabezpieczenia komunikacji, szyfrując ją.

wg genkey | sudo tee /etc/wireguard/private.key
chmod go= /etc/wireguard/private.key
cat /etc/wireguard/private.key | wg pubkey | tee /etc/wireguard/public.key

Teraz czas na docelową konfigurację:

/etc/wireguard/wg0.conf

[Interface]
# Wirtualny ip naszego serwera
Address = 10.5.5.1/24
# Port na którym jest dostępna usługa
ListenPort = 51829
PrivateKey = <tu znajduje się nasz klucz prywatny z pliku /etc/wireguard/private.key>

# Reguły ustawiające nasz routing, wykonywane podczas tworzenia tunelu
PostUp = ip rule add table 200 from 10.0.0.4
PostUp = ip route add table 200 default via 10.0.0.1
PostUp = ufw route allow in on wg0 out on ens192
PostUp = iptables -t nat -I POSTROUTING -o ens192 -j MASQUERADE
PostUp = ip6tables -t nat -I POSTROUTING -o ens192 -j MASQUERADE
# Reguły usuwające nasz routing, wykonywane podczas zamykania tunelu
PreDown = ip rule delete table 200 from 10.0.0.4
PreDown = ip route delete table 200 default via 10.0.0.1
PreDown = ufw route delete allow in on wg0 out on ens192
PreDown = iptables -t nat -D POSTROUTING -o ens192 -j MASQUERADE
PreDown = ip6tables -t nat -D POSTROUTING -o ens192 -j MASQUERADE

# Nasz pierwszy klient - Stacja robocza 1
[Peer]
# Klucz publiczny klienta
PublicKey = <tu znajduje się nasz klucz publiczny z naszego lokalnego komputera z pliku /etc/wireguard/public.key>
# Adres ip klienta
AllowedIPs = 10.5.5.2/32

# Drugi klient - Laptop 2
[Peer]
# Klucz publiczny klienta
PublicKey = <tu znajduje się nasz klucz publiczny z naszego lokalnego komputera z pliku /etc/wireguard/public.key>
# Adres ip klienta
AllowedIPs = 10.5.5.3/32
  • Oznaczenie ens192, odpowiada naszej karcie sieciowej (więcej po wpisaniu "ip a s" w terminalu),
  • Adres 10.0.0.1 oznacza adres bramy wychodzącej dla naszego serwera (pobieramy go za pomocą polecenia "ip route list table main default"),

Pora na dodanie wyjątków w naszym firewallu:

ufw allow 51829
ufw reload
# ufw route allow in on wg0
ufw route allow in on wg0 out on ens192

Następnie testujemy uruchomienie naszego serwera:

wg-quick up wg0
wg-quick down wg0

Na sam koniec, w przypadku braku błędów, uruchamiamy usługę na stałe:

systemctl start wg-quick@wg0
systemctl enable wg-quick@wg0

Kolejne kroki w kliencie:

Na nim wykonamy analogiczne instalacje oraz konfiguracje zupełnie jak w paragrafie powyżej:

apt install wireguard resolvconf

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

wg genkey | sudo tee /etc/wireguard/private.key
chmod go= /etc/wireguard/private.key

Teraz czas na docelową konfigurację:

/etc/wireguard/wg0.conf

[Interface]
# Mój virtualny adres ip, stacji roboczej
Address = 10.5.5.2/32
# Port na którym jest uruchomiona usługa na serwerze
ListenPort = 51829
DNS = 1.1.1.1
PostUp = wg set %i private-key /etc/wireguard/private.key
# Pozwala nam przetestować czy po uzyskaniu połączenia, nasz serwer jest w sieci wewnętrznej
PostUp = ping -c1 10.5.5.1

[Peer]
PublicKey = <tu znajduje się klucz publiczny naszego serwera /etc/wireguard/public.key>
# Zewnętrzny adres ip naszego serwera
Endpoint = 10.0.0.1:51829
# Powoduje że nasz serwer będzie w stanie widzieć inne serwery znajdujące się w tej samej podsieci
AllowedIPs = 10.5.5.0/24
# Odkomentowanie poniższej opcji, spowoduje że cały ruch wychodzacy będzie się odbywał przez serwer vpn.
#AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

Następnie testujemy uruchomienie połączenia z naszym serwerem:

wg-quick up wg0

Dodatkowo, możemy dodać do autostartu, automatyczne łączenie się z vpn-em:

systemctl enable --now wg-quick@wg0

*Od siebie mogę dodać poniższą ciekawostkę:

Czasami mimo poprawnego połączenia serwer nie jest wykonać pingu na pozostałę urządzenia. W takim przypadku zalecam wyłączyć i włączyć firewalla - ufw.


Powyższe rozwiązania, pozwolą nam zbudować niskim kosztem, bezpieczne środowisko developerskie które umożliwi nam jeszcze tańsze i prostsze dostarczanie klientowi oprogramowania do testów.


Podsumowanie

Stworzyliśmy dwia mikroserwisy, dzięki którym użytkownik może dodać zadanie wyszukiwania, dla naszego serwera u lokalnego dostawcy. Po czym to zadanie trafi z aplikacji napisanej w Django, do napisanej w Flasku nasz lokalny komputer celem przetworzenia. Co oszczędzi tak cenne w dzisiejszych czasach zasoby w usługach opartych na płaceniu za moc obliczeniową.

Kamil Mirończuk

I kiedy czegoś gorąco pragniesz, to cały wszechświat sprzyja potajemnie twojemu pragnieniu
~Paulo Coelho

Komentarze

Zostaw komentarz

Twój adres mailowy NIE zostanie opublikowany. W razie otrzymania zapytania, otrzymasz na niego odpowiedź.
Wymagane pola są oznaczone jako *