Kilka dni temu, w artykule o moim nowym internecie podstawowym (Internet na Kartę w Play) wspomniałem, że po kilku latach, brak stałego i publicznego adresu IP jest dla mnie większym utrudnieniem niż „skacząca prędkość”, czy w ogóle zanikający od czasu do czasu transfer. Dlatego uznałem, że muszę wdrożyć jakieś dodatkowe mechanizmy, które pozwolą mi zniwelować minusy wynikające z dynamicznie przydzielanego adresu IP…
Spis treści w artykule
Dynamiczny adres IP przy uwierzytelnieniu dwuskładnikowym na serwerze VPS
Zapewne przeciętny internauta nawet nie zawraca sobie głowy czymś takim jak adres IP, a tym bardziej tym, czy jest on stały (i publiczny), bo najzwyczajniej w świecie nie ma to dla niego znaczenia. Ja jednak mam pod opieką m.in. kilka serwerów (VPS), na których jednym z mechanizmów mających na celu blokowanie nieautoryzowanego dostępu jest uwierzytelnienie dwuskładnikowe, co wymaga podania oprócz nazwy użytkownika i hasła (oraz wskazania odpowiedniego pliku klucza) jednorazowego kodu, generowanego przez programowy token (w moim przypadku Authy zamiast Google Authenticatora).
By jednak sobie trochę ułatwić codzienne obcowanie z serwerami – zwłaszcza podczas korzystania z WinSCP – na niektórych serwerach miałem ustawione tak, że połączenie SSH/SCP z mojego stałego adresu IP nie wymagało każdorazowego podawania kodu jednorazowego. Powiedzmy, że taki kompromis między bezpieczeństwem a wygodą.
Od liku dni korzystam jednak głównie z internetu mobilnego, w którym adres IP jest zmieniany dynamicznie, „co jakiś czas”, a tym samym ręczne ustawienie go w wyjątkach na serwerze byłoby może nie tyle bezcelowe, co… dość mocno absorbujące. Dlatego postanowiłem, że trzeba wdrożyć jakieś rozwiązania, które bez (znacznej) utraty bezpieczeństwa pozwolą mi ponownie wygodnie łączyć się z (wybranymi) serwerami z mojego internetu domowego/biurowego.
Serwer VPN
Pierwszym rozwiązaniem, które uznałem za sensowne w takiej sytuacji, będzie skorzystanie z serwera VPN, który z założenia ma stały adres IP, a więc można ten adres dodać do wyjątków na docelowych serwerach. Do niedawna taką rolę pełnił komputer Raspberry Pi (VPN/PPTP), ale z racji tego, że znajduje się on w mojej sieci, czyli obecnie „za łączem mobilnym”, to nic to nie da.
Można w takiej sytuacji skorzystać z jednej z firm oferujących usługi typu VPN, których obecnie na rynku jest całkiem sporo. Ja jednak postanowiłem, że lepszym rozwiązaniem (i chyba nawet tańszym) będzie postawienie dedykowanego serwera VPS, na którym uruchomię tylko i wyłącznie usługę VPN. Tak na wszelki wypadek.
Cloudflare
Kolejnym rozwiązaniem, które przyszło mi do głowy było skorzystanie z serwisu Cloudflare, gdzie można przygotować jakąś subdomenę, i za pomocą skryptu podstawiać tam aktualny adres IP, który w regularnych odstępach czasu (CRON) będzie odczytywany przez serwery, i podstawiany do odpowiedniego pliku.
Rozwiązanie sensowne, zarazem dość proste do wdrożenia – powiedzmy, że do artykułu poświęconego wykorzystaniu Cloudflare jako serwisu DDNS (DynDNS) trzeba by dopisać rozdział poświęcony odczytywaniu tego adresu z Cloudflare, by podstawić go w odpowiednim pliku konfiguracyjnym. Rozwiązanie ma taką zaletę, że nie potrzeba tu dodatkowego serwera, a więc i nic nie generuje dodatkowych kosztów.
Uznałem jednak, że na potrzeby tego artykułu pójdziemy jeszcze o krok dalej, i zrezygnujemy również z Cloudflare, bo zapewne nie wszyscy korzystają z tej usługi.
Odczytywanie i zapisywanie dynamicznego adresu IP za pomocą skryptów
Postanowiłem, że wykorzystam Raspberry Pi do odczytywania aktualnego adresu IP (tak samo, jak przy Cloudflare jako DDNS) i przekazywania go do zdalnego serwera, na którym kolejny skrypt dokona odpowiedniej modyfikacji plików konfiguracyjnych SSH, tak by dodać mój aktualny adres IP do wyjątków.
Raspberry Pi, czyli sprawdzamy aktualny adres IP
Pierwsze co skrypt musi zrobić, to ustalić aktualny adres IP. Postanowiłem skorzystać z adresu:
https://cdn.pryc.eu/add/myip.php
Możecie zrobić to samo, ale możecie też na jakimś serwerze z obsługą PHP (webserwer) utworzyć plik o nazwie np. myip.php i takiej zawartości:
<?php
echo $_SERVER['REMOTE_ADDR'];
?>
Za pomocą tego pliku (lub podanego wcześniej adresu) będziemy mogli odczytać aktualny adres IP. Oczywiście nie jest to jedyna metoda, ale ja z takiej korzystam od dawna i mi się sprawdza bardzo dobrze.
Kolejnym krokiem jest przygotowanie skryptu, który sprawdzi aktualny adres IP, i w przypadku wykrycia, że się zmienił powiadomi zdalny serwer o tym fakcie:
#!/bin/bash
IP_CONTROL_LAST=1 # 1 - sprawdź czy adres IP uległ zmianie ($IP_LOG_FILE), 0 - zawsze wysyłaj adres IP
# Adres serwera by sprawdzić aktualny IP:
IP_CHECK_URL=https://cdn.pryc.eu/add/myip.php
IP_LOG_FILE=/boot/last-ip.txt # Plik przechowujący ostatni znany adres IP
if [ -z "$IP_CHECK_URL" ]
then
echo "IP_CHECK_URL is empty!"
exit 0
else
IP_NOW=`wget -O - -q $IP_CHECK_URL`
fi
if [ ! -f $IP_LOG_FILE ] && [ "$IP_CONTROL_LAST" = "1" ]
then
sudo touch $IP_LOG_FILE
fi
if [ $IP_LOG_FILE ] && [ "$IP_CONTROL_LAST" = "1" ]
then
IP_LAST=$(cat $IP_LOG_FILE)
fi
function f_send_ip() {
if [ -n "$IP_NOW" ]
then
# Polecenie przekazujące do zdalnego serwera adres IP:
wget -O - -q https://adres.mojego.serwera.vps/newip.php?ip=$IP_NOW > /dev/null
fi
}
if [ "$IP_CONTROL_LAST" = "1" ]
then
if [ "$IP_NOW" != "$IP_LAST" ]
then
echo $IP_NOW | sudo tee $IP_LOG_FILE > /dev/null
f_send_ip
else
echo "IP is OK"
fi
else
echo $IP_NOW | sudo tee $IP_LOG_FILE > /dev/null
f_send_ip
fi
exit 0
Nie jest to skrypt z którego ja korzystam – zostały z niego wyeliminowane wszystkie dodatkowe mechanizmy związane z dodatkowym uwierzytelnieniem. Nie tylko dlatego, by nie zdradzać co i jak wdrożyłem w tym zakresie, ale też po to, by niepotrzebnie nie komplikować/zaciemniać przedstawionego rozwiązania. Ta zasada będzie też dotyczyć wszystkich kolejnych skryptów, które pojawią się w tym artykule.
Serwer (VPS) zdalny
W skrypcie powyżej – w przypadku zmiany adres IP – następuje wywołanie pliku newip.php, który odpowiada za przyjęcie tej informacji (nowy adres IP), oraz zapisanie jej do pliku konfiguracyjnego (na potrzeby artykułu będzie to plik „newip”).
Skrypt PHP (w wersji uproszczonej, o czym już wspominałem) może wyglądać tak:
<?php
if ( !isset( $_GET['ip'] ) ) {
die();
} else {
$IP = preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_GET['ip'] );
$IP = $_GET['ip'];
$LogFile = "./newip";
file_put_contents($LogFile, $IP, LOCK_EX);
die();
}
?>
W tym momencie do akcji wkracza drugi skrypt, który na podstawie pliku „newip” dokona – jeśli zajdzie taka potrzeba – zmian w konfiguracji usługi SSH na serwerze:
#!/bin/bash
IP_LOG_FILE=/ścieżka/do/pliku/newip
# Odczyt aktualnego IP:
if [ $IP_LOG_FILE ]
then
IP2SET=$(cat $IP_LOG_FILE)
fi
# Odczyt archiwalnego IP:
CurrentIP=$(sed -n 2p /etc/security/access-local.conf | tail -c +11)
# Porównanie IP:
if [ "$IP2SET" != "$CurrentIP" ]
then
# Podmiana IP:
sudo sed -i "2s/"$CurrentIP"/"$IP2SET"/g" /etc/security/access-local.conf > /dev/null
# Restart SSH:
sudo /etc/init.d/ssh restart > /dev/null
fi
exit
Od razu zwracam uwagę na linijkę:
CurrentIP=$(sed -n 2p /etc/security/access-local.conf | tail -c +11)
Znajduje się tutaj parametr „2p”, który oznacza drugą linijkę w pliku access-local.conf, bo w moim przypadku tam znajduje się zapis, który będziemy odczytywać i w razie potrzeby modyfikować:
+ : ALL : 123.124.125.126
Dotyczy to również linijki:
sudo sed -i "2s/"$CurrentIP"/"$IP2SET"/g" /etc/security/access-local.conf > /dev/null
Tutaj jest to parametr „2s”. Można też lekko zmodyfikować ten zapis, tak, że będzie dokonywał podmiany bezwzględnie, bez szukania konkretnej zawartości do podmiany (starego adresu IP):
sudo sed -i "2s/.*/+ : ALL : "$IP2SET"/g" /etc/security/access-local.conf > /dev/null
Pamiętajcie tylko, by zarówno ten skrypt, jak i ten uruchamiany np. na Raspberry Pi ustawić jako wykonywalny:
sudo chmod 755 /ściażka/do/skryptu/nazwa-skryptu
Niby drobiazg, ale z doświadczenia wiem, że sporo osób o tym zapomina, i potem są problemy, ze skrypt się nie uruchamia…
Podstawowe mechanizmy zabezpieczające
Wywołany komentarzem postanowiłem podać prosty i dość skuteczny sposób, w jaki można dodatkowo zabezpieczyć system przed nieprawioną podmianą adres IP.
Podstawa to HTTPS/SSL, co i tak staje się już standardem. Do tego można dołożyć token, który będzie weryfikowany podczas każdego połączenia ze skryptem PHP, a bez znajomości którego, nie będzie można przekazać nowego adres IP do serwera.
W skrypcie sprawdzającym aktualny adres IP wystarczy lekko zmodyfikować polecenie Wget:
wget -O - -q https://adres.mojego.serwera.vps/newip.php?ip=$IP_NOW&token=WebinsiderPL > /dev/null
Do tego w pliku PHP modyfikujemy pierwszy warunek tak, by sprawdzał czy wywołanie nastąpiło z prawidłowym tokenem:
if ( ( !isset( $_GET['ip'] ) ) && ( !isset( $_GET['token'] ) ) && ( $_GET['token'] != "WebinsiderPL" ) ) { die(); }
Oczywiście zamiast „WebinsiderPL” należy wstawić jakiś unikatowy ciąg znaków. Uważajcie tylko na znaki specjalne (poza a-z, A-Z, 0-9, – i _), bo niektóre z nich (np. „), bez odpowiedniej „otoczki” mogą uszkodzić działanie skryptu.
Można też (dodatkowo) dostęp do pliku PHP zabezpieczyć za pomocą loginu i hasła HTTP…
Odczytywanie adresu IP bezpośrednio z wywołania strony
Można całość zmodyfikować również tak, że klient (Raspberry Pi, telefon, komputer) nie sprawdza jaki ma adres IP, tylko co jakiś czas wywołuje stronę WWW (skrypt PHP), który odczytuje adres IP klienta i dodaje go do wyjątków. W takim wariancie jednak warto wdrożyć token, by przypadkowa osoba nie mogła ustawić adresu IP.
Skrypt PHP w wersji podstawowej, z tokenem może wyglądać tak:
<?php
if ( ( !isset( $_GET['token'] ) ) && ( $_GET['token'] != "WebinsiderPL" ) ) {
die();
} else {
$IP = $_SERVER['REMOTE_ADDR'];
$LogFile = "./newip";
file_put_contents($LogFile, $IP, LOCK_EX);
die();
}
?>
W tym momencie, by ustawić adres IP wystarczy na urządzeniu, które będzie się łączyło wywołać adres:
https://adres.mojego.serwera.vps/newip.php?token=WebinsiderPL
Roziwązanie prostrze, może się przydać zwłaszcza tam, gdzie bardziej rozbudowanych skrytpów nie możemy uruchomić (np. na telefonie).
CRON, czyli harmonogram zadań
Na koniec warto jeszcze całość zautomatyzować – bo w końcu po to w ogóle zostały stworzone te skrypty, np. za pomocą systemowego harmonogramu zadań (CRON). Częstotliwość wywoływania skryptów zależy tylko do Was, moim zdaniem 5-10 minut w większości wystarczy:
*/5 * * * * root /ściażka/do/skryptu/nazwa-skryptu >/dev/null 2>&1
Więcej na temat CRONa pisałem w tym artykule, i oczywiście zachęcam, by się z nim zapoznać…
Rozwiązanie, które po prostu działa
Jak już pisałem – przedstawione tutaj rozwiązanie zostało przeze mnie trochę „wykastrowane”, ale tylko o elementy, które nie są niezbędne dla prawidłowego działania, a są związane z dodatkowymi zabezpieczeniami (np. tokeny uwierzytelniające, dzięki którym osoba nieuprawniona nie powinna móc skutecznie wywołać pliku PHP, dodatkowe raporty/powiadomienia).
I choć na początku kierowałem się ku rozwiązaniu z VPNem, to muszę przyznać, że w trakcie kilkudniowych testów tego rozwiązania przekonałem się do niego na tyle, że… w tym czasie z VPNa skorzystałem tylko raz, głównie sprawdzić czy wszystko działa. Co nie znaczy, że nie będę go stopniowo modyfikował, bo jest to jego pierwsza odsłona, i pewnie niektóre elementy można napisać lepiej… ;-)
SSH zamiast PHP
Teoretycznie można zamiast PHP, czyli przekazywania nowego (aktualnego) adresu IP za pomocą wywołania strony internetowej skorzystać z połączenia SSH, i dokonać zdalnej modyfikacji ustawień serwera lub w ten sposób przynajmniej przekazać nowy adres IP.
Ale tu koło się zamyka – bo gdy mamy wymuszone 2FA, to tego typu połączenie (SSH – SSH) nie zadziała. W skrypcie możemy wykorzystać połączenie z loginem i hasłem, a nawet kluczem, ale z 2FA będzie dużo trudniej. Nie dodamy też wcześniej (automatycznie) naszego IP do wyjątków, bo… jest on zmienny, i właśnie po to cała ta zabawa, by go dodać.
- Wakacje składkowe ZUS a zawieszenie działalności gospodarczej, czyli uważaj, bo być może nie będziesz mógł skorzystać (w 2024) - 1970-01-01
- Przykładowy kalkulator wyceny usługi druku 3D, czyli nie tylko materiał się liczy - 1970-01-01
- Home Assistant 2024.10, czyli nowa karta „nagłówek” i niedziałający TTS w ramach usługi Google Cloud - 1970-01-01
Wydaje mi się, iż rozwiązanie oparte o API CF będzie jednak łatwiejsze. Bo nie musisz robić też całej otoczki bezpieczeństwa.
RP1 i skrypt 1 po prostu będzie sprawdzał, jakie ma IP i aktualizował subdomenę np. homenet o aktualny IP, a skrypt 2 na VPS będzie po prostu odczytywał wartość.
Zgadzam się, dlatego o Cloudflare wspomniałem na samym początku, bo tak jak Ty uważam, że jest to dobre i bezpieczne rozwiązanie. Ale wymaga to korzystania z tej usługi. I o ile ja korzystam (np. Webinsider.pl działa chyba nawet na planie Business jeśli dobrze pamiętam) i polecam, to wiem, że – z różnych przyczyn – nie wszyscy mogą (np. jeśli korzystasz z Home.pl i chcesz mieć u nich też pocztę e-mail). Dlatego chciałem przygotować coś bardziej uniwersalnego (jak ktoś będzie chciał, to nawet za pomocą małej modyfikacji zrobi sobie stronę WWW, którą wystarczy odwiedzić – z odpowiednią autoryzacją oczywiście – by został jego aktualny adres IP ustawiony na serwerze). Do tego o wykorzystaniu API Cloudflare pisałem w kontekście DDNS, więc wystarczy dołożyć część informacji z tego artykułu (podmiana adresu IP w konfiguracji SSH) i jest gotowe rozwiązanie.
Otoczka bezpieczeństwa też nie musi być jakaś mega, wydaje mi się, że w większości przypadków wystarczy HTTPS/SSL (w sumie i tak staje się to standardem) oraz np. token (hasło) przekazywany w dodatkowej zmiennej:
Który będzie weryfikowany przez skrypt PHP:
Banalne, a już samo to znacznie podnosi bezpieczeństwo całego rozwiązania. Nie zapominajmy też, że to tylko eliminuje 2FA dla tego adresu IP. By połączyć się z serwerem nadal potrzebny jest login i hasło, oraz klucz.
W obecnych czasach korzystanie z usług (tu DNS), które nie umożliwiają zarządzania via API to proszenie się o więcej roboty. Oczywiście, nie musi to być CF, ale nie wiem czy np. OVH niema też API dla DNSów.
Oczywiście sam korzystam z API w Cloudflare, również do zarządzania DNSami, ale… korzystam, bo jest. Główna zaleta DNSów w CF to dla mnie MEGA szybkie zmiany rekordów – podaje np. nowy adres IP dla rekordu A dla domeny i kilka sekund później to już działa. Podałem też przykład Home.pl – co byśmy o ofercie tej firmy nie myśleli, to jest jednak popularna (inna sprawa dlaczego ;-)), i tam np. jak chcesz mieć pocztę e-mail, to nie ma zmiłuj – domena musi wskazywać na ich DNSy.