feat(help): domain-tls topic with the reverse-proxy setup guide
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
28c28df868
commit
d0275db308
|
|
@ -0,0 +1,51 @@
|
||||||
|
@php
|
||||||
|
$h = 'font-display text-base font-semibold text-ink';
|
||||||
|
$p = 'text-sm leading-relaxed text-ink-2';
|
||||||
|
$li = 'text-sm leading-relaxed text-ink-2';
|
||||||
|
$code = 'rounded bg-inset px-1.5 py-0.5 font-mono text-[12px] text-accent-text';
|
||||||
|
$pre = 'overflow-x-auto rounded-md border border-line bg-inset p-3 font-mono text-[11px] leading-relaxed text-ink-2';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Drei Betriebsarten</h3>
|
||||||
|
<p class="{{ $p }}">Wo und wie das Panel erreichbar ist, stellst du unter <span class="text-ink">System → Domain & TLS</span> ein.
|
||||||
|
Änderungen an der Domain greifen erst nach einem <span class="text-ink">Stack-Neustart</span> (Knopf in System).</p>
|
||||||
|
<ul class="ml-4 list-disc space-y-1.5">
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">Bare-IP (ohne Domain):</span> Klartext-HTTP über <code class="{{ $code }}">http://<Server-IP></code>. Bleibt immer als Recovery-Weg erreichbar.</li>
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">Eingebautes TLS:</span> Domain eintragen → Clusev holt automatisch ein Let's-Encrypt-Zertifikat und erzwingt HTTPS. Voraussetzung: die Domain zeigt per DNS auf diesen Server, Ports 80/443 sind öffentlich erreichbar.</li>
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">Externer Reverse-Proxy:</span> ein vorgelagerter Proxy Manager terminiert TLS, Clusev liefert nur HTTP. Anleitung unten.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Eingebautes TLS (Let's Encrypt)</h3>
|
||||||
|
<ol class="ml-4 list-decimal space-y-1.5">
|
||||||
|
<li class="{{ $li }}">DNS deiner Domain auf die Server-IP zeigen lassen.</li>
|
||||||
|
<li class="{{ $li }}">System → Domain eintragen, TLS-Terminierung auf <span class="text-ink">„Eingebautes TLS"</span>.</li>
|
||||||
|
<li class="{{ $li }}">Speichern → Stack neu starten. Das Zertifikat wird beim ersten HTTPS-Aufruf automatisch ausgestellt und erneuert.</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Hinter einem externen Reverse-Proxy (Proxy Manager)</h3>
|
||||||
|
<p class="{{ $p }}">In diesem Modus terminiert dein vorgelagerter Proxy TLS; Clusev stellt <span class="text-ink">kein</span> eigenes Zertifikat aus.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">1. Im Panel:</span> System → Domain eintragen → TLS-Terminierung auf <span class="text-ink">„Externer Reverse-Proxy"</span> → speichern → Stack neu starten.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">2. Im Proxy Manager:</span> einen neuen Host/Eintrag anlegen mit diesen Werten:</p>
|
||||||
|
<pre class="{{ $pre }}">Eingehende Domain: panel.<deine-domain>
|
||||||
|
Ziel / Backend: http://<Clusev-Server-IP>:80 (HTTP, nicht HTTPS)
|
||||||
|
Weiterreichen: Host-Header + X-Forwarded-Proto: https
|
||||||
|
WebSocket: aktivieren (für Realtime: /app/* und /apps/*)</pre>
|
||||||
|
<p class="{{ $p }}">Wichtig: das Ziel ist <span class="text-ink">HTTP auf Port 80</span> des Clusev-Servers, und der Proxy muss <code class="{{ $code }}">X-Forwarded-Proto: https</code> sowie WebSocket-Upgrades weiterreichen — sonst bleiben Assets/Login bzw. die Live-Verbindung leer.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">3. Auf dem Clusev-Server (`.env`):</span> setze die Proxy-Adresse als vertrauenswürdig — sonst sind das `Secure`-Cookie und die echte Besucher-IP im Audit-Log nicht korrekt:</p>
|
||||||
|
<pre class="{{ $pre }}">TRUSTED_PROXY_CIDR=<Proxy-IP>/32
|
||||||
|
|
||||||
|
# danach einmal anwenden:
|
||||||
|
sudo ./update.sh</pre>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">4. Firewall:</span> sichere den HTTP-Port (80) des Clusev-Servers so ab, dass er <span class="text-ink">nur vom Proxy</span> erreichbar ist.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}">Hinweis: Über die Domain (HTTPS) funktioniert alles inkl. Security-Keys. Der Bare-IP-Weg (<code class="{{ $code }}">http://<Server-IP></code>) bleibt als Recovery erreichbar — dort gelten die Einschränkungen aus „Sicherheit & 2FA".</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
@php
|
||||||
|
$h = 'font-display text-base font-semibold text-ink';
|
||||||
|
$p = 'text-sm leading-relaxed text-ink-2';
|
||||||
|
$li = 'text-sm leading-relaxed text-ink-2';
|
||||||
|
$code = 'rounded bg-inset px-1.5 py-0.5 font-mono text-[12px] text-accent-text';
|
||||||
|
$pre = 'overflow-x-auto rounded-md border border-line bg-inset p-3 font-mono text-[11px] leading-relaxed text-ink-2';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Three modes</h3>
|
||||||
|
<p class="{{ $p }}">Where and how the panel is reachable is set under <span class="text-ink">System → Domain & TLS</span>.
|
||||||
|
Domain changes apply only after a <span class="text-ink">stack restart</span> (button in System).</p>
|
||||||
|
<ul class="ml-4 list-disc space-y-1.5">
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">Bare IP (no domain):</span> plain HTTP at <code class="{{ $code }}">http://<server-ip></code>. Always stays reachable as a recovery path.</li>
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">Built-in TLS:</span> set a domain → Clusev obtains a Let's Encrypt certificate automatically and enforces HTTPS. Requires the domain's DNS to point here and ports 80/443 to be publicly reachable.</li>
|
||||||
|
<li class="{{ $li }}"><span class="text-ink">External reverse proxy:</span> an upstream proxy manager terminates TLS, Clusev serves HTTP only. Guide below.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Built-in TLS (Let's Encrypt)</h3>
|
||||||
|
<ol class="ml-4 list-decimal space-y-1.5">
|
||||||
|
<li class="{{ $li }}">Point your domain's DNS at the server IP.</li>
|
||||||
|
<li class="{{ $li }}">System → enter the domain, TLS termination set to <span class="text-ink">"Built-in TLS"</span>.</li>
|
||||||
|
<li class="{{ $li }}">Save → restart the stack. The certificate is issued and renewed automatically on the first HTTPS request.</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<h3 class="{{ $h }}">Behind an external reverse proxy (proxy manager)</h3>
|
||||||
|
<p class="{{ $p }}">In this mode your upstream proxy terminates TLS; Clusev issues <span class="text-ink">no</span> certificate of its own.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">1. In the panel:</span> System → enter the domain → TLS termination to <span class="text-ink">"External reverse proxy"</span> → save → restart the stack.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">2. In the proxy manager:</span> create a new host/entry with these values:</p>
|
||||||
|
<pre class="{{ $pre }}">Incoming domain: panel.<your-domain>
|
||||||
|
Target / backend: http://<clusev-server-ip>:80 (HTTP, not HTTPS)
|
||||||
|
Forward: Host header + X-Forwarded-Proto: https
|
||||||
|
WebSocket: enable (for realtime: /app/* and /apps/*)</pre>
|
||||||
|
<p class="{{ $p }}">Important: the target is <span class="text-ink">HTTP on port 80</span> of the Clusev server, and the proxy must forward <code class="{{ $code }}">X-Forwarded-Proto: https</code> and WebSocket upgrades — otherwise assets/login or the live connection stay blank.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">3. On the Clusev server (`.env`):</span> mark the proxy address as trusted — otherwise the `Secure` cookie and the real visitor IP in the audit log are not correct:</p>
|
||||||
|
<pre class="{{ $pre }}">TRUSTED_PROXY_CIDR=<proxy-ip>/32
|
||||||
|
|
||||||
|
# then apply once:
|
||||||
|
sudo ./update.sh</pre>
|
||||||
|
|
||||||
|
<p class="{{ $p }}"><span class="text-ink">4. Firewall:</span> secure the Clusev server's HTTP port (80) so it is reachable <span class="text-ink">only from the proxy</span>.</p>
|
||||||
|
|
||||||
|
<p class="{{ $p }}">Note: over the domain (HTTPS) everything works including security keys. The bare-IP path (<code class="{{ $code }}">http://<server-ip></code>) stays reachable for recovery — the limits there are covered under "Security & 2FA".</p>
|
||||||
|
</div>
|
||||||
|
|
@ -36,4 +36,14 @@ class HelpPageTest extends TestCase
|
||||||
$this->actAsAdmin();
|
$this->actAsAdmin();
|
||||||
Livewire::test(Index::class, ['topic' => 'bogus'])->assertSet('topic', 'overview');
|
Livewire::test(Index::class, ['topic' => 'bogus'])->assertSet('topic', 'overview');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_domain_tls_topic_has_the_reverse_proxy_guide(): void
|
||||||
|
{
|
||||||
|
$this->actAsAdmin();
|
||||||
|
Livewire::test(Index::class)
|
||||||
|
->set('topic', 'domain-tls')
|
||||||
|
->assertSee('X-Forwarded-Proto')
|
||||||
|
->assertSee('TRUSTED_PROXY_CIDR')
|
||||||
|
->assertDontSee('Zoraxy'); // generic — no product names
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue