<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Vpn – Serverküche</title><link>https://serverkueche.de/tags/vpn/</link><description>Vpn – Neueste Beiträge von Serverküche</description><generator>Hugo</generator><language>de-DE</language><managingEditor>feedback@serverkueche.de (Serverküche)</managingEditor><webMaster>feedback@serverkueche.de (Serverküche)</webMaster><copyright>2026 Serverküche</copyright><lastBuildDate>Thu, 30 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/tags/vpn/index.xml" rel="self" type="application/rss+xml"/><item><title>WireGuard-VPN einrichten: sicherer Zugang zum eigenen Server</title><link>https://serverkueche.de/tutorials/wireguard-vpn-einrichten/</link><pubDate>Thu, 30 Jul 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/tutorials/wireguard-vpn-einrichten/</guid><description>Ein schlankes WireGuard-VPN nativ auf dem netcup-Server: Schlüssel, Server- und Client-Konfig, Firewall, Handy-QR – plus der Docker-Fallstrick beim Full-Tunnel.</description><content:encoded><![CDATA[<p>Bisher ist alles auf deinem Server öffentlich hinter Traefik erreichbar. Manches soll aber
<strong>nur für dich</strong> laufen – ein Admin-Dashboard, eine Datenbank, ein interner Dienst. Mit
WireGuard baust du dir einen verschlüsselten Tunnel direkt in den Server, ohne dafür weitere
Ports ins Internet zu öffnen.</p>
<h2 id="was-bauen-wir">Was bauen wir?</h2>
<p>Ein <strong>WireGuard-VPN</strong> nativ auf dem Server (kein Container – WireGuard steckt im Linux-Kernel,
das ist die schlankeste und stabilste Variante). Deine Geräte – Laptop, Handy – bekommen einen
verschlüsselten Tunnel zum Server und darüber eine private IP im Netz <code>10.8.0.0/24</code>. Zwei
Betriebsarten, die du über eine einzige Client-Zeile umschaltest:</p>
<ul>
<li><strong>Split-Tunnel (Standard):</strong> Nur der Weg <strong>zum Server und seinen internen Diensten</strong> läuft
durchs VPN. Damit erreichst du Dinge, die <em>nicht</em> öffentlich stehen sollen (ein Dashboard,
eine Datenbank, später ein eigener DNS-Filter), ohne dafür einen Port zu veröffentlichen.</li>
<li><strong>Full-Tunnel (optional):</strong> <strong>Der gesamte</strong> Internetverkehr deines Geräts läuft über den
Server – praktisch im offenen Café-WLAN, weil dann alles verschlüsselt über deine eigene IP
rausgeht.</li>
</ul>
<p>Getestet mit <strong>wireguard-tools 1.0.20210914</strong> unter Debian 13 (Kernel 6.12, WireGuard ist dort
fest eingebaut).</p>
<p>Warum WireGuard und nicht OpenVPN oder ein offener Port? WireGuard läuft <strong>im Kernel</strong>, ist mit
wenigen tausend Zeilen Code winzig (leicht prüfbar, kleine Angriffsfläche), braucht nur einen
<strong>einzigen UDP-Port</strong> und kommt mit einer Handvoll Konfigzeilen aus. Der Handshake ist so
schlank, dass eine mobile Verbindung nach dem Aufwachen praktisch sofort wieder steht. Statt
also jeden internen Dienst mit eigener Authentifizierung ins Netz zu stellen, legst du <strong>einen</strong>
sicheren Tunnel – und alles dahinter bleibt privat.</p>
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Ein Server mit aktiver <a href="/tutorials/firewall-ufw-einrichten/">UFW-Firewall</a> und root- bzw.
sudo-Zugang.</li>
<li>Wenn du zusätzlich die <a href="/tutorials/netcup-firewall-einrichten/">netcup-Firewall</a> nutzt: den
WireGuard-Port dort später freigeben (fertige Vorlage im Firewall-Tutorial).</li>
<li>Ein Client-Gerät mit der offiziellen <strong>WireGuard-App</strong> (Windows, macOS, Linux, iOS, Android).</li>
</ul>
<div class="not-prose my-6 overflow-hidden rounded-xl border border-paprika-200 bg-paprika-50 dark:border-paprika-800 dark:bg-paprika-900/20"
     data-track-content data-content-name="Affiliate-Box · /tutorials/wireguard-vpn-einrichten/" data-content-piece="VPS 1000 G12">
  <div class="flex items-center justify-between border-b border-paprika-200 bg-paprika-100 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-paprika-700 dark:border-paprika-800 dark:bg-paprika-900/40 dark:text-paprika-300">
    <span>🍳 Empfehlung</span>
    <span title="Mit * markierte Links sind Affiliate-Links.">Anzeige</span>
  </div>
  <div class="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between">
    <div>
      <p class="text-lg font-bold text-slate-900 dark:text-white">VPS 1000 G12</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">4 vCore · 8 GB RAM · 256 GB NVMe</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">ab 10,36 €/Monat</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Ein WireGuard-VPN braucht kaum Ressourcen – der kleinste VPS reicht locker.</p>
    </div>
    <a href="https://www.netcup.com/de/server/vps/vps-1000-g12-12m?ref=44083" rel="sponsored noopener" target="_blank"
   data-track-event="Affiliate|netcup: Affiliate-Box|VPS 1000 G12 · {page}"
   class="inline-flex shrink-0 items-center justify-center rounded-lg bg-paprika-600 px-5 py-2.5 font-semibold text-white transition-colors hover:bg-paprika-700">
  Zu netcup →
</a>

  </div><div class="px-4 pb-4"><p class="not-prose my-3 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-lg border border-herb-500/40 bg-herb-50 px-3 py-2 text-sm text-slate-700 dark:bg-herb-900/20 dark:text-slate-200">
  <span>💶 <strong>5 € Gutschein</strong> für netcup-Neukunden:</span><code data-track-voucher="36nc17844976032"
        class="rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</code>
  <span class="text-xs text-slate-500 dark:text-slate-400">(nur Neukunden, keine Domains)</span>
</p></div>
</div>

<h2 id="schritt-für-schritt">Schritt für Schritt</h2>
<h3 id="schritt-1-wireguard-installieren">Schritt 1: WireGuard installieren</h3>
<p>Der Kernel bringt WireGuard schon mit; du brauchst nur die Userspace-Werkzeuge (und <code>qrencode</code>
für den Handy-QR-Code):</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install -y wireguard wireguard-tools qrencode</span></span></code></pre></div>
</div>
<p>Prüfe, dass das Kernel-Modul geladen werden kann:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo modprobe wireguard <span class="o">&amp;&amp;</span> lsmod <span class="p">|</span> grep wireguard</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">wireguard             118784  0</span></span></code></pre></div>
</div>
<h3 id="schritt-2-schlüsselpaare-erzeugen">Schritt 2: Schlüsselpaare erzeugen</h3>
<p>WireGuard authentifiziert rein über <strong>Schlüsselpaare</strong> – kein Passwort, keine Zertifikate.
Jede Seite (Server und jeder Client) hat einen privaten und einen öffentlichen Schlüssel. Der
private darf <strong>niemals</strong> die Maschine verlassen. Setz zuerst eine strikte Dateimaske, damit die
Schlüssel nicht für andere lesbar sind:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">umask</span> <span class="m">077</span>
</span></span><span class="line"><span class="cl">wg genkey <span class="p">|</span> sudo tee /etc/wireguard/server.key <span class="p">|</span> wg pubkey <span class="p">|</span> sudo tee /etc/wireguard/server.pub</span></span></code></pre></div>
</div>
<p>Der erste Befehl erzeugt den privaten Schlüssel (<code>server.key</code>), leitet ihn durch <code>wg pubkey</code>
und speichert den öffentlichen (<code>server.pub</code>). Erzeuge auf dieselbe Weise ein Paar für deinen
ersten Client:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wg genkey <span class="p">|</span> tee client.key <span class="p">|</span> wg pubkey &gt; client.pub</span></span></code></pre></div>
</div>
<p>Du hast jetzt vier Schlüssel. Merke dir die Zuordnung: In die <strong>Server</strong>-Konfig kommt der
<strong>private Server-Schlüssel</strong> und der <strong>öffentliche Client</strong>-Schlüssel; in die <strong>Client</strong>-Konfig
umgekehrt. Das ist die häufigste Fehlerquelle – nie den privaten Schlüssel der falschen Seite
eintragen.</p>
<h3 id="schritt-3-server-konfiguration-anlegen">Schritt 3: Server-Konfiguration anlegen</h3>
<p>Lege <code>/etc/wireguard/wg0.conf</code> an. Trag deinen privaten Server-Schlüssel und den öffentlichen
Client-Schlüssel ein:</p>
<div class="sk-code">
  <span class="sk-code-head">INI</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Interface]</span>
</span></span><span class="line"><span class="cl"><span class="na">Address</span> <span class="o">=</span> <span class="s">10.8.0.1/24</span>
</span></span><span class="line"><span class="cl"><span class="na">ListenPort</span> <span class="o">=</span> <span class="s">51820</span>
</span></span><span class="line"><span class="cl"><span class="na">PrivateKey</span> <span class="o">=</span> <span class="s">DEIN_SERVER_PRIVATE_KEY</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Peer]</span>
</span></span><span class="line"><span class="cl"><span class="na">PublicKey</span> <span class="o">=</span> <span class="s">DEIN_CLIENT_PUBLIC_KEY</span>
</span></span><span class="line"><span class="cl"><span class="na">AllowedIPs</span> <span class="o">=</span> <span class="s">10.8.0.2/32</span></span></span></code></pre></div>
</div>
<p>Was die Zeilen bedeuten:</p>
<ul>
<li><strong><code>Address</code></strong> – die VPN-interne IP des Servers (<code>10.8.0.1</code>); die <code>/24</code> spannt das
VPN-Subnetz <code>10.8.0.0/24</code> auf.</li>
<li><strong><code>ListenPort</code></strong> – der UDP-Port, auf dem WireGuard lauscht. <code>51820</code> ist der Standard.</li>
<li><strong><code>[Peer]</code> + <code>AllowedIPs = 10.8.0.2/32</code></strong> – dieser Client darf ausschließlich unter der
VPN-IP <code>10.8.0.2</code> auftauchen. <strong>Wichtig:</strong> <code>AllowedIPs</code> hat auf beiden Seiten eine andere
Bedeutung – auf dem <strong>Server</strong> ist es die feste Tunnel-IP des Clients, auf dem <strong>Client</strong>
(Schritt 5) legt es fest, <em>was</em> durch den Tunnel geroutet wird.</li>
</ul>
<p>Für jeden weiteren Client fügst du später einfach einen weiteren <code>[Peer]</code>-Block mit der
nächsten IP (<code>10.8.0.3/32</code> …) hinzu.</p>
<h3 id="schritt-4-firewall-öffnen-und-den-tunnel-starten">Schritt 4: Firewall öffnen und den Tunnel starten</h3>
<p>WireGuard spricht über <strong>UDP</strong> – gib den Port frei. In der UFW:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw allow 51820/udp</span></span></code></pre></div>
</div>
<p>Sitzt zusätzlich die netcup-Firewall davor, brauchst du dort eine eingehende Regel
<code>UDP · Ziel-Port 51820</code> – die fertige Vorlage steht im
<a href="/tutorials/netcup-firewall-einrichten/">netcup-Firewall-Tutorial</a>. Starte nun den Tunnel und
richte den Autostart ein:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo wg-quick up wg0
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> wg-quick@wg0</span></span></code></pre></div>
</div>
<p>Prüfe, dass die Schnittstelle läuft:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo wg show wg0</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">interface: wg0
</span></span><span class="line"><span class="cl">  public key: nZT4kozgZjtHVzGfPRL+niI4llEQCUqGpYG0ni+fAT4=
</span></span><span class="line"><span class="cl">  private key: (hidden)
</span></span><span class="line"><span class="cl">  listening port: 51820
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">peer: j5h2jl87JApAbWrJikrth3H/6fNuf6X2TgBgDSJVwwE=
</span></span><span class="line"><span class="cl">  allowed ips: 10.8.0.2/32</span></span></code></pre></div>
</div>
<p>Der Peer ist eingetragen, aber es gibt noch keinen <code>latest handshake</code> – logisch, der Client
fehlt noch.</p>
<h3 id="schritt-5-client-einrichten">Schritt 5: Client einrichten</h3>
<p>Die Client-Konfig ist das Spiegelbild. Erstelle eine Datei <code>client.conf</code> (auf dem Server zum
Erzeugen des QR-Codes, den Inhalt trägst du dann ins Client-Gerät):</p>
<div class="sk-code">
  <span class="sk-code-head">INI</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Interface]</span>
</span></span><span class="line"><span class="cl"><span class="na">Address</span> <span class="o">=</span> <span class="s">10.8.0.2/24</span>
</span></span><span class="line"><span class="cl"><span class="na">PrivateKey</span> <span class="o">=</span> <span class="s">DEIN_CLIENT_PRIVATE_KEY</span>
</span></span><span class="line"><span class="cl"><span class="c1"># DNS = 10.8.0.1   # nur aktivieren, wenn auf 10.8.0.1 wirklich ein DNS-Resolver läuft – sonst z. B. 1.1.1.1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Peer]</span>
</span></span><span class="line"><span class="cl"><span class="na">PublicKey</span> <span class="o">=</span> <span class="s">DEIN_SERVER_PUBLIC_KEY</span>
</span></span><span class="line"><span class="cl"><span class="na">Endpoint</span> <span class="o">=</span> <span class="s">DEINE_SERVER_IP:51820</span>
</span></span><span class="line"><span class="cl"><span class="na">AllowedIPs</span> <span class="o">=</span> <span class="s">10.8.0.0/24</span>
</span></span><span class="line"><span class="cl"><span class="na">PersistentKeepalive</span> <span class="o">=</span> <span class="s">25</span></span></span></code></pre></div>
</div>
<p>Die entscheidenden Zeilen:</p>
<ul>
<li><strong><code>Endpoint</code></strong> – die <strong>öffentliche</strong> IP deines Servers plus Port. Hierhin baut der Client den
Tunnel auf.</li>
<li><strong><code>AllowedIPs = 10.8.0.0/24</code></strong> – das ist der <strong>Split-Tunnel</strong>: Nur Verkehr ins VPN-Subnetz
läuft durch den Tunnel, dein normaler Internetverkehr bleibt direkt. Für den <strong>Full-Tunnel</strong>
(alles über den Server) setzt du hier <code>0.0.0.0/0</code> – dazu Schritt 6.</li>
<li><strong><code>DNS = 10.8.0.1</code></strong> – bewusst auskommentiert. Diese Zeile leitet den <strong>gesamten</strong> System-DNS
deines Geräts auf <code>10.8.0.1</code> um – dort lauscht aber standardmäßig <strong>nichts</strong> auf Port 53, sodass
bei aktivem Tunnel die Namensauflösung bricht. Setz sie nur, wenn auf dem Server wirklich ein
eigener DNS-Resolver/-Filter läuft; brauchst du im Tunnel einfach einen funktionierenden DNS,
trag stattdessen einen echten Resolver wie <code>1.1.1.1</code> ein.</li>
<li><strong><code>PersistentKeepalive = 25</code></strong> – hält die Verbindung offen, wenn der Client hinter einem
NAT/CGNAT sitzt (Handy im Mobilfunknetz).</li>
</ul>
<p>Fürs Handy erzeugst du aus der Datei einen QR-Code direkt im Terminal – in der WireGuard-App
„Tunnel hinzufügen → QR-Code scannen&quot;:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">qrencode -t ansiutf8 &lt; client.conf</span></span></code></pre></div>
</div>
<p>Am Desktop importierst du die <code>client.conf</code> einfach in der WireGuard-App. Danach die
<code>client.conf</code> und <code>client.key</code> vom Server <strong>löschen</strong> (sie gehören aufs Endgerät, nicht auf den
Server).</p>
<h3 id="schritt-6-verbindung-prüfen-und-full-tunnel-aktivieren">Schritt 6: Verbindung prüfen (und Full-Tunnel aktivieren)</h3>
<p>Aktiviere den Tunnel in der Client-App und sieh auf dem Server nach:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo wg show wg0</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">peer: j5h2jl87JApAbWrJikrth3H/6fNuf6X2TgBgDSJVwwE=
</span></span><span class="line"><span class="cl">  endpoint: 203.0.113.24:34582
</span></span><span class="line"><span class="cl">  allowed ips: 10.8.0.2/32
</span></span><span class="line"><span class="cl">  latest handshake: 45 seconds ago
</span></span><span class="line"><span class="cl">  transfer: 1.58 KiB received, 1.39 KiB sent</span></span></code></pre></div>
</div>
<p>Ein <code>latest handshake</code> plus wachsender <code>transfer</code> heißt: Der Tunnel steht. Vom Client aus
erreichst du den Server jetzt unter seiner VPN-IP:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ping 10.8.0.1</span></span></code></pre></div>
</div>
<p>Willst du den <strong>Full-Tunnel</strong> (gesamter Verkehr übers VPN), sind zwei Änderungen nötig. Auf dem
<strong>Client</strong> <code>AllowedIPs = 0.0.0.0/0</code> setzen. Setz auf dem Client jetzt außerdem eine <code>DNS =</code>-Zeile
(z. B. <code>DNS = 1.1.1.1</code> oder deinen eigenen Resolver): Sobald <em>aller</em> Verkehr durch den Tunnel
läuft, ist der DNS-Leak am Tunnel vorbei nämlich der <strong>Normalfall</strong>, nicht die Ausnahme – ohne
diese Zeile fragt dein Gerät weiter die DNS-Server deines lokalen Netzes ab (Details im
<a href="/tutorials/wireguard-vpn-einrichten/#wenn-es-nicht-funktioniert">DNS-Leak-Eintrag</a> im Fehlerteil). Auf dem <strong>Server</strong> das Routing
plus NAT aktivieren – zuerst IP-Forwarding dauerhaft einschalten:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;net.ipv4.ip_forward=1&#34;</span> <span class="p">|</span> sudo tee /etc/sysctl.d/99-wireguard.conf
</span></span><span class="line"><span class="cl">sudo sysctl -p /etc/sysctl.d/99-wireguard.conf</span></span></code></pre></div>
</div>
<p>Dann in der <code>[Interface]</code>-Sektion der <code>wg0.conf</code> die Weiterleitungs- und NAT-Regeln ergänzen
(ersetze <code>eth0</code> durch dein echtes externes Interface, siehe <code>ip route get 1.1.1.1</code>):</p>
<div class="sk-code">
  <span class="sk-code-head">INI</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">PostUp</span> <span class="o">=</span> <span class="s">iptables -I FORWARD 1 -i wg0 -j ACCEPT; iptables -I FORWARD 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE</span>
</span></span><span class="line"><span class="cl"><span class="na">PostDown</span> <span class="o">=</span> <span class="s">iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE</span></span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Docker auf demselben Server? Dann müssen die Regeln nach oben
  </p>
  <div class="prose-kitchen text-sm">Läuft auf dem Server auch Docker (z. B. dein <a href="/tutorials/reverse-proxy-traefik/">Traefik</a>),
setzt Docker die <code>FORWARD</code>-Policy auf <strong>DROP</strong> und schiebt eigene Ketten davor. Hängst du die
WireGuard-Regeln mit <code>-A</code> (anhängen) an, wird der <strong>Rückweg</strong> deiner Antwortpakete von Docker
verworfen – der Handshake klappt, aber es kommt kein Internet an. Deshalb oben <code>-I FORWARD 1</code>
(<strong>einfügen</strong> ganz vorne) und die <code>RELATED,ESTABLISHED</code>-Regel für den Rückweg. Dasselbe gilt bei
aktiver UFW, denn auch die setzt eine <code>FORWARD</code>-Default-Policy auf DROP – das <code>-I FORWARD 1</code>
übersteuert sie, sodass die Regeln in beiden Fällen greifen. Genau so getestet: Danach geht der
Client mit der IP des Servers ins Netz.</div>
</div>
<p>Nach <code>sudo wg-quick down wg0 &amp;&amp; sudo wg-quick up wg0</code> prüfst du den Full-Tunnel, indem du auf
dem Client deine öffentliche IP abfragst – sie sollte jetzt die <strong>Server-IP</strong> sein:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s ifconfig.me</span></span></code></pre></div>
</div>
<h3 id="schritt-7-einen-dienst-nur-übers-vpn-erreichbar-machen">Schritt 7: Einen Dienst nur übers VPN erreichbar machen</h3>
<p>Jetzt der eigentliche Gewinn. Ein Dienst, den nur du brauchst, muss nicht öffentlich hinter
Traefik stehen – du bindest ihn an die <strong>VPN-IP</strong> des Servers (<code>10.8.0.1</code>), dann ist er
ausschließlich durch den Tunnel erreichbar, ohne einen einzigen Port im Internet.</p>
<p>Bei einem Docker-Dienst heißt das: den Port nicht an <code>0.0.0.0</code>, sondern gezielt an die VPN-IP
veröffentlichen. Statt <code>- &quot;8080:80&quot;</code> also:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;10.8.0.1:8080:80&#34;</span></span></span></code></pre></div>
</div>
<p>Der Dienst hört damit nur auf der WireGuard-Schnittstelle – aus dem Internet ist er
unsichtbar, über das VPN erreichst du ihn unter <code>http://10.8.0.1:8080</code>. (Wichtig: <code>wg0</code> muss
beim Start des Containers schon oben sein – der <code>systemctl enable</code> aus Schritt 4 sorgt dafür.)</p>
<p>Nach demselben Prinzip kannst du auch <strong>SSH nur noch übers VPN</strong> zulassen: In der UFW den
öffentlichen SSH-Zugang entfernen und stattdessen nur aus dem VPN-Subnetz erlauben –</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo ufw allow from 10.8.0.0/24 to any port <span class="m">22</span> proto tcp</span></span></code></pre></div>
</div>
<p>– dann die öffentliche SSH-Regel löschen. <strong>Vorher unbedingt</strong> eine zweite Verbindung übers VPN
testen, sonst sperrst du dich aus (im Notfall hilft die VNC-Konsole im
<a href="/tutorials/netcup-snapshots-scp/">netcup-SCP</a>).</p>
<h2 id="wenn-es-nicht-funktioniert">Wenn es nicht funktioniert</h2>
<div class="troubleshoot not-prose">
<p><strong>In <code>wg show</code> erscheint nie ein <code>latest handshake</code>.</strong> Der UDP-Port ist nicht erreichbar oder
die Schlüssel passen nicht. WireGuard ist bei falschen Schlüsseln <strong>stumm</strong> – es gibt keine
Fehlermeldung, nur keinen Handshake. Prüfe: <code>sudo ufw status</code> (Port <code>51820/udp</code> offen?), die
netcup-Firewall (eingehend <code>UDP 51820</code>), den <code>Endpoint</code> im Client (richtige öffentliche IP?) und
dass privater und öffentlicher Schlüssel nicht vertauscht sind.</p>
<p><strong>Handshake ist da, aber im Full-Tunnel kommt kein Internet an.</strong> Fast immer das Routing/NAT.
Ist <code>net.ipv4.ip_forward=1</code> gesetzt (<code>sysctl net.ipv4.ip_forward</code>)? Läuft Docker mit – oder ist
UFW aktiv –, greift die <code>FORWARD</code>-DROP-Falle aus dem Warnkasten oben; beide setzen die
Forward-Policy auf DROP. Die Regeln müssen mit <code>-I FORWARD 1</code> <strong>vor</strong> den bestehenden Ketten
stehen. Stimmt das externe Interface im <code>MASQUERADE</code> (<code>eth0</code> vs. etwas anderes)?</p>
<p><strong>Full-Tunnel steht, aber DNS-Anfragen laufen weiter am Tunnel vorbei (DNS-Leak).</strong> Ohne <code>DNS =</code>-Zeile fragt dein Gerät weiter die DNS-Server deines lokalen Netzes – im offenen WLAN sieht
der Betreiber also weiter, welche Domains du aufrufst. Prüfe unter Linux mit <code>resolvectl status</code>, welcher DNS-Server dem Interface <code>wg0</code> zugeordnet ist, oder mach einen Leak-Test (z. B.
auf <code>dnsleaktest.com</code>): Tauchen dort fremde Resolver auf, trägst du in der Client-Konfig einen
echten Resolver ein (<code>DNS = 1.1.1.1</code>) – oder den Server selbst (<code>10.8.0.1</code>), falls dort ein
eigener DNS-Resolver läuft.</p>
<p><strong>Der Tunnel steht, aber große Übertragungen (SSH, HTTPS, Downloads) hängen oder brechen ab.</strong>
Ein MTU-Problem. <code>wg-quick</code> setzt die MTU auf <code>1420</code>; in manchen Netzen (DS-Lite, bestimmte
Mobilfunknetze) ist das noch zu hoch. Setz testweise <code>MTU = 1412</code> oder <code>1280</code> in der
<code>[Interface]</code>-Sektion des Clients.</p>
<p><strong><code>wg-quick up</code> meldet <code>resolvconf: command not found</code>.</strong> Die <code>DNS =</code>-Zeile braucht
<code>resolvconf</code>. Entweder <code>sudo apt install openresolv</code> installieren oder die <code>DNS</code>-Zeile
entfernen, wenn du den VPN-DNS nicht brauchst.</p>
<p><strong>Die Verbindung schläft ein, sobald das Handy kurz nichts sendet.</strong> Der Client sitzt hinter
NAT/CGNAT. <code>PersistentKeepalive = 25</code> in der Client-Konfig hält die Verbindung offen.</p>

</div>

<h2 id="wartung--backups">Wartung &amp; Backups</h2>
<ul>
<li>
<p><strong>Weitere Geräte hinzufügen.</strong> Pro Gerät ein eigenes Schlüsselpaar und ein weiterer
<code>[Peer]</code>-Block in der <code>wg0.conf</code> mit der nächsten freien IP (<code>10.8.0.3/32</code> …). Nie denselben
Schlüssel auf zwei Geräten verwenden. Damit bestehende Tunnel dabei <strong>nicht abreißen</strong>, lädst
du die geänderte Konfig im Betrieb neu, statt <code>down/up</code> zu machen:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo wg syncconf wg0 &lt;<span class="o">(</span>wg-quick strip wg0<span class="o">)</span></span></span></code></pre></div>
</div>
</li>
<li>
<p><strong>Schlüssel sichern – sie sind das Herz des VPN.</strong> Nimm <code>/etc/wireguard/</code> in dein
<a href="/tutorials/backups-mit-restic/">verschlüsseltes Restic-Backup</a> auf. Wer die privaten
Schlüssel hat, kommt ins VPN – behandle sie wie Passwörter.</p>
</li>
<li>
<p><strong>Aktuell halten.</strong> <code>wireguard-tools</code> bekommt seine Updates über
<a href="/tutorials/automatische-updates-unattended-upgrades/">automatische Sicherheitsupdates</a>; das
Kernel-Modul kommt mit den Kernel-Updates.</p>
</li>
<li>
<p><strong>Interne Dienste ans VPN binden.</strong> Der eigentliche Gewinn: Dienste, die niemand aus dem
Internet erreichen soll, lässt du nur auf <code>10.8.0.1</code> lauschen (statt <code>0.0.0.0</code>) – dann sind
sie ausschließlich über den Tunnel erreichbar, ganz ohne öffentlichen Port.</p>
</li>
<li>
<p><strong>Bei Server-Umzug</strong> die neue öffentliche IP im <code>Endpoint</code> jedes Clients nachziehen –
sonst findet der Client den Server nicht mehr.</p>
</li>
<li>
<p><strong>Ehrlich zum Aufwand:</strong> Einmal eingerichtet, läuft WireGuard praktisch wartungsfrei. Der
einzige wiederkehrende Handgriff ist das Anlegen eines Peers pro neuem Gerät.</p>
</li>
</ul>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Viele Geräte? Dann lohnt ein Mesh-Manager
  </p>
  <div class="prose-kitchen text-sm">Solange du eine Handvoll Geräte zu <strong>einem</strong> Server verbindest, ist das reine WireGuard hier
genau richtig – volle Kontrolle, nichts läuft über Dritte. Sobald du aber viele Geräte
untereinander vernetzen willst (Laptop ↔ Handy ↔ mehrere Server), wird die manuelle
Peer-Pflege mühsam. Dann lohnt ein Blick auf <strong>Tailscale</strong> oder das selbstgehostete
<strong>Headscale</strong>: Beide bauen <em>auf</em> WireGuard auf und übernehmen nur den Schlüsselaustausch und
das Vernetzen automatisch. Das Fundament aus diesem Tutorial verstehst du dann trotzdem – es
ist dasselbe darunter.</div>
</div>
]]></content:encoded></item></channel></rss>