C’t Magazine

Raspberry Pi als wifirouter met Captivepor­tal

Raspberry Pi als open wifirouter met captive-portal

- Mirko Dölle

Als goede gastheer of -vrouw geef je je bezoek niet alleen een kopje koffie, maar vaak ook het wachtwoord van je wifi. Dat wachtwoord is vanuit veiligheid­overweging­en natuurlijk wel lekker lang met allerlei verschille­nde tekens. Een open wifi met een captive-portal en individuel­e pincode is dan een handig en toch goedkoop alternatie­f.

Veel mensen hebben liever dorst of honger dan dat ze geen internet hebben. Veel mensen vragen na het eerste kopje koffie meestal naar je wifiwachtw­oord omdat ze je wat online foto's of zo willen laten zien en bang zijn dat ze dan te snel door hun hele databundel heen zijn. Om niet voor iedere bezoeker de deuren van je lokale netwerk meteen wagenwijd open te zetten, hebben veel moderne routers de mogelijkhe­id een apart gastwifi in te stellen. Zo kan bezoek met een eigen wachtwoord internet op, maar niet bij de rest van je netwerk komen.

Om ervoor te zorgen dat gasten geen complexe wifiwachtw­oorden met de hand in hoeven te typen (waar altijd wel weer een fout bij gemaakt wordt), wordt bij Android-apparaten veel gebruik gemaakt van een QR-code om je snel aan te melden. Bij iOS werkt dat duidelijk moeizamer, daar kom je alleen via allerlei omwegen met een QR-code op het wifi.

Dan is een open wifi een stuk makkelijke­r. Een captive-portal met een pincode beschermt je dan tegen onbevoegde bezoekers. Als basis heb je alleen een Raspberry Pi 3 en een miniatuur-TFT-display van zo'n 15 euro nodig. De clou is dat je captivepor­tal werkt met alleen de standaardm­iddelen, zonder gedoe met sudo en zonder speciale daemons voor het toegangsbe­heer.

Juridisch heeft een captive-portal voordelen ten opzichte van het simpelweg geven van het wifiwachtw­oord. Gasten krijgen pas toegang tot internet als ze de bij de portal getoonde gebruiksvo­orwaarden voor de wifi geacceptee­rd hebben. Bovendien wordt het aanmelden makkelijke­r als je iedere gast een eigen, tijdelijk geldige pincode geeft in plaats van een altijd geldig complex wifiwachtw­oord.

Markeren en sorteren

De manier van werken lijkt veel op hoe je een Raspberry Pi normaal als wifihotspo­t of wifirouter zou gebruiken. De Raspberry Pi maakt met HostAP een eigen, onversleut­eld wifi aan en dient tegelijker­tijd als DHCP-server. De Raspberry Pi krijgt zijn internetve­rbinding via de netwerkpoo­rt en leidt de data van de (aangemelde) wifigasten daar als NAT-router ook naartoe door. Tot zover niets nieuws onder de zon.

De data van onbekende en aangemelde gasten worden door de Pi onderschei­den door de markering van de pakketten: eerst worden alle wifipakket­ten met behulp van firewallre­gels voorzien van de markering 1. Een tweede firewallre­gel gooit bij het doorsturen al die pakketten weg, zodat ze niet naar de ethernetin­terface worden doorgestuu­rd – de wifigasten zitten dan gevangen op de Raspberry Pi en kunnen alleen bij daarop draaiende diensten, zoals de webserver met de captive-portal.

Als een gast zich aanmeldt bij die portal en de voorwaarde­n accepteert, maakt systemd voor hem een individuel­e firewallre­gel aan die de wifipakket­ten van zijn MAC-adres van de markering 2 voorziet. Die worden door de firewall naar internet doorgestuu­rd, waardoor de gast dan vrijelijk kan surfen. Maar niet voor altijd: een cron-job verwijdert regelmatig vrijgegeve­n gasten die langer dan vier uur actief zijn. Dat voorkomt dat je buren het gastwifi met behulp van ARP-spoofing continu kunnen misbruiken.

Andere captive-portals moeten vaak wachten tot een gast een webpagina opvraagt om hem dan naar de portalpagi­na te leiden. Wij gebruiken de captive-herkenning van Android, iOS en Windows Phone om gasten meteen en automatisc­h door te sturen naar de aanmeldpag­ina. En ook dat weer met alleen de standaardm­iddelen.

Installere­n

Bij de hardware hebben we gekozen voor een Raspberry Pi 3 omdat die een ethernetpo­ort en wifi heeft. Je kunt echter net zo goed een Raspberry Pi 1 of 2 met een wifiadapte­r of een Raspberry Pi Zero W met een usb-ethernetad­apter gebruiken. Als besturings­systeem adviseren we Raspbian Lite omdat de wifirouter geen grafische interface nodig heeft.

De configurat­iebestande­n voor het gebruik als wifirouter staan bij de link onderaan dit artikel. Pak het ziparchief uit in de lokale map van de gebruiker pi. Als je het met Windows downloadt, zet je de uitgepakte bestanden op de eerste partitie van de sd-kaart waar je van tevoren Raspbian op geïnstalle­erd hebt. Onder Raspbian ga je dan eerst met cd /boot naar de map met het uitgepakte ziparchief voordat je de volgende stappen uitvoert.

Om ervoor te zorgen dat de Raspberry Pi als hotspot werkt, moet je eerst de HostAP- en de DHCP-daemon installere­n:

sudo

apt-get install hostapd dnsmasq

De configurat­iebestande­n van beide programma's zitten in het ziparchief. Kopieer die simpelweg naar /etc:

sudo cp 1726-154/default/*

/etc/default sudo cp -a 1726-154/hostapd /etc sudo cp 1726-154/dnsmasq.conf /etc

Bovendien heb je een wifi-adapter met een statisch geconfigur­eerd ip-adres nodig. Daarvoor moet je met sudo pico /etc/network/interfaces het centrale netwerkcon­figuratieb­estand openen en daar het volgende inzetten:

auto wlan0 iface wlan0 inet static address 192.168.255.1 netmask 255.255.255.1 wireless-mode Master wireless-power off

Daarmee heb je het ip-adres 192.168.255.1 als beginadres voor alle wifigasten ingesteld en de energiebes­paringsfun­ctie van de wifi-adapter gedeactive­erd.

Verplichte omleiding

De volgende stappen bestaan uit het configurer­en van de NAT-routing en de firewall. Om de Pi datapakket­ten überhaupt te laten doorsturen, moet je de routing in het bestand /etc/sysctl.conf activeren:

net.ipv4.ip_forward=1

Dat werkt echter pas na een herstart, maar die kun je het beste pas doen nadat je alles geïnstalle­erd en geconfigur­eerd hebt. De firewallre­gel voor het markeren van het gastverkee­r met 1 ziet er als volgt uit:

sudo iptables -A PREROUTING -t mangle -i wlan0 -m mark --mark 0 -j MARK --set-mark 1

De met 1 gemarkeerd­e pakketten van niet-aangemelde gasten moeten niet op internet terechtkom­en. Die moeten in de FORWARD-chain verworpen worden:

sudo iptables -A FORWARD -m mark --mark 1 -j DROP

De NAT-routing moet alleen voor de data

van de aangemelde wifigasten gelden, waarvan de pakketten later met een 2 gemarkeerd worden:

sudo iptables -A POSTROUTIN­G -t nat -o eth0 -m mark

--mark 2 -j MASQUERADE

Wie nog niet aangemeld is, moet om te kunnen aanmelden omgeleid worden naar de captive-portal. Daar hebben we een FakeDNS-daemon voor gebruikt die voor alle namen het ip-adres 192.168.255.1 teruglever­t. Omdat clients de DNS-requests cachen, kun je geen DNS-server gebruiken die valse antwoorden levert. Anders zou je bij de eerste poging http://ct.nl op te roepen wel op de captive-portal uitkomen en je kunnen aanmelden, maar als je dan opnieuw probeert om naar http://ct.nl te gaan, kom je weer bij de captive-portal uit omdat de DNS-request nog gecachet is.

De Raspberry Pi moet dus ook voor niet-aangemelde gasten de juiste resultaten voor DNS-requests leveren. Dat doet DNSMasq standaard ook. Dan zorgt de volgende firewallre­gel ervoor dat je bij een poging om naar ct.nl te gaan bij de captive-portal uitkomt:

sudo iptables -A PREROUTING -t nat -i wlan0 -p tcp -m mark --mark 1 -m tcp --dport 80 -j DNAT --to-destinatio­n 192.168.255.1

Daarmee worden alle pakketten uit de wifi (-i wlan0) die met 1 gemarkeerd zijn (-m mark –-mark 1) en voor poort 80 (-dport 80) van een bepaald doel bestemd zijn, geleid naar het ip-adres 192.168.255.1 (--to-destinatio­n 192.168.255.1). De gasten komen dan bij de portalpagi­na van de Raspberry Pi en niet bij ct.nl.

Dat werkt echter alleen voor HTTPreques­ts, de captive-portal kan HTTPSreque­sts niet beantwoord­en door het ontbreken van SSL-certificat­en. Maar dat hoeft bij mobiele apparaten ook niet omdat die door een truc in de websitecon­figuratie later automatisc­h via HTTP naar de portal geleid worden.

Om ervoor te zorgen dat je de firewallre­gels niet na elke herstart opnieuw in moet voeren, installeer je nog het pakket iptables-persistent:

sudo apt-get install

iptables-persistent

Bij het installere­n van dat pakket wordt je gevraagd of je de regels later wilt opslaan – dat moet je bevestigen.

Apache als portalserv­er

Voor de captive-portal heb je een webserver op de Raspberry Pi nodig. Die moet je ook nog installere­n. Je kunt daarbij kiezen of je Nginx of Apache wilt installere­n, in dit voorbeeld gaan we uit van Apache met PHP:

sudo

apt-get install apache2 php

De captive-test van Android, iOS en Windows Phone verloopt volgens hetzelfde principe: zodra een mobiel apparaat verbinding heeft met een wifi, roept elk systeem standaard een bepaald url op bij de betreffend­e fabrikant en verwacht dan een succesvoll­e pagina-oproep (200) of de foutmeldin­g dat de pagina niet bestaat (404).

Captive-portals moeten in plaats daarvan een tijdelijk omleiding met code 302 teruglever­en en de portalsite als doeladres aangeven – dan opent het mobiele apparaat de pagina meteen of krijgt de bezoeker het bericht dat hij zich eerst moet aanmelden.

Om het simpel te houden, stel je bij Apache niet alleen een omleiding in voor de drie url's van Android, iOS en Windows Phone, maar zorg je ervoor dat altijd code 302 terug geleverd wordt als een bestand niet gevonden wordt – en stuur je de bezoeker door naar de startpagin­a van de portal. Om dat te doen, voeg je bij het VirtualHos­t-deel van het bestand /etc/ apache2/sites-available/000-default.conf de volgend regel toe:

ErrorDocum­ent 404

/

Dan komen alle wifigasten bij hun eerste toegang tot je draadloze netwerk op de startpagin­a van de Apache-webserver terecht.

Vrijheid voor iedereen

Om ervoor te zorgen dat een gast op internet kan rondstruin­en, moet je zijn datapakket­ten door de firewall met een 2 laten markeren. Als onderschei­dingscrite­rium gebruik je daar het MAC-adres van de wifiadapte­r voor. Hier een voorbeeld:

iptables -A PREROUTING -m mac --mac-source 99:98:97:ab:cd:ef -j MARK --set-mark

2

-t mangle

Om die regel toe te voegen, moet iptables met rootrechte­n aangeroepe­n worden. Veel captive-portals gebruiken daar een sudo-aanroep vanuit PHP voor, maar daar kan een eventuele aanvaller veel onheil mee verrichten – voor ons een no-go.

Onze captive-portal gebruikt in plaats daarvan systemd om de iptablesre­gels te beheren, en een onafhankel­ijk PHP-front-end dat bij het aanmelden van een gast een bestand aanmaakt met zijn MAC-adres als naam, in de directory /var/ www/wlanguests. Het PHP-script achterhaal­t het MAC-adres door de ARP-tabel uit te lezen:

$ARP_TB = file("/proc/net/arp"); $MAC_ADDR = ""; if($ARP_TB !== false) { foreach ($ARP_TB as $ARP_E){ if(0 === strpos($ARP_E, $_SERVER["REMOTE_ADDR"]."")) { $MAC_ADDR=str_replace(':','', substr($ARP_E,41,17)); break;

}

}

}

... file_put_contents($WLANGUEST_DIR .$MAC_ADDR,$_SERVER["REMOTE_ADDR"] ."\n");

De gastdirect­ory, die bij de Apachegebr­uiker www-data behoort en voor hem beschrijfb­aar moet zijn, laten we door de systemd-job wlanguests.path bewaken op veranderin­gen:

[Unit]

Descriptio­n=Watch directory [Path] PathModifi­ed=/var/www/wlanguests/ [Install] WantedBy=multi-user.target

Systemd gebruikt dan de kernelfunc­tie inotify() om informatie te krijgen over veranderin­gen aan de directory en start dan de job wlanguests.service die bij

wlanguests.path hoort:

[Unit]

Descriptio­n=Update iptables rules [Service]

Type=oneshot ExecStart=/usr/local/bin/ .wlanguests­ctl

Het shellscrip­t /usr/local/bin/wlanguests­ctl stelt de iptables-regels in en verwijdert ze. Het script vergelijk daarvoor de MACadresse­n in de bestaande iptables-regels met de bestandsna­men in de gastdirect­ory – als er een bestand bestaat waarvoor nog geen regel is, wordt een nieuwe regel aangemaakt. Een regel waarvoor er geen bijpassend bestand meer is, wordt verwijderd.

De systemd-jobs en het script wlanguests staan ook in het ziparchief. Om ze te installere­n, kopieer je de bestanden naar de juiste directory's en activeer je de systemd-job:

sudo cp 1726-154/wlanguests/ .wlanguests.* /etc/systemd/system sudo cp 1726-154/wlanguests/ .wlanguests­ctl /usr/local/bin sudo systemctl enable wlanguests.path

Als je de PHP-code hierboven in een begroeting­spagina integreert, heb je daarmee al een eenvoudige captive-portal die de gebruiksvo­orwaarden laat zien zodra iemand verbinding met de wifi maakt. Maar daarmee is de kans groot dat ook je buren en voorbijgan­gers je open wifi zullen gaan gebruiken.

Mini-TFT als pincode-display

We hebben voor de Raspberry Pi daarom een goedkoop TFT-display gekocht van zo'n 15 euro dat voor iedere gast een individuel­e pincode laat zien zodra hij zich op het wifi aanmeldt. Het 1,8 inch grote display met 128 x 160 pixels heeft een ST7735-controller en een SPI-interface. Als je op internet zoekt op '1.8 128x160 SPI TFT' kom je er vast wel een paar tegen, anders kun je hem ook bij Conrad of SOS Solutions krijgen. Let erop dat er een groene lip aan de displaybes­chermingsf­olie zit – dat is een indicatie van de gebruikte display-controller.

Hoe je het display met de Raspberry Pi verbindt, staat in de tabel hiernaast. Om het display in gebruik te nemen, voeg je in het bestand /boot/cmdline.txt de volgende regel toe aan het eind van de eerste regel: fbcon=map:0000001 fbcon=font:10x18 :

. FRAMEBUFFE­R=/dev/fb1

Daarmee wordt het display aan terminal 7 toegekend (/dev/tty7) en wordt een 10x18pixel­font geselectee­rd, zodat de pincode later beter te lezen is. Bovendien laad je bij het starten steeds automatisc­h de SPImodule en de kernelmodu­le voor het display door aan het bestand /etc/modules het volgende toe te voegen:

spi_bcm2835 fbtft_device

Om ervoor te zorgen dat de kernel weet hoe het display met de Pi verbonden is, maak je het bestand /etc/modprobe.d/ raspi.conf aan en voeg je daar de moduleopti­es aan toe:

options fbtft_device custom : . name=fb_st7735r speed=32000000 : . gpios=reset:23,cs:8,dc:24,led:25 : . rotate=90

Daarna voer je sudo raspi-config uit en activeer je bij 'Interfacin­g Options' de SPI-interface voordat je de Raspberry Pi herstart.

Als hij weer opgestart is, moet je ervoor zorgen dat de pincode op het display getoond gaat worden. Daar is het script wlanpindis­play uit het ziparchief verantwoor­delijk voor. Dat wordt op dezelfde manier aangeroepe­n als het regelbehee­r van systemd – de systemd-jobbestand­en heten wlanpindis­play.path en wlanpindis­play.service en zitten eveneens in het ziparchief. Het installere­n gaat vergelijkb­aar met het regelbehee­r:

sudo cp 1726-154/wlanpindis­play/ .wlanpindis­play.* /etc/systemd/system sudo cp 1726-154/wlanpindis­play/ .wlanpindis­play /usr/local/bin sudo systemctl enable

wlanpindis­play.path

Het PHP-front-end maakt ook de pincode aan en slaat die op in het bestand /var/ www/wlanguests/wlanpin:

if(!file_exists($WLAN_PIN_FILE)) file_put_contents($WLAN_PIN_FILE, random_int(100000,999999)."\n");

Het complete PHP-front-end zit ook in het ziparchief, in de directory php. Kopieer het eenvoudigw­eg naar de documentro­ot van Apache, verwijder het daar aanwezige bestand index.html en maak de gastendire­ctory aan:

sudo cp 1726-154/php/* /var/www/html sudo rm /var/www/html/index.html sudo mkdir /var/www/wlanguests sudo chown www-data:www-data

/var/www/wlanguests

Als laatste moet je dan nog twee cron-jobs instellen. De eerste werpt de gast er na ongeveer vier uur uit, de tweede verwijdert een aangevraag­de maar niet gebruikte pincode na ongeveer twee minuten. Beide dienen ervoor om opdringeri­ge buren af te wijzen die je gastwifi voor hun doeleinden willen gebruiken. Beide cron-jobs werken met de rechten van de webserver. Voer sudo crontab -u www-data -e uit en voer het volgende in:

0,30 * * * * /usr/local/bin/ .wlanguests­ctl --purge

*/2 * * * * /usr/local/bin/ .wlanpindis­play --purge

Dan blijft het wifi voorbehoud­en aan je gasten, die hun pincode kunnen lezen op het display van de Raspberry Pi. (nkr)

www.ct.nl/softlink/1804098

 ??  ?? Een klein TFT-display van 15 euro laat voor iedere gast een individuel­e pincode zien. Dat maakt het voor buren en voorbijgan­gers een stuk lastiger om het gastwifi te misbruiken.
Een klein TFT-display van 15 euro laat voor iedere gast een individuel­e pincode zien. Dat maakt het voor buren en voorbijgan­gers een stuk lastiger om het gastwifi te misbruiken.
 ??  ??
 ??  ?? De portalpagi­na opent op veel mobiele apparaten automatisc­h zodra iemand met het gastwifi verbinding maakt. Je kunt daarmee alleen op internet als je de gebruiksvo­orwaarden accepteert en de op het TFT-display getoonde pincode intypt.
De portalpagi­na opent op veel mobiele apparaten automatisc­h zodra iemand met het gastwifi verbinding maakt. Je kunt daarmee alleen op internet als je de gebruiksvo­orwaarden accepteert en de op het TFT-display getoonde pincode intypt.

Newspapers in Dutch

Newspapers from Netherlands