PiBake

auf die Schnelle: eine WLAN-Bake

Bei einer Autofahrt mit einem Parteifreund kam uns die Idee, man könnte mittels WLAN-Baken - also Geräten, die ein WLAN aufspannen z.B. mit einem klingenden Namen - auch Wahlkampf oder sonstige Werbung machen.
Und das Konzept wurde dann immer ausgefeilter, wie das so ist, wenn zwei diplomierte Ingenieure so ins Brainstorming kommen.

Bei weiteren Recherchen viel dann auf, dass so ein Projekt durchaus eine Anwendung finden könnte.
Und dass es ggf. als Blaupause und Anleitung dienen könnte für andere Leute.

Daher haben wir uns entschlossen, dass ich eine Anleitung zum Bau so eines Gerätes erstellen.

Also machen wir das jetzt hier.

Schritt für Schritt

Die Grundidee dieser Seite ist, dass hier ein einfaches Kochrezept dargestellt wird, das man einfach Schritt für Schritt nachvollziehen kann und so zum fertigen Gerät kommt.
Wenn ich aus dem erhaltenen feedback Interesse verspüre, dann mache ich zu den Einzelschritten noch separate Seiten, die den jeweiligen Schritt genauer darstellen.

Bin gespannt, wie das klappt.
Kommentare und Anregungen unter mailto://hase@hase.net sind also durchaus erwünscht.

Übersicht

Was brauchen wir?

Zunächst mal einen RaspberryPi in der Version 3, was zum Zeitpunkt dieses Projektes die aktuelle Fassung darstellt.
Der 3er-Raspi enthält bereits ein WLAN Funkmodul, das bei älteren Modellen erst nachgerüstet werden müsste, z.B. als USB-Stick.
Mit der Kombo aus einem Raspbi-V2 und USB-Wlan-Stick müste die Anleitung auch funktionieren, aber probiert habe ich es nur mit der V3 und dem dort integrierten WLAN.

Dann eine Speicherkarte Typ Micro-SD mit mindestens 8 GB.
Ich empfehle eine 16- oder 32-GB Karte, Markenhersteller aus zuverlässiger Quelle.
Schnelle Karten machen mehr Freude als langsame, aber die Markengeräte sind heutzutage alle sehr gut, weil für den Einsatz in Handies vorgesehen, die schnelle Karten besonders mögen.

Dann eine Stromversorgung.
Für stationären Betrieb wäre das ein Netzteil - und da ist der Raspi etwas anspruchsvoll.
Spannender erscheint mir aber der mobile Betrieb über eine PowerBank.
Hier würde ich ein Modell empfehlen, das bei angeschlossenem Ladekabel immer noch Strom auf den Ausgangsbuchsen liefert.
Selbstverständlich ist das nicht, bei mehreren von mir gekauften Geräten werden die USB-Ausgänge deaktiviert während die PowerBank lädt.

Grosse Akkusysteme, die für den Betrieb von Laptops oder gar als Starthilfebox für Kraftfahrzeuge vorgesehen sind, liefern nach meiner Erfahrung permanent die 5V am USB-Ausgang.
Mit so einer PowerBank lässt sich dann sehr bequem zwischen Mobil- und Stationärbetrieb wechseln.
Verbunden wird die USB-Stromquelle über ein Micro-USB-Kabel mit dem Pi.
Ein möglichst kurzes Kabel hoher Qualität (grosser Leitungsquerschnitt für minimale Verluste; gute Ladekabel bieten das) macht hier mehr Freude als Billigware.

Zum Einrichten benötigen wir einen Internetzugang für den neuen Raspbi, der über (verkabeltes) Ethernet bereitgestellt wird:
da das Wlan ja als AccessPoint konfiguriert wird, kann es nicht gleichzeitig die Netzwerk-Anbindung des Raspi herstellen.
Zum Einrichten benötigen wir weiterhin eine Tastatur mit USB-Anschluss sowie einen Monitor mit HDMI-Eingang und das passende Verbindungskabel.
DVI-Monitore sollten mit dem passenden Adapterkabel ebenfalls funktionieren.
Und wir benötigen letztlich einen Computer mit dem wir eine Mikro-SD-Karte beschreiben können.
Ich verwende einen Linux-Computer, erfahrene Windows-Anwender sollten alle Schritte jedoch analog nachvollziehen können.
Dieses Zubehör sollte sich in vielen Haushalten finden.

Schritt 1: Raspbian herunterladen.

Auf der Seite der RaspberryPi Foundation finden wir unter downloads das gewünschte Image.
Für die Anleitung verwende ich die Variante Raspbian Stretch Lite

Schritt 2: Auf die SD-Karte schreiben

Unter Linux ist das besonders einfach: image entpacken (unzip) auf die SD-Karte schreiben (dd)

Die offizielle Installationsanleitung der Foundation empfiehlt für windows-Anwender das Programm Etcher. Dieser Empfehlung schliesse ich mich an.

Schritt 3: erster Boot

Das frisch installierte Image wird hier das erste Mal gestartet.
Dazu schliessen wir die Tastatur an einen beliebigen USB-Port an, verbinden den HDMI-Ausgang mit dem Monitor und schliessen zuletzt die Stromversorgung an.

Der Monitor sollte zuvor eingeschaltet sein, damit man den Bootvorgang komplett sehe kann.
Zunächst wird ein Farbmuster dargestellt, dann erscheint die Textkonsole des Pi mit 4 Himbeeren in der linken oberen Ecke des Bildschirms (ältere Modelle zeigen hier eine oder zwei Himbeeren; das stellt die Anzahl der CPU-Kerne dar).

Achtet dabei - und während der ganzen Instalaltion - bitte genau auf die rechte obere Ecke des Monitorbildes.
Erscheint hier ein gelber Blitz, dann hat der Raspi eine Unterspannung der Stromversorgung festgestellt.
Von einem Betrieb mit so einer Stromversorgung rate ich dringend ab.
Es scheint zwar zunächst alles gut zu funktionieren, aber die SD-Karte wird dann sehr schnell ausfallen; viele Gerüchte um die "Unzuverlässigkeit" von "Bastellösungen" mit Raspis gehen auf diesen Umstand zurück.
Die Foundation empfiehlt daher Netzteile, deren Spannung sich am oberen Ende der USB-Spezifikation bewegen (5,2V oder 5,25V), viele Powerpanks liefern ähnliche Spannungen.
Übrigens: die Versorgung mit 5V über eine USB-Buchse für ein Gerät, das selber USB-Buchsen hat, halte ich für die grösste Schwäche des RaspberryPi-Konzeptes überhaupt.

Das Gerät sollte jetzt booten.

Schritt 4: Grundkonfiguration des Pi

Auf der Konsole loggen wir uns ein mit dem benutzer pi und dem Passwort raspberry.
Achtung Anfängerfalle: auf einer deutschen Tastatur ist das als raspberrz einzutippen...
Jetzt gilt es, die Grundkonfiguration herzustellen mittels sudo raspi-config.

Als erstes wählen wir unter Advanced Options den Punkt Expand Filesystem.
Damit wird das Dateisystem auf der SD-Karte so modifiziert, dass wir nach einem Neustart die gesamte Kapazität nutzen können.
Danach verlassen wir raspbi-config mit finish und starten den Pi nue mit sudo shutdown -r now.
Dann wieder einloggen (wie oben) und mit sudo raspbi-config in die Konfiguration einsteigen.
Jetzt den Punk Update wählen, um raspbi-config auf die aktuelle Version zu bringen.
Danach bitte unbedingt den Punkt Change User Password ausführen.
Dies ändert das Passwort für den Benutzer pi (der bei Raspbian der Standard-Benutzer ist).
Passwort merken, sonst von vorn :-)

Nun die anderen Grundeinstellungen, hier im Schnelldurchlauf:

Network Options

  • Hostname: ich vergebe immer einen Hostnamen nach der Funktion des Geräts. Z.B. Bake
  • Wi-Fi: unverändert lassen
  • Network Interface Names: "yes" für "predictables names" anwählen

Boot Options

  • Desktop/Cli: Console (Textkonsole, ohne grafischen desktop denn: wir bauen einen Server)

localization options

  • Change Locale: nach Wunsch, ich bleib immer bei UK-English
  • Change Timezone: auf deutsche Zeitzone (Büsingen oder so) einstellen
  • Change Keyboard: auf das verwendete Keyboard einstellen; bei mir "german"
  • Change Wi-fi Country: auf DE einstellen.

Grad das Setting für Wi-fi Country ist wichtig: damit stellt man das Funkmodul so ein, dass nur mit zulässigen Sendeleistungen in den hierzulande zulässigen Bändern gefunkt wird.
Soll das Gerät ausserhalb Deutschlands eingesetzt werden, dann muss hier das Land eingestellt werden, wo der betrieb vorgesehen ist.

Interfacing Options

SSH: einschalten, alles andere aus. (Wer mag aktiviert eine serielle Konsole, aber wenn Du damit was anfangen kannst, warum liest Du das hier überhaupt? :-)

Overclocking:

geht bei V3-Pi eh nicht, ist auch sonst selten ratsam, weil Überhitzung droht.

Advanced Options

  • Overscan hilft mit manchen Monitoren, ausprobieren.
  • Memory Split: auf 16 stellen (nur 16MB für die GPU - mehr für die CPU, denn wir bauen einen Server)
  • Resolution: ggf. anpassen an den eigenen Monitor
  • Pixel Doubling: ggf. ebenfalls anpassen für ein lesbareres Bild
  • GL driver: aus

Zum Abschluss dann neu starten ("yes" bei "reboot now" oder auf der Kommandozeile sudo shutdown -r now)

Schritt 5: Raspbian updaten, benötigte Software installieren

Zunächst müssen wir uns hier wieder einloggen, ich würde sagen per ssh.
Dazu gilt es zunächst, die IP-Adresse des neuen Pi herauszufinden.
Bei einem OpenWRT-Router zeigt der Statusbildschirm die zur Zeit vergebenen Adressen; Im Screenshot sehen wir, dass der Router für manche Geräte einen Hostnamen kenn, für andere Geräte nicht; der Pi gehört offenbar zu denen, die keinen Namen im DHCP-Request senden - was schade ist.
Dennoch finden wir über den DHCP-Status des Routers die gesuchte IP-Adresse, hier die 192.168.42.170: screenshot of router configuration

Auf der Kommandozeile können wir uns nun mit ssh pi@<IP-Adresse> also ssh pi@192.168.42.170 auf dem Pi einloggen.
Das Passwort sollte im Moment noch das Standardpasswort für Raspbian sein (raspberry).

Als erstes ändern wir nun dieses Standardpasswort:

* passwd <enter>

und dann altes und neues Passwort (zweimal) eingeben.
In einem zweiten Fenster mit Kommandozeile loggen wir uns nun ein zweites Mal ein mit

ssh pi@<IP-Adresse> 

und können so testen, dass auch mit dem neuen Passwort der Login funktioniert.

Für extra Komfort erlaube ich dann noch meinen üblichen ssh-keys den Zugang zum Pi ohne Passwort-Eingabe; auf der Linux-Kommandozeile:

ssh-copy-id pi@<IP-Adresse>  

Das fragt dann nach dem aktuellen Passwort des Benutzers (pi) und kopiert dann den öffentlichen ssh-Schlüssel aus .ssh/id_ecdsa.pub auf den pi.
Damit kann jeder Computer, auf dem der zugehörige private Schlüssel erfügbar ist, eine ssh-Verbindung herstellen ohne dass ein Passwort abgefragt wird.

Nun die Software.

Ich bevorzuge aptitude gegenüber apt, weil man damit besser nach Paketen suchen kann, für diese Anleitung verwende ich aber apt.
Zunächst ein komplettes Update aller installierten Software auf die aktuellen Stände:

sudo bash -c 'for i in update {,dist-}upgrade auto{remove,clean}; do apt-get $i -y; done'  

Das führt nacheinander einige apt-Kommandos aus:

  • update: aktualisiert die Paketlisten vom Server
  • upgrade -y: aktualisiert installierte Software (ohne Nachfragen durch das -y)
  • dist-upgrade -y: aktualisiert Basissoftware (wieder ohne Nachfragen durch -y)
  • autoremove -y: entfernt nicht mehr benötigte Pakete
  • autoclean -y: entfernt alte Daten aus dem Paket-Cache

Danach starten wir zur Sicherheit neu, da vermutlich der Kernel und andere wichtige Software aktualisiert wurden.

sudo shutdown -r now

Wieder einloggen und nun die Installation der benötigten Software für unseren Server:

sudo apt-get install aptitude supervisor python python-pip vim hostapd dnsmasq apache2

Der Editor vim wird heutzutage nicht mehr automatisch in Debian/Raspbian installiert, und viele bevorzugen heute den vorinstallierten nano - ich bleibe bei dem, was ich kenne :-).

Schritt 6: Konfiguration WLAN-AccessPoint (hostapd)

Grundsätzlich werden bei WiFi drei Betriebsmodi unterschieden: Point-to-Point auch WiFi Direct genannt; erlaubt Kommunikation zwischen Geräte ohne weitere Infrastruktur; heute selten eingesetzt.
Station * AccessPoint (AP)

Manche WLAN-Hardware lässt sich gar nicht als AP betreiben, heutzutage ist das allerdings selten geworden; dennoch werden Wlan-Adapter meisst im Modus Station betrieben.
Eine Station registriert sich gegenüber einem AccessPoint und stellt so eine Assoziation her. Das ist vergleichbar mit dem Einstecken eines Ethernet-Kabels am Gerät.
Die Interfaces von Routern werden idR. im Modus AccessPoint betrieben, stellen also Stations gegenüber eine Funkzelle bereit.
Genau das wollen wir hier mit dem Pi bewirken.

Der Daemon hostapd ist die Standard-AP-mplementation für Linux und dient auf Millionen "Wlan-Routern" für genau diesen Zweck.

Konfiguriert wird der Daemon über die Datei /etc/hostapd/hostapd.conf.
Die bei Raspbian gelieferte Beispiel-config ist recht lang, was auch daran liegt, dass der WiFi-Standard seit zwei Jahrzehnten immer wieder erweitert wird.
Zunächst ist diese zu kopieren und zu dekomprimieren:

sudo cp /usr/share/doc/hostapd/examples/hostapd.conf.gz /etc/hostapd
sudo gunzip /etc/hostapd/hostapd.conf.gz

Danach ändern wir die Zeilen, die wir benötigen und lassen die anderen Werte auf den Beispielwerten stehen - das passt schon sehr gut.
Editieren darf die config-Datei nur der Superuser (root), daher öffnen wir die Datei mit

sudo <lieblingseditor> /etc/hostapd/hostapd.conf.

In der Datei suchen wir jetzt die Zeilen, die zu ändern sind; ich gebe hier nur die zu ändernden Zeilen an, die ganze config-Datei wäre zu lang.
Die Suchen-Funktion des verwendeten Editors hilft hier, in vim ist das einfach der Slash (/).
Protip: eine Suche nach ^string findet den gesuchten string nur am Zeilenanfang, wenn der Editor Suche mit regular expression beherrscht wie vim.

#ssid=test # Zeile entfernen oder mit # zum Kommentar machen
ssid2=P"Pirate\nOpenSpot"
country_code=DE
ieee80211d=1
ieee80211h=1
local_pwr_constraint=3
spectrum_mgmt_required=1
hw_mode=g
channel=12
beacon_int=50
dtim_period=2
no_probe_resp_if_max_sta=254
ap_max_inactivity=120
skip_inactivity_poll=1
disassoc_low_ack=1
ap_isolate=1

Besonderes Augenmerk verdienen hier

  • ssid2: das ist der Name des WLAN der in den Auswahlmenüs der Clients angezeigt wird. Der hostapd erlaubt hier auch Return-Zeichen, was ungewöhnlich ist.
  • country_code: muss unbedingt auf den korrekten Wert (DE für Deutschland) gesetzt werden.
  • ap_isolate=1: damit können Stations an diesem AP nicht direkt miteinander kommunizieren. In einem Heimnetz würde man das auf 0 setzen.
  • wir haben keine Verschlüsselung für das WLAN spezifiziert, betreiben es also offen; für ein Heimnetz ist das idR. nicht die "richtige" Einstellung.

Als nächstes teilen wir Rasbian mit, wo nach der hostapd.config zu suchen ist.

sudo <lieblingseditor> /etc/default/hostapd

#DAEMON_CONF=""

ändern in

DAEMON_CONF="/etc/hostapd/hostapd.conf"

Schritt 7: DHCP client konfigurieren

Wir wollen auf dem kabelgebundenen Ethernet weiterhin unsere IP-Adresse mittels DHCP von unserem lokalen Router beziehen.
Auf dem Wlan-Interface dagegen benötigen wir eine statische IP-Adresse für unseren Hotspot.
Die Konfiguration des DHCP-clients in Raspian erledigt beides;
normalerweise bezieht der dhcpcd auf allen verfügbaren interfaces eine IP-Adresse, wir stellen das nur für das wlan0 interface um und schon entsteht das gewünschte Verhalten.

sudo <lieblingseditor> /etc/dhcpcd.conf

Am Ende der Datei einfügen:

interface wlan0
static ip_address=192.168.23.1/24

Danach ist leider offenbar ein reboot nötig?
Schadet nicht und geht fix :-)

sudo shutdown -r now

und wieder einloggen.

Jetzt gilt es noch, den hostap-Dienst auch zu aktivieren und zu starten:

sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl restart hostapd

Nun sollte das neue WLAN sichtbar sein unter der eingestellen SSID.
Nur funktioniert es (noch) nicht, weil wir noch keine IP-Adressen an assoziierte Stationen vergeben.

Schritt 8: Konfiguration DHCP-Server (dnsmasq)

Die Adressvergabe in Ethernet (und dem verwandten WiFi) geschieht idR. mit dem Dynamic Host Configuration Protocol DHCP.
Für dieses gibt es eine Reihe Implemendationen, die häufiger im Einsatz sind.
Der freie dnsmasq ist auf Linux-basierten Routern besonders beliebt, da er nicht nur DHCP bereitstellt sondern auch DNS-Caching und weitere Funktionen - und das alles bei geringem Bedarf an Speicher und CPU, die auf (Heimnetz-)Routern häufig knapp sind.
Konfiguriert wird der Dienst über /etc/dnsmasq.conf, die wieder mit sudo zu editieren ist.

Wir konfigurieren ihn wie folgt:

interface=wlan0      # Use the require wireless interface - usually wlan0
  dhcp-range=192.168.23.2,192.168.23.253,255.255.255.0,1h
interface=wlan0 #listen on this interface only
except-interface=lo
bind-interfaces # needed for interface= option to work
listen-address=192.168.23.1
port=53
no-resolv # do not forward dns lookups to  the servers in /etc/resolv.conf
server=127.0.0.1

Und aktivieren und starten den Dienst danach:

sudo systemctl enable dnsmasq
sudo systemctl restart dnsmasq

Nun sollte z.B. ein Smartphone beim Auswählen des neuen WLAN auch eine IP-Adresse erhalten.
Im Grunde funktioniert unser "Hotspot" jetzt schon.
Aber er stellt keinen Router dar, der Pakete aus dem WLAN über sein Ethernet weiterleitet. Das war auch nicht das Ziel.
Die Weiterleitung von Paketen aus einem Netz (z.B. Wlan) in ein anderes Netz (z.B. Ethernet) wird unter Linux von dem Kernel-Parameter net.ipv4.ip_forward gesteuert.
Dieser Parameter steht normalerweise auf 0, wie sich wie folgt überprüfen lässt:

sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

Dies verbietet dem Kernel, Pakete aus einem Netz in ein anderes weiterzuleiten, es verbietet das, was man klassisch "Routing" nennt.
Nicht zu routen ist eine sinnvolle Grundeinstellung für alle Geräte, die nicht explizit Router sein sollen, Raspbian macht das also richtig.
Über unseren Hotspot ist also nur Zugriff auf den Pi selbst (also auf Programme/Dienste/Daemonen die auf dem Pi laufen) möglich.
Gut so.

Allerdings wird der Pi ab dieser Stelle ein Problem mit dem Internetzugang haben: Ab hier wird die DNS-Auflösung nicht mehr funktionieren, da wir keine externen DNS server mehr abfragen (sondern nur den internen dnsmasq).
Das korrigieren wir temporär, indem wir die DNS-Konfiguration auf einen externen DNS-Server umstellen:

sudo <lieblingseditor> /etc/resolv.conf

nameserver 127.0.0.1
ändern in
nameserver 8.8.8.8

Der Nameserver 8.8.8.8 wird von Google betrieben, wer Google nicht die DNS- Abfragen seines Pi petzen möchte, kann hier einen anderen der öffentlichen DNS einstellen oder die IP-Adresse des eigenen Routers: da ist normalerweise ein dnsmasq drauf, der die Anfragen passend weiterleitet.

Schritt 9: Konfiguration DNS-Lügner

Im Grunde ist nach Schritt 7 der Zugriff auf Dienste auf dem Pi schon möglich.
Allerdings müsste man immer die IP-Adresse des wlan0 des Pi als "hostnamen" eintippen.

Geschickter wäre es, wenn wir den Pi über einen Namen adressieren könnten.
Noch pfiffiger scheint es mir allerdings, wenn wir den Pi über beliebige Namen adressierten: egal, welcher Name angefragt wird, wir geben immer die IP-Adresse des Pi-wlan0 zurück.

Dafür gibt es keine fertige Software.
Und das ist gut so: diese Software stellt im Grunde einen Eingriff in die Rechte Dritter dar.
Der Autor ist z.B. Inhaber der Domains "hase", sowohl in der TLD ".net" als auch in der TLD "rocks".
Letztlich heisst das, dass nur ich festlegen darf, welche Auskunft das DNS für Anfragen nach
<irgendwas>.hase.net oder <wasauchimmer>.hase.rocks geben darf.
Als Inhaber steht dieses Recht, diese Verfügungsgewalt über die DNS-Auskunft, nur mir zu.

Dieses Recht wurde zunächst in den 90ern mit dem aufkommenden DNS festgelegt als das, was Juristen eine Standesregel nennen: der Berufsstand der DNS-Admins stelle sicher, dass nur die Domain-Inhaber die Auskünfte aus diesem Verzeichnisdienst bestimmten.

In dieses Recht wurde mehrfach schon eingegriffen.
Besonderes Aufsehen erregte z.B. Anfang der Nullerjahre ein Fall von DNS-Manipulationen, die bei verschienen ISP aufgrund Verfügung durch die Bezirksregierung Düsseldorf durchgeführt wurden.

Das im folgenden dargestellte Programm dns-liar.py macht im Grunde etwas ähnliches: statt die im DNS vorgesehenen Antworten auf DNS-Queries gibt es einfach immer dieselbe Antwort zurück.
Interessanterweise scheint das bis heute nicht gesetzwidrig zu sein, es ist halt noch immer eine Standesregel.
Und im privaten Umfeld und bei begrenzter Reichweite erscheint es dem Autor auch durchaus vertretbar, speziell auf einem Hotspot-Gerät, das keine Verbindung zum Internet hat bzw. bereitstellt.

Stellen wir erstmal ein paar Grundlagen her.
Unser DNS-Lügner soll später als Daemon laufen, der muss irgendwie gestartet werden.
Für diese Anleitung verwende ich das Paket supervisor, das unabhängig von den Linux-Systemeigenen Mechanismen daemonen kontrollieren kann.
Das bietet zum einen den Vorteil, dass es auf verschiedenne Distributionen immer gleichartig läuft, egal welches Daemon-Management die Distribution intern einsetzt. Und daher beisst es sich auch nicht mit Updates des Distributionsinternen Mechanismus.

Der supervisord wird über die Datei /etc/supervisor/supervisord.conf konfiguriert, die wir aber im Zustand belassen können, den Raspbian ausliefert.
Die von supervisor kontrollierten Daemonen werden über einzelne Dateien in /etc/supervisor/conf.d konfiguriert.

sudo <lieblingseditor> /etc/supervisor/conf.d/dns-liar.conf

[program:dns-liar]
logfile_maxbytes = 30MB
logfile_backups = 5
command=/usr/local/bin/dns-liar.py --address "127.0.0.1" --response ". 60 IN A 192.168.23.1"

Wichtig ist hier die IP-Adresse 192.168.23.1: diese gibt der DNS-Lügner als Antwort auf jede Frage. Hier verwenden wir die IP-Adresse unseres wlan0-Interface.

Das entsprechende Programm brauchen wir jetzt noch.

sudo <lieblingseditor> /usr/local/bin/dns-liar.py  

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    FixedResolver - example resolver which responds with fixed response
                    to all requests
"""

from __future__ import print_function

import copy

from dnslib import RR
from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger

class FixedResolver(BaseResolver):
    """
        Respond with fixed response to all requests
    """
    def __init__(self,zone):
        # Parse RRs
        self.rrs = RR.fromZone(zone)

    def resolve(self,request,handler):
        reply = request.reply()
        qname = request.q.qname
        # Replace labels with request label
        for rr in self.rrs:
            a = copy.copy(rr)
            a.rname = qname
            reply.add_answer(a)
        return reply

if __name__ == '__main__':

    import argparse,sys,time

    p = argparse.ArgumentParser(description="Fixed DNS Resolver")
    p.add_argument("--response","-r",default=". 60 IN A 127.0.0.1", metavar="<response>", help="DNS response (zone format) (default: 127.0.0.1)")
    p.add_argument("--zonefile","-f", metavar="<zonefile>", help="DNS response (zone file, '-' for stdin)")
    p.add_argument("--port","-p",type=int,default=53, metavar="<port>", help="Server port (default:53)")
    p.add_argument("--address","-a",default="", metavar="<address>", help="Listen address (default:all)")
    p.add_argument("--udplen","-u",type=int,default=0, metavar="<udplen>", help="Max UDP packet length (default:0)")
    p.add_argument("--tcp",action='store_true',default=False, help="TCP server (default: UDP only)")
    p.add_argument("--log",default="request,reply,truncated,error", help="Log hooks to enable (default: +request,+reply,+truncated,+error,-recv,-send,-data)")
    p.add_argument("--log-prefix",action='store_true',default=False, help="Log prefix (timestamp/handler/resolver) (default: False)")
    args = p.parse_args()

    if args.zonefile:
        if args.zonefile == '-':
            args.response = sys.stdin
        else:
            args.response = open(args.zonefile)

    resolver = FixedResolver(args.response)
    logger = DNSLogger(args.log,args.log_prefix)

    print("Starting Fixed Resolver (%s:%d) [%s]" % (
                        args.address or "*",
                        args.port,
                        "UDP/TCP" if args.tcp else "UDP"))

    for rr in resolver.rrs:
        print("    | ",rr.toZone().strip(),sep="")
    print()

    if args.udplen:
        DNSHandler.udplen = args.udplen

    udp_server = DNSServer(resolver,
                           port=args.port,
                           address=args.address,
                           logger=logger)
    udp_server.start_thread()

    if args.tcp:
        tcp_server = DNSServer(resolver,
                               port=args.port,
                               address=args.address,
                               tcp=True,
                               logger=logger)
        tcp_server.start_thread()

    while udp_server.isAlive():
        time.sleep(1)

Das Programm muss ausführbar sein:

sudo chmod 777 /usr/local/bin/dns-liar.py

Und wir brauchen noch die entsprechenden Python-Module:

sudo pip install dnslib

Wir können jetzt probieren, ob das so funktioniert wie gewünscht:

sudo /usr/local/bin/dns-liar.py --address "127.0.0.1" --response ". 60 IN A 192.168.23.1"

Wenn man jetzt ein Smartphone am Hotspot einbucht, sollten hier schon die ersten Anfragen geloggt werden: Smartphone fragen ständig diverse Hosts an, auch um zu testen, ob das WLAN auch Zugang zum Internet bereitstellt.

Mit Ctrl-C beenden wir das Programm und mit

sudo systemctl restart supervisor
sudo supervisorctl restart dns-liar

starten wir den Daemon im Hintergrund.
Die Ausgaben des Daemons finden wir dann in /var/log/supervisor.

Schritt 10: Content

Jetzt fehlt eigentlich nur noch ein Webserver auf dem Pi.
Da wir ja alle DNS-Anfragen mit der IP-Adresses des Pi beantworten landen alle Browser-Connections auf diesem Webserver.

Das funktioniert übrigens nicht mit vielen modernen Websites, die schon die aktuellen Sicherheitsstandards umsetzen.
In aller Kürze: idR. liefern grosse Sites heute nur noch über https aus und teilen das den Browsern auch mit.
Die Browser merken sich das und verlangen für einmal besuchte https-only-Sites, dass auch weiterhin nur https verwendet wird.
Bei Anfragen für solche Sites erscheint dann in unserem Hotspot-WLAN eine Fehlermeldung im Browser.

Das Paket apache2 stell in Raspbian den beliebten Webserver bereit.
Die Standardinstallation enthält eine simple config (/etc/apache2/sites-enabled/000-default.conf), die für unsere Zwecke auch unmodifiziert einsetzbar ist.
Eine komplettere Anleitung zur Konfiguration von Webserver sprengte auch den Rahmen dieses Kochrezeptes.

Nur gilt es nur noch, geeignete Inhalte im Verzeichnis /var/www/html abzulegen. Auch die Anleitung zur Erstellung von Websites lasse ich an dieser Stelle weg.

Auf hase.net findet der eilige Leser ein komplettes Projekt für pelican.
Enthalten ist nicht nur der "Quelltext" der Site sondern auch der fertige Output, der direkt auf den Pi kopiert werden kann. Zum neu-Übersetzen bräuchte man dann pelican, entweder auf dem Hotspot-Pi oder auf dem Linux-Desktop.

herunterladen der Datei:

wget https://www.hase.net/transfers/OpenSpot.tar.bz

auspacken:

tar xvfz OpenSpot.tar.bz

Und nun noch die Dateien an die richtige Stelle kopieren:

cd OpenSpot/output
sudo cp -r * /var/www/html
sudo chown -R www-data:www-data /var/www/html/*

Und dann den Webserver zur Sicheheit heu starten (sollte an sich nicht erfoderlich sein?)

sudo systemctl restart apache2

Der Webserver auf dem Pi sollte jetzt unter http://<IP-Adresse> und auch unter jedem beliebigen hostnamen z.B. http://huhu.blafasel.tld dieselbe Seite ausgeben.

Bei Fragen: fragen! mailto://hase@hase.net

social