feat(security): keep the operator console off the public hostnames
Only the marketing site and the customer portal are public; /admin and the Proxmox hosts stay on the private network. nginx was a single server_name _ catch-all, so /admin was served on all four dev domains. Two layers: - nginx denies /admin on the known-public hostnames before PHP is reached (denylist, so an allowlist typo cannot lock the operators out). - ADMIN_HOSTS is now populated, making the app layer a strict allowlist that also covers /livewire/update, which nginx cannot attribute to /admin. Verified live against the running stack: /admin is 404 on www/app/api/ws and on any unlisted host, 302->login on admin.dev / the private IP / localhost, while / and /dashboard stay reachable everywhere. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
ba861ad579
commit
7247edf9d8
|
|
@ -164,7 +164,7 @@ MONITORING_ATTEMPTS=2
|
||||||
# antwortet /admin mit 404 (nicht 403 — verrät nicht, dass es die Konsole gibt).
|
# antwortet /admin mit 404 (nicht 403 — verrät nicht, dass es die Konsole gibt).
|
||||||
# Komma-getrennt. LEER = keine Einschränkung (aktueller Dev-Stand).
|
# Komma-getrennt. LEER = keine Einschränkung (aktueller Dev-Stand).
|
||||||
# Beispiel: ADMIN_HOSTS=admin.dev.clupilot.com,10.10.90.185
|
# Beispiel: ADMIN_HOSTS=admin.dev.clupilot.com,10.10.90.185
|
||||||
ADMIN_HOSTS=
|
ADMIN_HOSTS=admin.dev.clupilot.com,10.10.90.185,localhost,127.0.0.1
|
||||||
|
|
||||||
# ── Nextcloud blueprint template ─────────────────────────────────────────
|
# ── Nextcloud blueprint template ─────────────────────────────────────────
|
||||||
# NOT an env var: set the Proxmox template VMID per plan in
|
# NOT an env var: set the Proxmox template VMID per plan in
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
# Hostnames that are reachable from the public internet. The operator console
|
||||||
|
# (/admin) must NEVER be served on these — only the marketing site and the
|
||||||
|
# customer portal are public; Proxmox hosts and /admin stay on the private
|
||||||
|
# network / VPN.
|
||||||
|
#
|
||||||
|
# Deliberately a denylist, not an allowlist: a typo in an allowlist would lock
|
||||||
|
# the operators out of their own console. The app-level allowlist (ADMIN_HOSTS,
|
||||||
|
# see config/admin_access.php) is the strict layer — this one guarantees that a
|
||||||
|
# request arriving on a known-public hostname never even reaches PHP.
|
||||||
|
map $host $admin_host_is_public {
|
||||||
|
default 0;
|
||||||
|
www.dev.clupilot.com 1;
|
||||||
|
app.dev.clupilot.com 1;
|
||||||
|
api.dev.clupilot.com 1;
|
||||||
|
ws.dev.clupilot.com 1;
|
||||||
|
# Production (add the real public hostnames here before launch):
|
||||||
|
# www.clupilot.com 1;
|
||||||
|
# app.clupilot.com 1;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 80 default_server;
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
@ -7,6 +27,21 @@ server {
|
||||||
charset utf-8;
|
charset utf-8;
|
||||||
client_max_body_size 64M;
|
client_max_body_size 64M;
|
||||||
|
|
||||||
|
# ^~ beats the regex locations, so this decides before PHP is reached. The
|
||||||
|
# try_files rewrite to /index.php re-runs location matching, so the console
|
||||||
|
# still executes normally on a permitted host.
|
||||||
|
#
|
||||||
|
# This only covers direct hits: Livewire posts component actions to
|
||||||
|
# /livewire/update, which nginx cannot attribute to /admin. That case is
|
||||||
|
# handled in the app (RestrictAdminHost is registered as Livewire-persistent
|
||||||
|
# middleware) — do not treat this block as the only control.
|
||||||
|
location ^~ /admin {
|
||||||
|
if ($admin_host_is_public) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue