WireGuard-VPN-Setup für Leihgeräte für Lehrkräfte

Prinzipieller Aufbau des VPNs

Die Leihgeräte für unsere Lehrkräfte werden bei uns in einem eigenen WireGuard-VPN-Netzwerk betrieben werden, damit zum einen gewährleistet ist, dass jederzeit ein administrativer Zugriff auf die Geräte mit Ansible, was wir unter anderem zur Verwaltung der Geräte einsetzen, möglich ist und damit zum anderen sichergestellt ist, dass sämtlicher Datenverkehr in einem potentiell unsicheren Heimnetzwerk mit dem Internet nur über die verschlüsselte VPN-Verbindung läuft. Zusätzlich ermöglicht der Einsatz von WireGuard, dass auch bestimmte Serverdienste aus dem Schulnetz zuhause zur Verfügung gestellt werden können, ohne diese mit dem Internet verbinden zu müssen.

WireGuard setzt bei der Verschlüsselung auf ein asymmetrisches Verschlüsselungssystem mit öffentlichem und privaten Schlüssel. Dieses ist bei der Einrichtung der Geräte eine gewisse Herausforderung, weil Schlüssel für eine Vielzahl an Geräten erzeugt und die jeweils öffentlichen Schlüssel dann auch dem WireGuard-Server bekannt gemacht werden müssen, damit sich die entsprechenden Clients verbinden können.

Dafür habe ich ein kleines Skript geschrieben, was die Schlüssel erzeugt, eine Konfigurationsdatei für die Clients (wg0.conf) erstellt, die den privaten Schlüssel des Geräts enthält und den öffentlichen Schlüssel für den Server speichert, sodass der Client einfach hinzugefügt werden kann.

Das Skript erzeugt einen qrcode mit der Konfigurationsdatei und erstellt dann ein PDF-Dokument, was den qrcode sowie menschenlesbar die IP-Adresse im WireGuard-Netz enthält, damit man noch weiß, zu welchem Client der qrcode gehört.

Auf den Clients muss man jetzt einfach nur noch mit dem Programm zbarcam (ist in Ubuntu in dem Paket zbar-tools in den Paketquellen enthalten) den qrcode mit der Webcam scannen und als Datei in /etc/wireguard speichern:

sudo zbarcam > /etc/wireguard/wg0.conf

Die PDFs mit den qrcodes muss man jetzt natürlich wie einen Schatz hüten, da dieser die privaten Schlüssel der Clients enthält. Jeder, der den qrcode abfotografieren kann, kann damit sein Gerät zum WireGuard-VPN-Netzwerk hinzufügen. Deswegen ist das Dokument am besten ausgedruckt an einem sicheren Ort aufzubewahren.

So sehen die qrcodes im PDF-Dokument aus. Die Daten sind natürlich nur Beispiele

Mein Skript benötigt eine Template-Config-Datei (template.conf):

[Interface]
PrivateKey = PRIVKEY
Address = IPADDRESS/24

[Peer]
PublicKey = 9OKdnpB3QijHMx+/7ij+GYFEABJvkiudTPsDgGVulmc=
Endpoint = 1.2.3.4:51820
AllowedIPs = 0.0.0.0/0

Außerdem ist eine Liste (liste.txt) mit IP-Adressen nötig, die dem VPN hinzugefügt werden sollen:

172.16.0.10
172.16.0.11
172.16.0.12
172.16.0.13
172.16.0.14
172.16.0.15

Außerdem habe ich eine Postscript-Datei für das PDF, in das dann die jeweilige IP-Adresse eingefügt werden soll:

<<
   /EndPage
   {
     2 eq { pop false }
     {
         gsave      
         /Helvetica findfont 48 scalefont setfont
         newpath
         .55 setgray 130 200 moveto 50 rotate
         (IPADDRESS) false  charpath
         1 setlinewidth stroke
         grestore
         true
     } ifelse
   } bind
>> setpagedevice

Schließlich das Skript, das ein paar Programme benötigt, damit es korrekt funktioniert:

#!/bin/bash
#Zum Lesen sudo apt install zbar-tools
#Einlesen in /etc/wireguard/ zbarcam > wg0.conf
#Dieses Skript benötigt pdftk (snap install pdftk), imagemagick, gs, qrencode

datum=$(date +"%d-%m-%y")

echo "-- Datum: $datum. --"
echo "Erzeugung von WireGuard-Schlüsseln gestartet!"

mkdir -p ./keys
mkdir -p ./conf
mkdir -p ./PDF
mkdir -p ./png

while read IP
do
  sed "s@\bIPADDRESS\b@${IP}@g" template.conf > temp-01.conf
  wg genkey | tee ./keys/privatekey_$IP | wg pubkey | tee ./keys/publickey_$IP
  read privatekey < ./keys/privatekey_$IP
  read pubkey < ./keys/publickey_$IP
  sed "s@\bPRIVKEY\b@${privatekey}@g" temp-01.conf > ./conf/wg0_$IP.conf
  echo "wg set wg0 peer $pubkey allowed-ips $IP" >> peers.txt
  qrencode <./conf/wg0_$IP.conf -o ./png/qrcode_$IP.png
  convert -page A4 ./png/qrcode_$IP.png ./PDF/CONFIG_tmp_$IP.pdf
  sed "s@\bIPADDRESS\b@${IP}@g" ./ps_template.ps > ./ps_template_tmp.ps
  gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=./PDF/CONFIG_$IP.pdf ./ps_template_tmp.ps ./PDF/CONFIG_tmp_$IP.pdf
done < ./liste.txt
rm ./PDF/CONFIG_tmp_*.pdf
pdftk ./PDF/CONFIG_*.pdf cat output ./qrcodes_$datum.pdf
rm temp-01.conf ps_template_tmp.ps
rm -r ./PDF
rm -r ./png

Mit ein bisschen sed und Shell-Scripting wird dann das PDF erzeugt und, ganz wichtig, eine Textdatei (peers.txt) mit den öffentlichen Schlüsseln der Clients angelegt, damit ich diese auf dem WireGuard-Server hinzufügen kann.

Wenn dann die Leihgeräte entliehen werden, wird bei der Ausgabe kurz der qrcode mir der Webcam gescannt und das Gerät damit dem VPN hinzugefügt. Wireguard ist auf den Geräten so konfiguriert, dass es automatisch beim Start mit systemd gestartet wird und die Endnutzerin oder der Endnutzer nichts mehr damit zu tun hat. Sobald eine Netzwerkverbindung besteht, läuft der Verkehr über das VPN.

Management von Clients mit Ansible

Zur Verwaltung unserer zahlreichen Linux-Notebooks verwenden wir die Open-Source-Software Ansible. Diese ermöglicht uns eine extrem leichte Softwareverteilung, eine Ad-hoc-Kommando-Ausführung auf allen unseren Clients sowie ein einfaches Konfigurationsmanagement. Ansible verwaltet Netzwerkcomputer über SSH und erfordert keinerlei zusätzliche Software auf dem zu verwaltenden System. Ansible kann sogar Windows-Clients managen.

Ansible lässt sich bei Ubuntu sowie Debian ganz bequem über die Paketquellen installieren:

sudo apt install ansible

Damit sich Ansible auf den Client-Systemen anmelden kann, benötigt man einen ssh-Schlüssel, den man mit dem folgenden Befehl erstellt:

ssh-keygen -b 4096

Nach der Erstellung muss dieser Schlüssel auf die Clients kopiert werden, die verwaltet werden sollen. Dieses geschieht mit dem folgenden Befehl:

ssh-copy-id -i .ssh/id_rsa.pub benutzer@IP-Adresse

Dabei muss „benutzer“ durch den Benutzernamen ersetzt werden, unter dem Ansible laufen soll und zudem muss „IP-Adresse“ durch die korrekte IP-Adresse des Clients ersetzt werden. Nach der Verteilung des öffentlichen ssh-Schlüssels an die Clients müssen diese in die Datei /etc/ansible/hosts eingetragen werden. Es folgt ein Beispiel für unsere Roboter-AG-Laptops:

[RoboLaptops]
10.1.11.1
10.1.11.2

Ad-hoc-Kommando-Ausführung auf den Clients

Nachdem alle Clients, die verwaltet werden sollen, in die Datei /etc/ansible/hosts eingetragen und der öffentliche ssh-Schlüssel auf die Geräte übertragen wurde, kann man Ansible testen und einen Befehl auf allen Clients einer Gruppe (z.B. „RoboLaptops“) ausführen:

ansible RoboLaptops -u benutzer -a "shutdown -h now"

Dieses Kommando führt nun auf allen entsprechenden Geräten, die der Gruppe „RoboLaptops“ angehören, den Befehl „shutdown -h now“ aus und fährt die Geräte somit herunter. Zum Herunterfahren sind allerdings root-Privilegien erforderlich, weswegen der Befehl, so wie angegeben, nur funktioniert, wenn der Benutzer root ist.

Nutzung von „playbooks“

Damit man nicht immer einzelne Befehle eingeben muss, die dann auf allen Clients ausgeführt werden, gibt es sogenannte „playbooks“. Dabei handelt es sich um eine YAML-Datei, die die zu erreichende Konfiguration und Befehlsabfolge auf den Clients beschreibt. Die folgende Datei „e31.yml“ sorgt dafür, dass alle Clients im PC-Raum E31 die Paketquellen aktualisieren, alle verfügbaren Updates installieren und nicht mehr benötige Abhängigkeiten entfernen:

---
- hosts: E31
  remote_user: root
  tasks:
  - name: Run the equivalent of "apt-get update" as a separate step
    apt:
     update_cache: yes
  - name: Update all packages to the latest version
    apt:
     upgrade: dist
     dpkg_options: 'force-confold,force-confdef'
  - name: Remove dependencies that are no longer required
    apt:
     autoremove: yes

Meine Playbooks sind bei github verfügbar:

https://github.com/feschoppe/ansible-manage-ubuntu-clients