4 min read

Wireguard Roadwarrior VPN in LXC unter Proxmox

Vorwort: Die Installation ist ziemlich simpel, und kann problemlos in einem LXC Container durchgeführt werden. Das Hinzufügen neuer Geräte ist kinderleicht und kann eigentlich von jedem ITler in weniger als einer Stunde bewältigt werdem, WENN man sich nicht so dumm anstellt wie ich und in der Portfreigabe TCP statt UDP freigibt ... 🙄 Damit habe ich nun 4 Stunden verschwendet, mehrere LXCs und VMs mit OpenVPN und Wireguard aufgesetzt, mir die in der pfSENSE integrierte Methoden angeschaut, und dann fällt mir plötzlich auf woran der Fehler lag ...


Update 24.10.2022

Auch wenn der Inhalt des Artikels noch korrekt ist und mein Setup funktioniert, habe ich mich entschieden meinen Server für eingehende VPN Verbindungen nochmal schnell neu zu installieren, denn ich bin auf wg-easy als zusätzliche Web UI umgestiegen. Sie bietet eine bessere Übersicht, bietet das erneute Scannen des QR-Codes (vorher wurde dieser nur beim Anlegen einer Verbindung angezeigt) und zeigt außerdem den aktuellen Up & Download jedes Clients an:

Die Installation ist keinesfalls komplex, ich habe es in einem Debian 11 LXC Container mit Docker Compose installiert. Zusätzlich Watchtower und unattended-upgrade für ein wartungsfreies System. Hier meine docker-compose.yml:

version: "3.8"
services:
  wg-easy:
    environment:
      - WG_HOST=vpn.peterge.de
      - PASSWORD=###PASSWORD###
      - WG_PORT=51820
      - WG_DEFAULT_ADDRESS=10.7.0.x
      - WG_DEFAULT_DNS=10.0.4.1
      # - WG_MTU=1420
      - WG_ALLOWED_IPS=10.0.4.0/24, 10.0.12.0/24
      # - WG_PRE_UP=echo "Pre Up" > /etc/wireguard/pre-up.txt
      # - WG_POST_UP=echo "Post Up" > /etc/wireguard/post-up.txt
      # - WG_PRE_DOWN=echo "Pre Down" > /etc/wireguard/pre-down.txt
      # - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt
    image: weejewel/wg-easy
    container_name: wg-easy
    volumes:
      - .:/etc/wireguard
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

  watchtower:
    container_name: watchtower
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_SCHEDULE=0 15 12 * * *
      - WATCHTOWER_CLEANUP=true
      - TZ=Europe/Berlin
      - WATCHTOWER_NOTIFICATIONS=email
      - WATCHTOWER_NOTIFICATION_EMAIL_FROM==###MAIL###
      - WATCHTOWER_NOTIFICATION_EMAIL_TO=mail@peterge.de
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.mailbox.org
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=465
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=###MAIL###
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD==###PASSWORD###
      - WATCHTOWER_NOTIFICATIONS_HOSTNAME=wireguard.gerhards.local
      - WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG=remote
    restart: unless-stopped

Naja, noch ein paar genutzte Befehle von mir auf dem sich endlos anfühlenden Weg der Fehlersuche: ⁣
netstat -tulpn (von Diensten genutzte Ports anzeigen)
nc -z -v -u <host> <port> (wie Telnet, nur UDP)

Installation

Nun aber komme ich zur Installation: ⁣
Wie man einen Container unter Proxmox anlegt, beschreibe ich hier nicht. Ich gehe nur darauf ein, welche Besonderheiten bei VPN Anwendung (OpenVPN ebenfalls) durchgeführt werden müssen: ⁣
Zunächst muss die Config des Containers angepasst werden: ⁣
nano /etc/pve/lxc/<ID>.conf
Dort müssen die Zeilen
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net dev/net none bind,create=dir

hinzugefügt werden. Dann chown 100000:100000 /dev/net/tun (mit ls -l /dev/net/tun überprüfen) ausführen und anschließend den Container neu starten.
Bei einer VM entfallen diese Schritte natürlich.

Die Installation von Wireguard selbst lasse ich mithilfe des (von mir gut getestetem /s) Skript ausführen:
wget https://git.io/wireguard -O wireguard-install.sh && bash wireguard-install.sh
Dort muss zunächst der öffentliche Hostname (oder DynDNS) eingegeben werden (Tipp: Jede FRITZ!Box hat eine .myfritz Adresse, welche dynamisch auf die FRITZ!Box zeigt!).
Danach einen zu verwendenden Port, welcher standardmäßig 51820 ist.
Und den zu verwendenden DNS-Server angeben.
Sowie einen Namen des Clients und schließlich ob das Skript die Wireguard Installation aktuell halten soll (Was grundsätzlich eine gute Idee ist, falls man keine automatisierte Ansible Rolle dafür hat. Man könnte fast meinen, hierzu kommt bald ein Artikel xD).

Dann spuckt das Skript eine <clientname>.conf heraus, und einen QR-Code, den man direkt per Handy abscannen kann.

So, nun ist die Installation grundsätzlich durch und schon sollte der Wireguard Server einsatzbereit sein.
Noch einen Kommentar zur Ressourcennutzung: sehr sparsam. Diese Nutzung trat mit 2 verbundenen Geräten auf.

Ressourcennutzung

Portforwarding

Und nun muss natürlich noch der Weg zu Server hin freigegeben werden. Ich beschreibe das hier mal für FRITZ!Box und pfSENSE, da bei mir der Server ja hinter beidem geparkt ist.

FRITZ!Box:
Unter Internet > Freigaben für die IP-Adresse der pfSENSE folgendes Anlegen.

Portfreigabe FritzBox

pfSENSE:
Sowohl unter Firewall > NAT > Port Forward

Portfreigabe pfSENSE NAT

Als auch Firewall > Rules > WAN müssen Einträge angelegt werden.

Portfreigabe pfSENSE Firewall Rules

Wireguard Client einrichten

Somit sollte alles erledigt sein. Für Android gibts dann Clients auf fast allen Systemen, worüber dann per <clientname>.conf zugegriffen werden kann. Ich beschreibe hier mal Android und Fedora:

Android:
APK (auch in F-Droid) installieren. Das Script erneut ausführen (bash wireguard-install.sh) und Add a new client wählen. Am Ende wird ein QR-Code im Terminal angezeigt, der direkt vom Handy abgescannt werden kann.

Fedora:
Die Client <clientname>.conf übertragen (python3 -m http.server hilft hierbei), mit nmcli connection import type wireguard file Documents/fedora-x240.conf die Konfig importieren und mithilfe von nmcli connection up fedora-x240 den Tunnel erstellen.
Zusätzlich kann noch mit nmcli connection modify <connection-name> autoconnect no das automatische Verbinden beim Boot ausgeschaltet werden.

Author: peterge