Raspberry Pi hinter NAT

Letzte Aktualisierung: 24. Mai 2020

Ein im heimischen LAN hinter einem WLAN-Router („behind NAT“) aufgestellter Linux-Rechner soll Web Services fürs Internet (WAN) anbieten. Dafür kann z.B. ein stromsparender Single Board Computer (SBC) wie Raspberry Pi eingesetzt werden, auf dem Raspian (entspricht aktuell Debian 10/Buster) läuft.

In diesem Szenario ergeben sich zwei besondere Problemstellungen:

  1. Behind NAT

    Da der dieser kleine Rechner hinter dem üblichen WLAN-Router steht (Fritzbox, Speedport, …), der nur Verbindungen von innen nach außen zum clientseitigen „Surfen“ zulässt (NAT), muss im WLAN-Router der fürs Internet freizugebende Port freigeschaltet werden (PAT). Damit wird eine Kommunikation in umgekehrter Richtung möglich. Aus Sicherheitsgründen öffnen wir den Port im WLAN-Router erst, nachdem wir den Server abgesichert haben.

  2. Dynamical IP Address

    Der WLAN-Router bekommt vom Internetprovider zumeist eine sich ändernde IP-Adresse dynamisch zugewiesen. Das ist natürlich ungünstig, wenn man draußen dauerhaft erreichbar sein möchte. Die Lösung hierfür ist DynDNS. Siehe dazu https://www.maketecheasier.com/best-dynamic-dns-providers/

Zu b) ist anzumerken, dass der Linux-Server nach dem Einrichten von Port-Forwarding auch direkt mit der öffentlichen IP-Adresse des WLAN-Routers angesprochen werden könnte, was wie beschrieben eher unpraktisch ist. Diese IP-Adresse kann z.B. auch per Kommandozeile ermittelt werden - und tatsächlich benötigen wir sie auch gleich bei der DynDNS-Registrierung:

raspi:~$ curl -4 ifconfig.co
84.124.158.196
raspi:~$
raspi:~$ curl ifconfig.co
2003:f2:e729:c533:244:d6fa:fa2b:f728
raspi:~$

Nun bitte hier registrieren… => https://www.spdyn.de/register (die hinterlegte eMail-Adresse dient dann als Loginname)

Im linken Navigationsbereich kann man sich via „Host“, „Add“ einen Domainnamen kostenlos registrieren:

  1. Verfügbaren Domainnamen aus der Drop/Down-Liste heraussuchen

  2. Einen eigenen Hostnamen eintragen (kann evl. schon vergeben sein)

  3. Update-Token für ddclient erstellen (= Passwort für automatische Aktualisierung)

  4. Status im „Dashboard“ kontrollieren, ein grüner Punkt vor dem Hostnamen bedeutet dann später, dass die Host-IP erfolgreich aktualisiert wurde.

Raspberry Pi als Server

Hierfür sind zwei wichtige Komponenten erforderlich:

Software installieren

Wir installieren gleich zwei Pakete, neben dem DynDNS-Client auch einen schnellen, sparsamen Webserver. Während der Installation von ‚ddclient‘ sind erste Einstellungen vorzunehmen, die aber im Anschluss sowieso angepasst werden müssen:

raspi:~ # apt-get install ddclient nginx
Reading package lists...

Konfiguration ddclient

Laut Beschreibung des Securepoint-Wikis konfigurieren wir weiter, in dem wir die Datei mittels nano /etc/ddclient.conf editieren und auf die hier gezeigten Werte anpassen:

# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

# Protokoll angeben:
protocol=dyndns2

# Falls der WLAN-Router eine eigene Konfiguration zulässt:
#use=if, if=eth0

# Falls der im LAN laufende Raspi das Updaten auslöst:
use=web, web=checkip.spdyn.de

# Bei Verwendung des Update-Tokens:
server=update.spdyn.de
login=linuxia.my-gateway.de
password='lgoo-rule-xvbb'
linuxia.my-gateway.de

Mit systemctl restart ddclient lesen wir die Änderung ein, wobei dann anhand systemctl status ddclient erkennbar wird, ob es geklappt hat („SUCCESS: updating linuxia.my-gateway.de: good: IP address set to 84.184.188.196“).

Testen können wir das Ganze vom selben DSL-Anschluss aus leider nicht, man nehme am besten sein Smartphone, deaktiviere WLAN und surfe dann via mobile Datenverbindung die neue URL an. Alternativen sind, Freunde zu bitten, die URL aufzurufen oder sich auf einen im Internet laufenden (Cloud-)Host einzuloggen, um von dort aus die Tests durchzuführen.

Aber einen Blick in die DNS-Datenbanken kann man schon werfen:

raspi:~ # host linuxia.my-gateway.de
linuxia.my-gateway.de has address 84.184.188.196
raspi:~ #

Für das neue IPv6 muss ein eben solcher Host mit „Add“ hinzugefügt werden (mit selben Namen, allerdings mit AAAA-Record). Ebenso ist wieder ein Update-Token zu erstellen. Das automatische Updaten via ddclient kann mit der folgenden, separaten Konfiguration /etc/ddclient-v6.conf erfolgen (leider lässt sich der Dual-Stack nicht in einer einzigen Datei konfigurieren):

# Protokoll für dynmische Updates:
protocol=dyndns2

# Hier wird die IP des Raspis und nicht wie bei v4 die des WLAN-Routers gesendet:
usev6=if, if=eth0

# Udate-URL:
server=update.spdyn.de

# FQDN des Hosts für Login:
login=linuxia.my-gateway.de

# Separater Update-Token für AAAA-Record:
password='ljfn-ufne-gfex'

# FQDN des Hosts:
linuxia.my-gateway.de

Testweise können wir diese Konfiguration per Direktaufruf starten:

root@raspi:~ # ddclient -verbose -file /etc/ddclient-v6.conf
INFO:     setting IP address to 2003:f2:e729:c533:244:d6fa:fa2b:f728 for linuxia.my-gateway.de
UPDATE:   updating linuxia.my-gateway.de
CONNECT:  update.spdyn.de
CONNECTED:  using HTTP
SENDING:  GET /nic/update?system=dyndns&hostname=linuxia.my-gateway.de&myip=2003:f2:e729:c533:244:d6fa:fa2b:f728 HTTP/1.0
SENDING:   Host: update.spdyn.de
SENDING:   Authorization: Basic dG9udXMuc3BkbHajKGU6bGlmbi11Zm58LWd2LXg=
SENDING:   User-Agent: ddclient/3.8.3
SENDING:   Connection: close
SENDING:
RECEIVE:  HTTP/1.1 200 OK
RECEIVE:  Server: openresty
RECEIVE:  Content-Type: text/plain; charset=UTF-8
RECEIVE:  Connection: close
RECEIVE:  Vary: Accept-Encoding
RECEIVE:  Cache-Control: no-cache
RECEIVE:  Date: Fri, 22 May 2020 20:43:06 GMT
RECEIVE:  pragma: no-cache
RECEIVE:  expires: Tue, 21 Apr 2020 20:43:06 GMT
RECEIVE:  X-RateLimit-Limit: 60
RECEIVE:  X-RateLimit-Remaining: 59
RECEIVE:  X-Frame-Options: DENY
RECEIVE:  X-Content-Type-Options: nosniff
RECEIVE:  X-XSS-Protection: 1; mode=block
RECEIVE:  Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://apis.google.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://stats.spdns.de; object-src 'none' ; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/ ; img-src 'self' data: https://ssl.gstatic.com/ https://stats.spdns.de ; media-src 'none'; frame-src https://stats.spdns.de/ https://www.google.com/recaptcha/ https://www.youtube.com/embed/; connect-src 'self' wss://dev-av.securepoint.de; font-src 'self' https://fonts.gstatic.com
RECEIVE:  Referrer-Policy: no-referrer
RECEIVE:  Expect-CT: max-age=86400
RECEIVE:  Feature-Policy: accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'
RECEIVE:
RECEIVE:  good 2003:f2:e729:c533:244:d6fa:fa2b:f728
SUCCESS:  updating linuxia.my-gateway.de: good: IP address set to 2003:f2:e729:c533:244:d6fa:fa2b:f728
root@raspi:~ #
root@raspi:~ #
root@raspi:~ # host linuxia.my-gateway.de
linuxia.my-gateway.de has address 84.124.158.196
linuxia.my-gateway.de has IPv6 address 2003:f2:e729:c533:244:d6fa:fa2b:f728
root@raspi:~ #

Anstelle die Aktualisierung für IPv6 manuell mittels ddclient -verbose -file <CONFIG> vorzunehmen, empfiehlt es sich dann, eine eigene Systemd-Serviceunit anzulegen, die einen automatischen Start ermöglicht.

Die Nutzung dieser globalen IPv6-Unicast-Adresse ist allerdings nur dann im Internet möglich, wenn der WLAN-Router sie nach draußen durchgibt. Anders als bei IPv4, wo private Adressen in öffentliche übersetzt werden müssen, sind wir mit IPv6 normalerweise direkt im Internet erreichbar. Leider gestatten nicht alle Router das Freischalten via Firewall. Das betrifft z.B. die Speedport-Geräte der Telekom.

Bereitstellung von Webdokumenten

Konfigurieren müssen wir den Webserver ‚nginx‘ vorerst nicht, interessant ist in der Datei /etc/nginx/sites-enabled/default nur, welcher Pfad als Ablageort für die Dokumente vorgesehen ist (Anweisung: root /var/www/html;). Dort legen wir eine Datei namens index.html (also insgesamt /var/www/html/index.html) mit folgendem Inhalt an:

<html>
<head>
<title>Testseite mit ein paar Links</title>
</head>
<body bgcolor="orange">
<h2>Kleine Testseite</h2>
<h3>Ein paar Hyperlinks:</h3>
<ul>
  <li><a href="https://metager.de">Metager.de - &Uuml;bersinnliche Engine in Hannover...!</a></li>
  <li><a href="https://fireball.com/">Fireball.com - Suchmaschine der TU Berlin</a></li>
  <li><a href="https://www.shodan.io/">shodan.io - Suchmaschine f&uuml;r Sicherheitsanalysen</a></li>
  <li><a href="https://88it.de/">News Feed zu Linux und Opensource</a></li>
</ul>
</body>
</html>

Sicherheit / Raspi härten

TLS-Verschlüsselung

Heutzutage ist es üblich geworden, Webseiten verschlüsselt auszuliefern. Spätestens, wenn man einen mit Benutzername/Passwort gesicherten Zugang realisieren möchte, ist dies wichtig. Eine Voraussetzung dafür sind Zertifikate nach dem X.509-Standard, die dank der Let’s Encrypt Initiative kostenlos zur Verfügung stehen. Die beim DynDNS-Provider Securepoint erhältlichen Domainnamen eignen sich problemlos für eine Registrierung bei Let’s Encrypt.

Unter linuxize.com ist sehr schön beschrieben, wie man das sicher und robust für Debian einrichten kann. Dazu gibt es unter pemmann.de einen Kurzabriss samt Konfigurationsdateien.

Hinweis: Im WLAN-Router muss dann später der TCP-Port 443 freigeschaltet werden (siehe ganz unten).

SSH-Server

Am besten erzeugt man sich auf einer anderen Linux-Maschine, die als Workstation dient (über die normalerweise der Login auf den Raspi geschieht), ein eigenes, clientseitiges Schlüsselpaar. Damit kann der unsichere Login mit Benutzername/Passwort-Paar später deaktiviert werden. Im Prinzip sind dies nur diese beiden Zeilen:

user@linuxPC:~ $ ssh-keygen -b 4096
...
user@linuxPC:~ $ ssh-copy-id pi@RASPBERRY
...

In letzterer Zeile bitte „RASPBERRY“ durch die IP-Adresse des Geräts ersetzen. Bei dieser Aktion wird der Public-Key auf den Raspberry-Server transferiert, wobei er anhängend in die auf dem Server evl. schon vorhandene Datei /home/pi/.ssh/authorized_keys geschrieben wird.

HINWEIS: Verschlüsselt wird stets mit dem öffentlichen Schlüssel des Partners, die Entschlüsselung erfolgt dann jeweils lokal mit dem eigenen, privaten Schlüssel. Er darf das System niemals verlassen!

Nun können wir die Einstellungen des SSH-Servers ändern, indem wir die Datei /etc/ssh/sshd_config editieren.

Vor allem ist hierbei die Direktive von Zeile 1 explizit auf ‚no‘ zu setzen (PermitRootLogin no). Nach dem erfolgreichen Einbinden dieses nun erzeugten Client-Schlüsselpaares, ist es nun auch möglich, die Direktive in Zeile 2 auf ‚no‘ zu schalten (PasswordAuthentication no) sowie die weiteren Einstellungen auf die hier gezeigten Werte zu setzen:

raspi:~ # grep -v '^\s*#\|^$' /etc/ssh/sshd_config | nl
   1  PermitRootLogin no
   2  PasswordAuthentication no
   3  ChallengeResponseAuthentication no
   4  UsePAM no
   5  X11Forwarding yes
   6  PrintMotd no
   7  AcceptEnv LANG LC_*
   8  Subsystem      sftp    /usr/lib/openssh/sftp-server
raspi:~ #

Nach dem Editieren nicht vergessen, systemctl reload sshd auszuführen.

Wenn wir einmal auf dem Server sind, wollen wir schließlich nicht vergessen, die Fingerprints für die Clients bereitszustellen, so dass sie beim Erstlogin in der Lage sind, die Echtheit der Serveridentität zu verifizieren und mit „yes“ in ihrer ~/.ssh/known_hosts dauerhaft zu beglaubigen:

ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub

Einbruchsvorbeugung

Das Installieren eines Intrusion Prevention Systems (IPS) lohnt sich in jedem Falle, um unberechtigten, permanenten Loginversuchen Einhalt zu gebieten. In der Grundeinstellung ist das Jail für den SSH-Server bereits aktiv:

raspi:~ # apt-get install fail2ban
...
raspi:~ # fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:      0
|  |- Total failed:  21
|  `- File list:     /var/log/auth.log
`- Actions
   |- Currently banned:      0
   |- Total banned:  0
   `- Banned IP list:
raspi:~ #

Weiterführendes siehe unter https://www.raspberrypi.org/documentation/configuration/security.md

Port-Forwarding im WLAN-Router

Zum Einrichten der Portweiterleitung vom Internet hin zum internen Raspberry siehe z.B. den Crashkurs Fernzugriff von PCwelt.

Im Speedport-Router schaut das so aus:

../_images/raspi-portweiterleitung.png

Möchte man wie beschrieben TLS-Verschlüsselung anbieten, muss im WLAN-Router zusätzlich zu Port 80 auch eine Portweiterleitung für Port 443 angelegt werden.