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…

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ć.

(!) Zgłoś błąd na stronie | Lub postaw nam kawę :-)
LUTy dla D-Cinelike (DJI Mini 3 Pro, DJI Avata, OSMO Pocket) od MiniFly
Wdrożenie Omnibusa w sklepie na WooCommerce
Jak (legalnie) latać dronem w Kategorii Otwartej
Patryk