Im 1. Teil habe ich grundlegende Überlegungen für den Betrieb eines Internet-Servers im eigenen Heim angestellt. tl;dr:
In diesem zweiten Teil geht es um die konkrete Umsetzung. Dabei stelle ich die Lösung vor, die mir am praktikabelsten erschien. Es gibt viele andere Wege. Es ist eine Frage der verfügbaren Ressourcen, der persönlichen Kenntnisse und Angewohnheiten und auch des Geschmacks, welche Lösungen man bevorzugt. Insofern erhebt die hier vorgestellte Lösung nicht den Anspruch, der Weisheit letzter Schluss zu sein.
Aufgesetzt wird ein Mailserver "mail" (SMTP + IMAP) und ein Webserver "web".
Hier nochmal eine (unvollständige) grafische Darstellung der Realisierung.
.--. _ -( )- _ .--,( INTERNET ),--. _.-( .----------. )-._ VPN-Tunnel ( | IPv4-VPS |<....................... '-._( '----------' )_..' . '__,( ),__' | . - ._(__)_. - | . | . _________ _____|___ . [_...__...]------------- [_..._'...] . Servernetz . Internet-Router. . Router | | . | ' . | .-,( ),-. . | .-( )-. . | ( Heimnetz ) . | '-( ).-' . | '-.( ).-' . .'-. . _ -( )- _ . .--,( Servernetz ),--. . _.-( .--------. )-._ . ( | Server |<..........................' '-._( '--------' )_.-' VPN-Tunnel '__,( ),__' - ._(__)_. -
Und hier detaillierter Ausschnitt über das Servernetz
____ VServer vps 1.2.3.4|====| | | _________ | | [_...._...] Servernetz-Router vpn 172.16.0.1 |____| | ^ ^ | ____ | | ___'____ |====|vpn 172.16.0.3---------' | |==|=====|-----------| | | | | | | | | | | | |____| | | | | Container mail 192.168.2.7 | | | | ____ | | |==== | |====|vpn 172.16.0.2--------------' |__|_____|-----------| | Server srv 192.168.2.5 | | |____| Container web 192.168.2.6 Servernetz 192.168.2.0/24
Der Servernetz-Router wird "hinter" dem Internet-Router angeschlossen. Hier wird ein eigenes, privates Netz konfiguriert. Auf dem Router selbst muss konfiguriert werden, dass keine keine Pakete aus dem Servernetz in das Heimnetz gelangen. Lediglich das NAT-Forwarding an den Heimnetz-Router wird erlaubt.
Dies ist unser Host für die VMs/Container. Ich nutze hier ein ausgemustertes Mini-ITX-Board mit einem Intel Atom D510 Prozessor. Dieser ist relativ verbrauchsarm und hat den Vorteil, nicht von Spectre/Meltdown betroffen zu sein - ist aber 64bit-fähig. Der größte Nachteil ist, dass diese CPU keine Hardware-Virtualisierung beherrscht. Daher nutze ich unprivilegierte LXC-Container.
Als OS verwende ich Alpine Linux in der Version 3.13 und einem LTS-Kernel. Die Installation erfolgt mit der Extended-Variante als System-Installation ("sys").
Nach der Installation muss das System vorbereitet werden, LXC-Container zu hosten. Die Container sollen im selben Netz sein wie der Host, also benötigen wir eine Netzwerk-Brücke (bridge). Zunächst werden die benötigten Pakete installiert:
apk add lxc bridge lxcfs lxc-download lxc-openrc lxc-templates bridge-utils
Dann wird die Bridge konfiguriert
# /etc/network/interfaces auto lo iface lo inet loopback auto br0 iface br0 inet static bridge-ports eth0 bridge-stp 0 address 192.168.2.5 netmask 255.255.255.0 gateway 192.168.2.1
Zwecks Separation sollten die Systeme voneinander getrennt sein. Container laufen im selben Kernelspace ab wie der Host und somit wäre es relativ einfach aus einem Container auszubrechen, der mit Root-Rechten läuft. Daher wären eigentlich "echte" virtuelle Maschinen zu bevorzugen. Da dies hier keine Option ist, verwende ich unprivilegierte Container. Dies sind - grob gesagt - Container ohne Root-Rechte auf dem Host-System. Dies wird erreicht durch ein User-ID-Mapping und den Einsatz von User-Namespaces innerhalb des Kernels. Der Root-User innerhalb des Containers ist also nicht identisch mit dem Host-Root-User. Außerdem sorgt der Kernel dafür, dass alle Prozesse des Containers in ihrem eigenen Namensraum "eingesperrt" werden.
Zunächst wird das Usermapping vorbereitet:
echo root:1000000:65536 | tee -a /etc/subuid echo root:1000000:65536 | tee -a /etc/subgid
In die Datei /etc/lxc/default.conf wird eingetragen
lxc.idmap = u 0 1000000 65536 lxc.idmap = g 0 1000000 65536
Nun können die Container erstellt werden:
lxc-create -n web -t download lxc-create -n mail -t download
Mit "-t download" kann man komfortabel vorhandene Images verschiedener Linux-Distributionen auswählen. Ich entschied mich hier erneut in beiden Fällen für Alpine 3.13.
Die Config-Datei des Containers liegt anschließend unter /var/lib/lxc/[Containername]/config. Diese sollte überprüft werden und etwa so aussehen (hier für den Container "web"):
lxc.include = /usr/share/lxc/config/common.conf lxc.include = /usr/share/lxc/config/userns.conf lxc.arch = linux64 lxc.idmap = u 0 1000000 65536 lxc.idmap = g 0 1000000 65536 lxc.rootfs.path = dir:/var/lib/lxc/web/rootfs lxc.uts.name = web lxc.net.0.type = veth lxc.net.0.flags = up lxc.net.0.link = br0
Gestartet werden die Container mit
lxc-start <Containername>
Der Zugriff auf die Container kann vom Host aus mit dem Befehl "lxc-attach" geschehen. Dies erspart dann auch die Installation eines SSH-Servers innerhalb des Containers - und vermeidet so einen weiteren Angriffsvektor.
Umgekehrt sollte auf dem Host jedoch der Zugriff per SSH aus dem Container heraus auf dem Host unterbunden werden. Man kann dies über die Konfiguration des SSH-Dienstes regeln oder aber auch einfach per iptables:
iptables -A INPUT -p tcp --dport 22 --source 192.168.2.6 -j DROP iptables -A INPUT -p tcp --dport 22 --source 192.168.2.7 -j DROP
Auf dem VPS läuft ebenfalls Alpine Linux. Hier müssen nun Maßnahmen getroffen werden, um dem VPN-Tunnel vorzubereiten. Aufgrund der Einfachheit habe ich mich für die moderne VPN-Lösung Wireguard entschieden
apk add wireguard-tools wireguard-virt cd /etc/wireguard umask 077; wg genkey | tee privatekey | wg pubkey > publickey
Nun wird noch die Konfiguration /etc/wireguard/wg0.conf für das Wireguard-Interface und zwei Peers (Web und Mailserver) erstellt.
[Interface] ListenPort = 43210 Address = 172.16.0.1/24 PrivateKey = <private key des Hosts> PostUp = iptables -A INPUT -i wg0 -j ACCEPT; iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; iptables -F -t nat [Peer] PublicKey = <public key web> AllowedIPs = 172.16.0.2/32 PersistentKeepalive = 25 [Peer] PublicKey = <public key mail> AllowedIPs = 172.16.0.3/32 PersistentKeepalive = 25
Die iptables-Regeln in "PostUp" erlauben Forwarding per NAT über wg0, damit die Peers anschließend über den VPS auch ins Internet kommen. PersistentKeepalive ist hier nötig, um den Tunnel offen zu halten.
An dieser Stelle muss der Wireguard-Tunnel auf den beiden Containern eingerichtet werden. Die Installation und die Generierung der Keys läuft exakt so wie auf dem externen vServer. Die Konfigurationsdatei /etc/wireguard/wg0.conf sieht wie folgt aus
[Interface] PrivateKey = <private key des Containers> Address = 172.16.0.2/24 #bzw. 172.16.0.3/24 für "mail" Table = off PostUp = ip rule add to 1.2.3.4 lookup main pref 30; ip rule add to 192.168.2.0/24 lookup main pref 31; ip rule add to all lookup 80 pref 40; ip route add default dev wg0 table 80 PostDown = ip rule del to 1.2.3.4 lookup main pref 30; ip rule del to all lookup 80 pref 40; ip rule del to 192.168.2.0/24 lookup main pref 31 [Peer] PublicKey = <public key des VPS> Endpoint = 1.2.3.4:43210 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25
Es soll anschließend das Tool wg-quick benutzt werden, welches standardmäßig default-Routen setzt. Da diese Default-Routing-Tabellen nicht ganz passten, habe ich sie deaktiviert (Table=off) und stattdessen mit PostUp eigene Routing-Tabellen angelegt.
Nun muss auf allen Seiten, d.h. auf dem VPS und in den beiden Containern, nur noch Wireguard gestartet werden:
wg-quick up wg0
Damit steht schon mal der Tunnel. Überprüft werden kann dies mit dem Befehl "wg show" und ping auf die IP des jeweiligen Peers.
Der Tunnel steht, wie kommt bekommt man nun Verbindungen, die zum VPS konnektieren auf die beiden Container? Hier gibt es mehrere Möglichkeiten
In der folgenden Lösung verwende ich Apache als Reverse Proxy für "web" und Port Forwarding für "mail".
Zunächst müssen wir Apache auf dem VPS einrichten. Die Grundkonfiguration inklusive SSL-Zertifikat (von Let's Encrypt) würde den Rahmen sprengen, daher hier nur der VHost-Abschitt für die im Container gehostete Webseite
<VirtualHost *:443> ServerAdmin meine@email.mail ServerName meine.coole.site SSLEngine On SSLProxyEngine On SSLCertificateFile /etc/ssl/meine.coole.site.fullchain.pem SSLCertificateKeyFile /etc/ssl/private/meine.coole.site.key ProxyPass / http://172.16.0.2/ ProxyPassReverse / http://172.16.0.2/ CustomLog logs/meine.coole.site.log combined ErrorLog logs/meine.coole.site-error.log </VirtualHost>
Apache leitet damit alle ankommenden Pakete, die an https: // meine.coole.site gerichtet sind, an den Webserver im Container "web" mit der URL http: // 172.16.0.2/ weiter. Zwar könnte man auch hier https verwenden, aber der Wireguard-Tunnel sorgt ja bereits für Verschlüsselung, daher habe ich an dieser Stelle darauf verzichtet.
Auf "web" muss dann nur noch ein Webdienst eingerichtet werden, der auf Port 80 hört.
Für den Mailserver werden auf dem VPS entsprechende Forwarding-Regeln per iptables eingerichtet
# SMTP iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 25 -j DNAT --to 172.16.0.3:25 # IMAP iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 143 -j DNAT --to 172.16.0.3:143 # IMAPS iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 993 -j DNAT --to 172.16.0.3:993
Wenn weitere Dienste genutzt werden sollen (z.B. POP3) müssen natürlich auch entsprechende Weiterleitungen eingerichtet werden. In dem Container "mail" müssen sodann Dienste für SMTP und IMAP eingerichtet werden, deren Installation und Konfiguration aber hier nicht besprochen wird - das folgt dann in einem späteren Beitrag.