mrau91
Goto Top

Beurteilung Programmcode, Wlan AP mit selbst geschriebenem Captive Portal in bash

Ich bin relativ neu im Umgang mit Linux und möchte meine Programmierkenntnisse vertiefen.
Deshalb interessiert mich eure Meinung zu meinem Code.
Falls jemand Zeit und Lust hat kann er gerne Kritik üben, denn nur so lernt man.

Ich brauchte für einen privaten Fall einen WLAN Hotspot der einen Internetzugang für bestimmte Geräte zulässt.
Ein ganz einfaches System, dass genauso einfach zu bedienen ist.
Umgesetzt mit einem Raspberry Pi 3b+.
Mir ist klar, dass der Pi für diesen Zweck eher ungeeignet ist, allerdings war es vorhanden und reicht als Testgerät vollkommen aus (2-10 Clients Zeitgleich).
Logging des Traffics sowie die Traffic Performance sind nicht relevant.
Es geht schlicht darum den Zugang zum Internet zu managen.
Mir ist auch bekannt, dass diese Funktion eine Firewall oder durch am Markt erhältliche AP's bereitgestellt werden kann.
Ich dachte mir bevor ich die Zeit in die Recherche und Umsetzung einer vorhandenen Lösung investieren (und es im Nachhinein dann doch nicht den Anforderungen zu 100 % entspricht, oder total oversized ist), schreib ich mir den Code selbst und lern dabei.

Anleitung Raspberry als WLAN AP betreiben von aqui:
Netzwerk Management Server mit Raspberry Pi

Raspberry Pi dient als Wlan AP und managed den Traffic.
Per default sperre ich jeglichen Traffic.
SSID und Passwort hängen aus, es kann sich also jeder der Zugang zu den Räumlichkeiten hat mit dem AP verbinden.
Will ein User Internetzugang kann er diesen (vorausgesetzt er hat einen Code) über eine Website des Pi's freischalten.
Der Code wird geprüft und dann die IP und die MAC gespeichert inkl. der Zeit bis wann der Client Zugang erhält.
Über eine Regel in den iptables wird der Zugang für den Client freigegeben.
Jede Nacht um 12 läuft ein Script, dass überprüft ob die Zeit der jeweiligen Clients abgelaufen ist.
Hier rechne ich ausschließlich in Tage, (Stundenweise ist nicht interessant, der aktuelle Tag wird nicht in die Rechnung mit einbezogen.
D.h. ein 1 Tages Pass schaltet den Internetzugang frei bis zum Ablauf des darauffolgenden Tages.

Die Codes können in einer Datei definiert werden.
Für diesen Fall habe ich einen Telegram Chat Bot eingerichtet, der folgende Funktionen bereitstellt:
- Codes eingetragen
- versendet Bestätigung wenn sich ein Client erfolgreich registriert
- Übersicht der freigeschaltenen Clients anzeigen mit Zeitangaben
- Übersicht der aktuellen Codes anzeigen
Der Code besteht aus 8 Zahlen, die ersten 6 sind individuell und die letzten 2 definieren die Dauer, die der Client Zugang erhält.
Die Codes gelangen auf 2 Wegen zu den Usern:
- Neuer Code wird per Telegram über den Chat Bot eingetragen und dann per Telegramm/Whatsapp an den User weitergeleitet.
- Codes werden vorab in der codes Datei definiert und dann ausgedruckt und im Bedarfsfall weitergegeben.
Die Codes könnten auch über eine weitere Website oder direkt durch bearbeiten der Codes Datei eingetragen werden.


Inhalt von wlangeraete.txt in der Form:
Ip-Adresse Mac-Adresse yy Tag-des-Jahres(1-365) Client-Name
192.168.xxx.xxx xx:xx:xx:xx:xx:xx 19 85 Name

Inhalt von codes.txt in der Form:
xxxxxx01 # 1 Tages Pass
xxxxxx07 # 1 Wochen Pass
xxxxxx30 # 1 Monats Pass
xxxxxx00 # 1 JahresPass



wlansysstart.sh # erstellt Forward Routen in den iptables. Wird beim Systemstart, bei neuer Userfreischaltung und beim User löschen ausgeführt.
sudo iptables -F # alle Forward Routen der iptables löschen

sudo iptables -t nat -A POSTROUTING -j MASQUERADE #maskierung
sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null #weiterleitung
sudo iptables -A FORWARD -o eth0 -i wlan0 -j DROP #alles blocken

URLFILE=/home/pi/wlangeraete.txt # Liste der zugelassenen Wlan Geräte

while read line || [ "$line" != "" ] #Liste auslesen und mit dem Inhalt eine Forward Route erstellen  
do
ipaddr=$(echo $line | awk '{ print $1 }')  
macaddr=$(echo $line | awk '{ print $2 }')  
sudo iptables -I FORWARD -o eth0 -i wlan0 -s $ipaddr -m mac --mac-source $macaddr -j ACCEPT
done < $URLFILE



checkgeraete.sh # wird nächtlich um 24 Uhr ausgeführt, prüft ob Client Zeit abgelaufen ist
typeset -i jahrist=$(date +%g) # aktuelles Jahr im Format yy speichern

# date +%j gibt den tag des Jahres 1-365 als 3 Ziffern aus(z.b.: 007, 056, 244). folgende Fallunterscheidung entfernt die Nullen am Anfang.
if [ $(echo $(date +%j) | cut -c1-2) = 00 ]; then
    typeset -i datumist=$(echo $(date +%j) | cut -c3)
elif [ $(echo $(date +%j) | cut -c1) = 0 ]; then
    typeset -i datumist=$(echo $(date +%j) | cut -c2-3)
else
    typeset -i datumist=$(echo $(date +%j))
fi


URLFILE=/home/pi/wlangeraete.txt # Liste der zugelassenen Wlan Geräte

while read line || [ "$line" != "" ]  
do
    ipaddr=$(echo $line | awk '{ print $1 }') # ip aus wlangeraete.txt  
    macaddr=$(echo $line | awk '{ print $2 }') # mac aus wlangeraete.txt  
    typeset -i jahr=$(echo $line | awk '{ print $3 }') # jahr aus wlangeraete.txt  
    typeset -i datum=$(echo $line | awk '{ print $4 }') # tag aus wlangeraete.txt  
    ausdruck=$line

# Prüfen ob Client Zeit abgelaufen ist und ggf. aus Liste wlangeraete.txt löschen
    if [ $jahr -lt $jahrist ]; then
        sed -i '/'$ipaddr'/d' $URLFILE  
    elif [ $jahr = $jahrist ]; then
        if [ $datum -lt $datumist ]; then
            sed -i '/'$ipaddr'/d' $URLFILE  
        fi
    fi
done < $URLFILE

bash /home/pi/wlansysstart.sh # script um iptables zu aktualisieren



get-mac-addr.sh # wird ausgeführt wenn ein User sich über die Website des Pi's mit einem Code registriert
Das PHP script übergibt die Client IP und den eingegebenen Code und zeigt dem User die echo Ausgabe dieses Scripts.
check=0 # variable für erfolgreiche Code Prüfung
URLFILE=/home/pi/codes.txt # Datei mit den Freischaltcodes

# prüfen ob der Code in der Datei enthalten ist
for i in `cat $URLFILE`; do
    if [ $3 = $i ]; then
        echo "Code check ok"  
        loeschen=$3 # Code zwischenspeichern um diesen am ende zu löschen
        check=1
    fi
done

# Code länge überprüfen mit Fehlerausgabe
laenge=$(expr length "$3")  
if [ $laenge != 8 ]; then
    echo "falsche code l&auml;nge"  
    exit
fi

# Fehlerausgabe bei falschem Code
if [ $check != 1 ]; then
    echo "falcher code"  
    exit
fi


ipaddress="$1" # von PHP script übergebene IP-Adresse  
interface="$2" # von PHP script übergebenes Interface, in diesem Fall wlan0  

# ip Adresse validieren
function valid_ip(){
    local  ip=$1
    local  stat=1
 
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'  
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}
 
# wenn ip validiert, dann Mac ermitteln
if valid_ip $ipaddress; then
    result=$(/usr/sbin/arping -i $interface $ipaddress -c 6 | grep '('$ipaddress')' | grep index=0 | awk '{ print $4 }')  
    #echo $result;
fi


typeset -i jahr=$(date +%g) # aktuelles Jahr im Format yy speichern

# date +%j gibt die den tag des Jahres 1-365 als 3 Ziffern aus(z.b.: 007, 056, 244). folgende Fallunterscheidung entfernt die Nullen am Anfang.
if [ $(echo $(date +%j) | cut -c1-2) = 00 ]; then
    typeset -i datum=$(echo $(date +%j) | cut -c3)
elif [ $(echo $(date +%j) | cut -c1) = 0 ]; then
    typeset -i datum=$(echo $(date +%j) | cut -c2-3)
else
    typeset -i datum=$(echo $(date +%j))
fi

dauer=$(echo $3 | cut -c7-8) # die letzten 2 Ziffern des codes speichern

# ermittlung der Dauer der Freischaltung über die letzten 2 Ziffern
if [ $dauer = 01 ]; then
    echo "Dauer: 1 Tag"  
    datum+=1
elif [ $dauer = 07 ]; then
    echo "Dauer: 1 Woche"  
    datum+=7
elif [ $dauer = 30 ]; then
    echo "Dauer: 1 Monat"  
    datum+=30
elif [ $dauer = 00 ]; then
    echo "Dauer: 1 Jahr"  
    jahr+=1
elif [ $dauer = 99 ]; then
    echo "endless"  
    jahr=99
fi

# Falls die Tagesausgabe den Wert 365 übersteigt, wird ein Jahreswechsel gemacht
if [ $datum -gt 365 ]; then
    datum=$datum-365
    jahr+=1
fi

# Ermittlung des Client Namens, nur für die Übersicht
name=$(nmap -sP $ipaddress | grep 'scan report' | awk '{ print $5 }')  

# Fehler falls Client Name nicht ermittelt werden konnte
if [ -z $name ]; then
    echo "Error name: Freischaltung fehlgeschlagen. Bitte erneut versuchen"  
    exit
fi

# Fehler falls mac nicht ermittelt werden konnte
if [ -z $result ]; then
    echo "Error mac: Freischaltung fehlgeschlagen, Bitte erneut versuchen"  
    exit
fi

# Client Daten speichern
ausgabe=$ipaddress" "$result" "$jahr" "$datum" "$name  
echo $ausgabe >> /home/pi/wlangeraete.txt

# löschen des Codes
sed -i '/'$loeschen'/d' $URLFILE  

echo 'Freischaltung erfolgreich'  

sleep 1s
bash /home/pi/wlansysstart.sh

website_screenshot

Content-Key: 431060

Url: https://administrator.de/contentid/431060

Printed on: April 27, 2024 at 10:04 o'clock

Member: aqui
Solution aqui Mar 21, 2019 updated at 09:04:58 (UTC)
Goto Top
Tolles Projekt und für eine kleine Installation wie einen HotSpot an einen Kiosk usw. reicht das doch allemal auf dem RasPi face-wink
An allem gibts nichts zu meckern. Evtl. nur das du das WLAN verschlüsselst, denn das ist eigentlich völlig sinnfrei.
Wenn du das Passwort so oder so öffentlich machst ist das einen vollkommen überflüssige Hürde für User. Da ist es besser wenn du das WLAN offen betreibst wie es in der Regel auch bei Hotspots üblich ist !
Für jemander der das nachvollziehen will solltest du ggf. noch einen Tip geben wie man den RasPi als WLAN Accesspoint betreibt und wie man die Vouchers erhält. face-wink
Netzwerk Management Server mit Raspberry Pi
Member: NetzwerkDude
Solution NetzwerkDude Mar 21, 2019 at 09:32:30 (UTC)
Goto Top
Cooles Tool.
Sitz hier am Handy und kann es daher nicht so in depth beurteilen - sieht aber auf den ersten Blick gut aus.
Schätze mal $3 wird im php skript validiert? (Damit der User keinen schabernack ans skript übergibt)
Member: mrau91
mrau91 Mar 21, 2019 updated at 13:41:04 (UTC)
Goto Top
@aqui
Danke für die Rückmeldung, deine Anmerkungen habe ich ergänzt.

@NetzwerkDude
Danke für den Hinweis. $3 hatte ich bisher nicht validiert. Nun prüfe ich im PHP script auf Sting Länge und ob alle Zeichen Zahlen sind, bevor ich diese weitergebe.