Zdalny nadzór routera
Ostatnia zmiana: 2013-11-26 18:43
Czasami mamy do czynienia z routerami, które w jakiś sposób chcielibyśmy kontrolować, ale są one poza naszym fizycznym zasięgiem. Router u rodziny, na działce, w samochodzie, z modemem 3G, nie wiadomo czy i kiedy będzie włączony. Chcielibyśmy, aby takie urządzenie "zgłaszało" się samo i czasami można było także dać mu jakieś polecenie do wykonania.
Takie rozwiązanie zastosowano w projekcie Fon - zadaniem skryptu było cykliczne zgłaszanie się do systemów nadzoru, co pozwalało uzyskać informacje o fakcie działania routera oraz zdalnie nadzorować router przez zlecanie mu określonych poleceń do wykonania.
Można utworzyć skrypt, który zostanie cyklicznie uruchomiony. Zadaniem skryptu będzie zgłoszenie się pod określony adres, pobranie danych i jeżeli coś jest - wykonanie tego. Rezultat może być odesłany z powrotem. Podane rozwiązanie znakomicie sprawdza się np. na routerach z modemami 3G, do których nie można się bezpośrednio dostać, a czasami należało by mieć nad nimi kontrolę. Do działania niezbędne będzie posiadanie strony www która będzie przetwarzała wywołania klientów.
Skrypt klienta
Czyli skrypt, który cyklicznie będzie wykonywał się na zdalnym routerze. Do działania niezbędny będzie jeszcze program
base64 który pozwoli na przetworzenie dowolnych danych do postaci czytelnego ciągu znaków ASCII.
# opkg update
# opkg install coreutils-base64
Tworzymy plik o nazwie
remote.sh w katalogu
/sbin
# touch /sbin/remote.sh
# chmod 777 /sbin/remote.sh
i umieszczamy w nim następujący skrypt:
#!/bin/sh
rm /tmp/skrypt.sh > /dev/null 2>&1
OUT="/tmp/output.txt"
MAC=$(ifconfig br-lan | awk '/HWaddr/ {gsub(":","");print $5}')
P=""
if [ -e "$OUT" ]; then
P=$(cat "$OUT" | base64 -w0)
rm "$OUT"
fi
wget -q -O /tmp/skrypt.sh "http://serwer.net/cgi-bin/heartbeat.cgi?mac=$MAC&p=$P"
if [ -s /tmp/skrypt.sh ]; then
sh /tmp/skrypt.sh > "$OUT" 2>&1
fi
exit 0
Parę słów wyjaśnienia. Klient wywołuje adres naszej strony, przekazując dwie dane w adresie URL: adres mac, pobrany z interfejsu
br-lan który identyfikuje ten konkretny router (pozbawiony znaków ":"), oraz dodatkową zmienną, tzw.
payload która zawiera wynik działania skryptu zleconego do wykonania przy poprzednim uruchomieniu (o ile jakiś wynik był). Żeby można było taki plik przesłać, jest on kodowany w
base64, dzięki czemu całość jest po prostu ciągiem znaków. Wynikiem może być skrypt do wykonania zlecony przez serwer do którego zostały wysyłane dane. Jeżeli jest coś, to jest to wykonywane. Wynik wykonania tego skryptu jest zapisywany do pliku, który jako
payload zostanie wysłany do serwera przy następnym uruchomieniu programu.
Uruchomienie skryptu można włożyć do
crona, cyklicznie co np. 10min:
# echo "*/10 * * * * /sbin/remote.sh" >> /etc/crontab/root
# /etc/init.d/cron restart
Skrypt serwera
Zakładamy że dysponujemy serwerem pod domeną
serwer.net. W nim mamy skrypt cgi, który będzie odbierał zgłoszenia od klientów i ew przekazywał im polecenia do wykonania. Rolę serwera może wykonywać np. OpenWrt z uruchomionym
uhttpd i odpowiednim skryptem zapisanym w podkatalogu
cgi_bin w katalogu głównym serwera www. Przykładowy taki skrypt (o nazwie
heartbeat.cgi, bo taka nazwa jest zdefiniowana w skrypcie klienta), może wyglądać następująco:
#!/bin/sh
echo "Content-type: text/xml"
echo ""
eval $(echo "$QUERY_STRING"|awk -F'&' '{for(i=1;i<=NF;i++){print $i}}')
date +'%Y-%m-%d %H:%M:%S' >> /tmp/log.txt
echo "mac: $mac" >> /tmp/log.txt
p=$(echo "$p" | base64 -d)
echo "payload:" >> /tmp/log.txt
echo "$p" >> /tmp/log.txt
echo "----" >> /tmp/log.txt
W takiej postaci skrypt będzie zapisywał do pliku
/tmp/log.txt informacje o zgłoszeniach klienta w postaci:
2012-09-25 21:19:23
mac: F8D111010203
payload:
Aby wydać polecenie do zdalnego systemu należy uzupełnić skrypt
heartbeat.cgi na serwerze o następujący kod (tu: dla routera o mac adresie F8D111010203):
cmd=""
case "$mac" in
"F8D111010203")
cmd="logread"
;;
esac
if [ -n "$cmd" ]; then
echo -e "#!/bin/sh\n""$cmd""\n"
fi
Działanie procesu
Nie wiemy kiedy router zostanie włączony a chcielibyśmy pobrać z niego cyklicznie logi systemowe. Wpisujemy więc dla danego mac adresu polecenie do wykonania (konkretnie
logread). Router, kiedy zostanie włączony, co 10min będzie wykonywał skrypt. Przy pierwszym uruchomieniu żadnego polecenia wcześniej jeszcze nie wykonał więc tylko wywoła pobranie danego adresu serwera przekazując mu swój mac adres jako parametr w URL. Serwer zapisze te dane i odeśle skrypt do wykonania (będzie to wspomniany
logread). Router odbierze skrypt, wykona go i rezultat zostanie zapisany do pliku
/tmp/output.txt na routerze. Po kolejnych 10 minutach router zobaczy, że istnieje rezultat wykonania poprzedniego polecenia, więc zakoduje go w
base64, dołączy jako parametr do adresu URL i ponownie pobierze dane z serwera. Cały cykl zostanie powtórzony.
Uwagi końcowe
Podane rozwiązanie jest bardzo proste - nie zawiera mechanizmów autoryzacji, kodowania danych i podobnych elementów. W ten sposób można wykonać dowolne polecenie systemowe co pozwoli na zbadanie np. poziomu sygnału modemu GSM, przesłanie logów, ale także można nakazać np. zestawić tunel ssh do innego hosta czy zdalnie zrobić upgrade systemu.