Ograniczenia dostępu do internetu
Ostatnia zmiana: 2023-11-17 07:48
Zakładamy że używamy routera z OpenWrt. Podłączamy się do niego urządzeniem (przewodowo lub bezprzewodowo, nie ma znaczenia), w domyślnej konfiguracji Openwrt urządzenie uzyskuje dostęp do internetu. Co w przypadku kiedy chcemy ograniczyć ten dostęp w jakiś sposób? Robimy sieć dla gości i chcielibyśmy ograniczyć transfer np. do 1GB tygodniowo lub nie więcej niż 2Mb/s. Mamy dzieci i nie chcemy żeby korzystały z internetu popołudniami, kiedy mają odrabiać lekcje, ale czasami potrzebujemy im dać dostęp żeby mogły poszukać informacji np. na wikipedii. Lub po prostu mamy wifi w biurze i domyślnie chcemy wyłączyć je na weekend. Ten poradnik pokazuje różne metody blokowania lub ograniczenia użytkowników. Elementem identyfikującym użytkowników będzie adres mac urządzenia klienckiego. W całym poradniku blokowany będzie hipotetyczny adres MAC 01:02:03:04:05:06 (tylko jeden). Zakładamy też że firewall OpenWrt ma domyślną konfigurację: ze strony lan akceptuje cały ruch, a zajmujemy się blokowaniem określonych hostów. Jeżeli chcemy mieć odwrotnie to należy zmienić firewall (sekcja lan) oraz odpowiednio dostosować logikę tworzonych reguł - akceptować (ACCEPT) zamiast odrzucać (REJECT) połączenia. Jeżeli chcemy blokować kilka adresów to należy albo zmodyfikować skrypty albo je powielić dla każdego adresu, stosowanie do zastosowanych blokad.
Rozwiązania przetestowano na LEDE 17.01.
Bezpośrednie użycie poleceń iptables możliwe jest tylko do wydania OpenWrt 21.02 włączenie. Nowsze wydania posiadają inne mechanizmy i nie można używać w nich reguł iptables, bez instalacji specjalnych pakietów.
Całkowita blokada urządzenia
Tymczasowa
Całkowicie blokujemy dostęp do internetu. Można to zrobić tworząc w konsoli odpowiednią regułę iptables:
# iptables -I FORWARD -m mac --mac-source 01:02:03:04:05:06 -j REJECT
Od tej pory nastąpi blokada dostępu do internetu dla tego adresu MAC. Blokada będzie obowiązywać do restartu routera, do restartu firewalla (lub np. zmiany konfiguracji co powoduje restart firewalla) lub do ręcznego usunięcia reguły.
Aby ręcznie usunąć regułę należy posłużyć się poleceniem:
# iptables -D FORWARD -m mac --mac-source 01:02:03:04:05:06 -j REJECT
Stała
Tworzymy po prostu odpowiednią regułę w firewallu OpenWrt korzystając z
uci:
# uci set firewall.mac010203040506=rule
# uci set firewall.mac010203040506.src='lan'
# uci set firewall.mac010203040506.dest='wan'
# uci set firewall.mac010203040506.src_mac='01:02:03:04:05:06'
# uci set firewall.mac010203040506.target='REJECT'
# uci commit firewall
# /etc/init.d/firewall restart
W/w polecenie tworzą tzw. "sekcję nazwaną" (patrz poradnik o
uci) dzięki czemu można później bezproblemowo manipulować zawartością reguły bez jej szukania w całym firewallu a jako nazwę sekcji wykorzystałem zapis związany z adresem mac. Oczywiście można używać prawie dowolnej nazwy typu "malgosia" pamiętając o ograniczeniach uci: żadnych spacji, znaków specjalnych, myślników itd.
Zdjęcie blokady można wykonać na dwa sposoby:
- przez wyłączenie reguły:
# uci set firewall.mac010203040506.enabled=0
# uci commit firewall
# /etc/init.d/firewall restart
- przez jej usunięcie z firewalla:
# uci del firewall.mac010203040506
# uci commit firewall
# /etc/init.d/firewall restart
Blokada określonych zasobów - po adresie IP lub po portach
(dotyczy blokady zasobów w internecie, nie w sieci lokalnej)
Powyższe reguły można lekko zmodyfikować - np. zablokować dostęp do www, zezwalając na resztę usług:
# uci set firewall.mac010203040506_80=rule
# uci set firewall.mac010203040506_80.src='lan'
# uci set firewall.mac010203040506_80.dest='wan'
# uci set firewall.mac010203040506_80.src_mac='01:02:03:04:05:06'
# uci set firewall.mac010203040506_80.proto=tcp
# uci set firewall.mac010203040506_80.dest_port=80
# uci set firewall.mac010203040506_80.target='REJECT'
# uci set firewall.mac010203040506_443=rule
# uci set firewall.mac010203040506_443.src='lan'
# uci set firewall.mac010203040506_443.dest='wan'
# uci set firewall.mac010203040506_443.src_mac='01:02:03:04:05:06'
# uci set firewall.mac010203040506_443.proto=tcp
# uci set firewall.mac010203040506_443.dest_port=443
# uci set firewall.mac010203040506_443.target='REJECT'
# uci commit firewall
# /etc/init.d/firewall restart
Można blokować:
- protokół (
proto)
- port docelowy (
dest_port)
- adres IP docelowy (
dest_ip) - uwaga: podajemy tu adres ip lub nazwę domeny, nie pełny adres URL!
Należy pamiętać że jeżeli podamy adres domeny to w momencie tworzenia reguły zostanie ona zamieniona na odpowiedni jej adres IP i reguła będzie obowiązywała dla danego adresu IP.
Blokada określonych zasobów - po nazwie domeny
(nie po pełnym adresie url!)
Jeżeli domena ma więcej adresów to powyższy sposób zablokuje tylko jeden z nich i całość nie będzie działać tak jak trzeba. Wtedy można posłużyć się innym sposobem, na przykład z wykorzystaniem
ipset. Przykład do blokowania
facebooka:
# opkg update
# opkg remove dnsmasq
# opkg install dnsmasq-full
# /etc/init.d/dnsmasq enable
# opkg install ipset
# uci set firewall.facebook=ipset
# uci set firewall.facebook.name='facebook'
# uci set firewall.facebook.match='dest_ip'
# uci set firewall.facebook.storage='hash'
# uci set firewall.facebook.family='ipv4'
# uci set firewall.mac010203040506_facebook=rule
# uci set firewall.mac010203040506_facebook.src='lan'
# uci set firewall.mac010203040506_facebook.dest='wan'
# uci set firewall.mac010203040506_facebook.src_mac='01:02:03:04:05:06'
# uci set firewall.mac010203040506_facebook.proto=tcp
# uci set firewall.mac010203040506_facebook.ipset=facebook
# uci set firewall.mac010203040506_facebook.target='REJECT'
# uci add_list dhcp.@dnsmasq[0].ipset='/fb.com/facebook'
# uci add_list dhcp.@dnsmasq[0].ipset='/facebook.com/facebook'
# uci commit
# /etc/init.d/dnsmasq restart
# /etc/init.d/firewall restart
Oczywiście w takim przypadku urządzenie musi korzystać z routera jako serwera DNS, ponieważ to dnsmasq zajmuje się gromadzeniem adresów IP blokowanej domeny (wg przykładu: fb.com i facebook.com), a iptables korzystając z tych adresów robi blokadę.
Powyższy przykład też nie jest idealny, bo np. blokując witryny typu google możemy też zablokować inne usługi korzystające z tych samych adresów IP.
Opis firewalla można znaleźć na
wiki openwrt.
Aby zdjąć w/w blokady należy je albo usunąć z firewalla albo ustawić dodatkowo opcję
enabled=0 i zrestartować firewall, identycznie jak to przedstawiono w pierwszym przykładzie.
Dostęp czasowy - w określonych godzinach
Czyli np. tzw. kontrola rodzicielska. Ustawiamy tak firewalla, żeby zablokować dostęp do internetu od poniedziałku do piątku w godzinach 14.30 do 22:45:
# uci set firewall.mac010203040506_weekend=rule
# uci set firewall.mac010203040506_weekend.src='lan'
# uci set firewall.mac010203040506_weekend.dest='wan'
# uci set firewall.mac010203040506_weekend.src_mac='01:02:03:04:05:06'
# uci set firewall.mac010203040506_weekend.weekdays='mon tue wed thu fri' #lub '! sat sun'
# uci set firewall.mac010203040506_weekend.target='REJECT'
# uci set firewall.mac010203040506_weekend.start_time='14:30:00'
# uci set firewall.mac010203040506_weekend.stop_time='22:45:00'
# uci commit firewall
# /etc/init.d/firewall restart
UWAGA: router musi mieć ustawiony aktualny czas!
Aby zdjąć blokadę należy ją albo usunąć z firewalla albo ustawić dodatkowo opcję
enabled=0 i zrestartować firewall, identycznie jak to przedstawiono w pierwszym przykładzie.
Dostęp czasowy - ilość zużytego czasu
Inny przykład - chcemy zapewnić dostęp do internetu łączenie przez 2 godziny w ciągu dnia.
WiFi
Rozwiązanie dla klientów bezprzewodowych było już
przedstawione na forum i ten przykład z niego korzysta.
Robimy nowy skrypt:
# touch /usr/bin/timequotas.sh
# chmod 755 /usr/bin/timequotas.sh
Robimy edycję pliku (
vi /usr/bin/timequotas.sh), wpisujmy:
#!/bin/sh
HOST="01:02:03:04:05:06"
# limit w minutach
LIMIT=120
F=/tmp/timequota-$HOST
if [ -e $F ]; then
USEDTIME=$(cat $F)
else
USEDTIME=0
fi
T=$(iw dev phy0-ap0 station dump | grep -i -A 1 $HOST | awk '/inactive time/{print $3}')
[ -z "$T" ] && T=$(iw dev phy1-ap0 station dump | grep -i -A 1 $HOST | awk '/inactive time/{print $3}')
[ -z "$T" ] && exit 0
if [ $T -lt 60000 ]; then
USEDTIME=$((USEDTIME + 1))
echo $USEDTIME > $F
fi
if [ $USEDTIME -gt $LIMIT ]; then
if [ "x$(uci -q get firewall.mac${HOST//:/}.enabled)" != "x1" ]; then
uci set firewall.mac${HOST//:/}=rule
uci set firewall.mac${HOST//:/}.src='lan'
uci set firewall.mac${HOST//:/}.dest='wan'
uci set firewall.mac${HOST//:/}.src_mac=$HOST
uci set firewall.mac${HOST//:/}.target='REJECT'
uci set firewall.mac${HOST//:/}.enabled=1
uci commit firewall
/etc/init.d/firewall restart
fi
fi
exit 0
Skrypt odczytuje czy urządzenie o podanym adresie jest podłączone przez wifi. Jeżeli tak to zwiększa swój wewnętrzny licznik; jeżeli został przekroczony limit to tworzona jest reguła blokująca. Do działania niezbędne jest jeszcze wykonywanie tego skryptu w
cronie co minutę:
# echo "*/1 * * * * /usr/bin/timequotas.sh" >> /etc/crontabs/root
# /etc/init.d/cron restart
Zostaje tylko zniesienie limitu dziennego - to też w cronie, jeden raz o północy:
Robimy nowy skrypt:
# touch /usr/bin/timequotas-clean.sh
# chmod 755 /usr/bin/timequotas-clean.sh
Robimy edycję pliku (
vi /usr/bin/timequotas-clean.sh), wpisujmy:
#!/bin/sh
HOST="01:02:03:04:05:06"
echo 0 > /tmp/timequota-$HOST
uci set firewall.mac${HOST//:/}.enabled=0
uci commit firewall
/etc/init.d/firewall restart
exit 0
i sama zmiana w cronie:
# echo "0 0 * * * /usr/bin/timequotas-clean.sh" >> /etc/crontabs/root
# /etc/init.d/cron restart
Jeżeli chcemy dać dodatkowe np. 15 minut to robimy:
# echo $((120-15)) > "/tmp/timequota-01:02:03:04:05:06"
# uci set firewall.mac010203040506.enabled=0
# uci commit firewall
# /etc/init.d/firewall restart
120 to dzienny limit który jest sprawdzany w skrypcie, 15 - czas jaki został do nałożenia limitu.
Jeżeli chcemy usunąć blokadę to należy wykonać to samo co w cronie, czyli:
# echo 0 > "/tmp/timequota-01:02:03:04:05:06"
# uci set firewall.mac010203040506.enabled=0
# uci commit firewall
# /etc/init.d/firewall restart
lub wołamy skrypt
# /usr/bin/timequotas-clean.sh
Należy pamiętać że restart routera powoduje zlikwidowanie liczników, więc jeżeli ktoś chce je zachować to należy wszystkie pliki
/tmp/timequota-* systematycznie backupować i odtwarzać przy starcie routera. Lub jeżeli mamy np. extroota to zmienić ich położenie w skryptach wskazując na pendrive.
Połączenia kablowe
Dla połączeń kablowych sprawa się komplikuje, ponieważ host może mieć ustawiony np. statyczny adres IP. Z pewnym przybliżeniem można wykorzystać więc fakt, że jeżeli transmitował ostatnio jakieś dane to powinien być w tablicy
ARP. Robimy identyczne rozwiązanie jak dla wifi tylko należy zmodyfikować skrypt na następujący:
#!/bin/sh
HOST="01:02:03:04:05:06"
# limit w minutach
LIMIT=120
F=/tmp/timequota-$HOST
if [ -e $F ]; then
USEDTIME=$(cat $F)
else
USEDTIME=0
fi
T=$(grep -i -e "$HOST" /proc/net/arp | grep "0x2")
[ -z "$T" ] && exit 0
USEDTIME=$((USEDTIME + 1))
echo $USEDTIME > $F
if [ $USEDTIME -gt $LIMIT ]; then
iptables -C FORWARD -m mac --mac-source $HOST -j REJECT || \
iptables -I FORWARD -m mac --mac-source $HOST -j REJECT
fi
exit 0
Limit transferu
Chcemy ograniczyć dostęp np. na 50MB dziennie. Instalujemy odpowiedni moduł iptables:
# opkg install iptables-mod-extra
Na początek robimy regułę ograniczającą (na razie do testów, do wykonania w konsoli, żeby mieć orientację jak całość działa):
# QUOTA=$((50 * 1024 * 1024))
# iptables -N mac010203040506_limit50mb
# iptables -A mac010203040506_limit50mb -m quota --quota $QUOTA -j ACCEPT
# iptables -A mac010203040506_limit50mb -j REJECT
# iptables -A forwarding_rule -j mac010203040506_limit50mb -s 192.168.1.100
# iptables -A forwarding_rule -j mac010203040506_limit50mb -d 192.168.1.100
Powyższe reguły zakładają nowy łańcuch który zlicza ilość przesłanych danych od i z adresu IP 192.168.1.100. Jeżeli przekroczony zostanie podany limit to następuje przejście do następnej reguły, czyli w tym przypadku odrzucenie połączeń. I do tego łańcucha podłączamy nasz adres IP. Ale to tylko zlicza, więc należy też go czyścić co określony czas, czyli np. raz dziennie o północy wykorzystując
cron:
# echo "0 0 * * * iptables -Z mac010203040506_limit50mb " >> /etc/crontabs/root
# /etc/init.d/cron restart
Wykorzystując cron możemy zrobić odnowienie limitu dziennie, co miesiąc, w określonym dniu miesiąca, co dwa tygodnie lub np. w każdy czwartek. Zależy to tylko od tego jak zrobimy wpis w
cronie.
Jeżeli chcemy dodać inny adres IP do tego samego limitu - po prostu powielamy ostatnie dwie linię z innym adresem. jeżeli chcemy zrobić inny limit dla innego adresu - tworzymy ponownie nowy zestaw reguł. Adres IP można uzyskać np. z tablicy ARP, choć lepiej zrobić tzw.
static dhcp aby mieć pewność że adres IP dla danego MAC będzie zawsze taki sam.
W/w polecenia obowiązują tylko do restartu routera, identycznie jak zliczone limity. Jeżeli chcemy aby przetrwało restart sprzętu to musimy takie reguły tworzyć podczas startu/restartu firewalla. I tu mamy kolejny problem - restart firewalla powoduje wyzerowanie liczników, więc musimy także wstępnie ustawić zawartość liczników oraz cyklicznie je backupować. Więc teraz będzie ta trudniejsza cześć:
- skrypt backupujący
# touch /usr/bin/quotas-backup.sh
# chmod 755 /usr/bin/quotas-backup.sh
Robimy edycję pliku (
vi /usr/bin/quotas-backup.sh), wpisujmy:
#!/bin/sh
RULE=mac010203040506_limit50mb
O=$(iptables -vxnL $RULE 2>/dev/null)
PKTS=0
BYTES=0
if [ -n "$O" ]; then
PKTS=$(iptables -vxnL $RULE | awk '/quota:/{print $1}')
[ -z "$PKTS" ] && PKTS=0
BYTES=$(iptables -vxnL $RULE | awk '/quota:/{print $2}')
[ -z "$BYTES" ] && BYTES=0
fi
FBACKUP=/etc/quotas
touch $FBACKUP
sed -i '/'$RULE'/d' $FBACKUP
echo "$RULE $PKTS $BYTES" >> $FBACKUP
exit 0
Skrypt sprawdza czy istnieje określony łańcuch w iptables, jeżeli tak to pobiera liczbę pakietów i bajtów przesłanych a następnie wraz z nazwą łańcucha zapisywane jest to do pliku
/etc/quotas. Można sprawdzić działanie programu po prostu go wykonując:
# quotas-backup.sh
# cat /etc/quotas
Jeżeli widzimy nazwę łańcucha (w tym przypadku: mac010203040506_limit50mb) oraz dwie wartości (lub zera jeżeli nie było łańcucha) to wszystko działa. Teraz wystarczy umieścić wywołanie programu w cronie. Żeby nie zniszczyć flash routera będziemy dane backupować co 15 min:
# echo "*/15 * * * * /usr/bin/quotas-backup.sh" >> /etc/crontabs/root
# /etc/init.d/cron restart
- skrypt odtwarzający regułę i backup
Do pliku
/etc/firewall.user dopisujemy:
RULE=mac010203040506_limit50mb
touch /etc/quotas
PKTS=$(awk '/'$RULE'/{print $2}' /etc/quotas)
[ -z "$PKTS" ] && PKTS=0
BYTES=$(awk '/'$RULE'/{print $3}' /etc/quotas)
[ -z "$BYTES" ] && BYTES=0
QUOTA=$((50 * 1024 * 1024))
iptables -N $RULE
iptables -A $RULE -m quota --quota $QUOTA --set-counters $PKTS $BYTES -j ACCEPT
iptables -A $RULE -j REJECT
iptables -A forwarding_rule -j $RULE -s 192.168.1.100
iptables -A forwarding_rule -j $RULE -d 192.168.1.100
Skrypt jest lekką przeróbką w/w przykładu - z pliku z backupem odczytujemy wartości pakietów i bajtów dla danej reguły a następnie ją tworzymy ustawiając wstępnie liczniki. Wykonujemy ostatni test:
# /etc/init.d/firewall restart
# iptables -vxnL mac010203040506_limit50mb
Jeżeli wyświetliła się reguła z ustawionymi licznikami to zrobiliśmy właśnie limit transferu. Należy pamiętać że w cronie musi być jeszcze zerowanie limitu co określony czas, np raz dziennie:
# echo "0 0 * * * iptables -Z mac010203040506_limit50mb " >> /etc/crontabs/root
# /etc/init.d/cron restart
Jeżeli chcemy dodać jeszcze trochę transferu po blokadzie to należy albo wykonać to samo polecenie co wykonuje cron (
iptables -Z mac010203040506_limit50mb - spowoduje to odnowienie limitu) albo zmienić plik
/etc/quotas zmniejszając liczniki bajtów przy danej regule i restartując firewalla.
W powyższych regułach używamy adresu IP a nie MAC. Aby uzyskać bieżący adres IP mają zdefiniowany
static dhcp:
# uci -q get dhcp.$(uci show dhcp | grep '01:02:03:04:05:06' | cut -f2 -d.).ip
Limity przepustowości
Generalnie mówimy tu o tzw. QoS, temat został poruszony w
innym dokumencie. Rozwiązań i skryptów jest bardzo wiele, choć wszystkie bazują na zestawie narzędzi tc/ip/iptables.
Zakończenie
Praktycznie każde przedstawione ograniczenie można dość szybko wyłączyć i włączyć wg potrzeb, więc można zrealizować funkcjonalność "kuponów" zezwalających na dalszy dostęp do internetu.
Należy także pamiętać o tym że limity są usuwanie w cronie o określonej godzinie. Jeżeli router jest wyłączony w tym czasie to limity mogą nadal obowiązywać następnego dnia - nie będą odnowione.
Każda blokada powoduje odcięcie urządzenia od internetu co kończy się lakonicznym komunikatem "brak połączenia" w przeglądarce. Jeżeli mamy ręcznie tworzone reguły blokowania to można pokusić się o zrobienie prostego przekierowania na lokalną stronę www zawierającą informację o powodzie zablokowania.