Skip to content

feat(portal): configurable portal subdomain with real ACME cert, strictly internal (sub-project C) - #180

Merged
CallMeTechie merged 13 commits into
masterfrom
feature/portal-subdomain
Jun 25, 2026
Merged

CallMeTechie merged 13 commits into
masterfrom
feature/portal-subdomain

Conversation

@CallMeTechie

Copy link
Copy Markdown
Owner

Portal-Subdomain (Teilprojekt C der Domain-Initiative A→C→B)

Macht den Host des VPN-Landing-Portals konfigurierbar: Der Admin wählt eine verifizierte Basis-Domain aus der Domains-Registry (Teilprojekt A) + ein freies Subdomain-Präfix; das Portal wird unter z. B. home.domaincaster.com mit einem echten Let's-Encrypt-Zertifikat ausgeliefert (keine Browser-Warnung), bleibt aber strikt intern erreichbar (nur VPN). Ohne Konfiguration verhält es sich exakt wie bisher (home.<GC_DNS_DOMAIN>, interner Issuer) — voll rückwärtskompatibel.

Architektur

  • Single Source of Truth effectivePortalHost() (Request-Zeit, nur Settings, kein Cache) — die drei bisher hartkodierten home.<dns.domain>-Stellen (caddyConfig, dns.js, portalIdentity) lesen ihn.
  • Save-Zeit validatePortalHost(): domains.isVerified + Kollisionsprüfung gegen Management-Host, Route-Domain und Peer-FQDN.
  • TLS/ACME: öffentlicher verifizierter Host → echtes ACME (nicht forceInternalDomains); interner Default → interner Issuer.
  • Portal-vhost-Routen-Reihenfolge (make-or-break): (0) ungegatete /.well-known/acme-challenge/* → (1) remote_ip-Gate (echte TCP-Quelle) → Portal → (2) nicht-intern & nicht-ACME → 404. Die 404-Route nimmt den ACME-Pfad per not-Matcher aus → Cert-Ausstellung bleibt möglich, externe Quellen sehen nie Portal-Inhalt.
  • Adress-Wechsel = bewusste Apply-Aktion (window.confirm), kein Autosave. base_domain+prefix gemeinsam committet; partielles PUT merged das fehlende Feld aus den aktuellen Settings (kein stiller Reset).
  • Registry-Integrität: DELETE einer vom Portal genutzten Domain ist blockiert (409).

Sicherheit

Das remote_ip-Gate (echte TCP-Quelle) ist die einzige Intern-only-Kontrolle; Header-Strip/Set + portalIdentity-Dreifachprüfung (Loopback + Header + hostname === effektiver Host) bleiben unverändert. Trade-off (dokumentiert): öffentlicher Host + Cert erscheinen in Certificate-Transparency-Logs — bewusste Admin-Entscheidung.

Tests

  • 7 neue portal_*-Testdateien (TDD) + angepasste portal_dns_caddy-Struktur-Asserts: 43/43 grün lokal.
  • Keine neuen Regressionen (5 breite caddy/dns-Fehler sind vorbestehend + umgebungsbedingt, identisch an der Basis).
  • i18n en+de, DOM-sicher, Community-Tier.

Offline-Blindfleck + Post-Deploy-Pflicht

ACME-Ausstellung, echtes remote_ip-Gating und dnsmasq-Reload sind offline nicht verifizierbar; kein lokales caddy-Binary für caddy validate. Die Post-Deploy-Pflichtcheckliste (intern→Portal+Cert, extern→404, ACME-Pfad→nicht-404, Recovery) steht im Doku-Batch unter Dokumentation/gatecontrol/portal-subdomain/.

Baut auf A (PR #177, gemergt v1.95.0) auf. B (Routen nutzen Registry) folgt als eigene Spec/Plan-Runde.

🤖 Generated with Claude Code

…ains hint i18n key

- Portal host error element had inline style="display:none" that was never toggled by classList changes; now directly manage style.display in the Apply handler (clear with 'none', show with '')
- Fix 3 i18n key references from non-existent settings.general.title to settings.tab_general in aurora/default/pro settings templates
- Remove unused app variable declaration in portal_host_ui.test.js (both declare and assign were dead code)
…omain

# Conflicts:
#	src/i18n/de.json
#	src/i18n/en.json
Comment thread tests/portal_caddy_tls.test.js Fixed
Comment thread tests/portal_caddy_tls.test.js Fixed
@CallMeTechie
CallMeTechie merged commit 83ba4d1 into master Jun 25, 2026
8 checks passed
@CallMeTechie
CallMeTechie deleted the feature/portal-subdomain branch June 25, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants