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ą.
Komentarze