From 3a530704a2c50780bc8ff8151aa3b68eb4e7ce8a Mon Sep 17 00:00:00 2001 From: francesco Date: Sun, 19 Jul 2026 19:01:29 +0200 Subject: [PATCH] Rendi configurabili le porte esposte sull'host Le porte erano legate a 127.0.0.1, quindi l'applicazione non era raggiungibile per IP: andava bene solo accedendo da un dominio via Traefik. Ora interfaccia e porta si decidono separatamente nel .env, per ogni servizio. APP_BIND=0.0.0.0 APP_PORT=8080 app esposta (default) DB_BIND=127.0.0.1 DB_PORT_HOST=3306 database solo da localhost MAILPIT_BIND=127.0.0.1 solo col profilo dev Il database resta su localhost per due motivi: ci si arriva comunque con un tunnel SSH senza esporlo a Internet, e su un host con piu' progetti la 3306 va spesso in conflitto (per questo e' cambiabile con DB_PORT_HOST). Redis non pubblica nulla: non serve da fuori. SESSION_SECURE_COOKIE passa a false come default, perche' ora l'accesso tipico e' in HTTP per IP. Con true il cookie di sessione e' marcato "secure" e il browser non lo rimanda su HTTP: verificato, il login risponde 419 Page Expired e si torna alla schermata di accesso. Il tranello e' che provandolo su localhost non si manifesta, perche' i browser considerano localhost un contesto sicuro: emerge solo accedendo per IP. E' documentato in .env.example insieme al sintomo esatto. Verificato: app raggiungibile dall'IP di rete, porta del database chiusa dall'esterno e aperta da localhost, login e pagine funzionanti via IP. Nota: senza dominio il traffico e' in chiaro. Il README segnala il passaggio a HTTPS come consigliato, con le tre variabili da cambiare. Co-Authored-By: Claude Opus 4.8 --- .env.example | 62 ++++++++++++++++++++++++++++++++++++---------- README.md | 33 ++++++++++++++++++++---- docker-compose.yml | 22 ++++++++++------ 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/.env.example b/.env.example index dbd96b3..f87731c 100644 --- a/.env.example +++ b/.env.example @@ -20,11 +20,15 @@ APP_ENV=production APP_DEBUG=false APP_TIMEZONE=Europe/Rome -# [OBBLIGATORIA] Deve combaciare ESATTAMENTE col dominio servito, schema -# incluso. L'app rifiuta richieste con un Host diverso (protezione contro -# l'host header injection): un valore sbagliato da' 400 su tutto. -# In locale: http://localhost:8080 -APP_URL=https://territori.example.org +# [OBBLIGATORIA] Deve combaciare ESATTAMENTE con l'indirizzo da cui accedi, +# schema e porta inclusi. L'app rifiuta richieste con un Host diverso +# (protezione contro l'host header injection): un valore sbagliato da' 400 +# su tutto. +# +# accesso per IP:porta -> http://192.0.2.10:8080 +# accesso in locale -> http://localhost:8080 +# dominio con HTTPS -> https://territori.example.org +APP_URL=http://192.0.2.10:8080 # [OBBLIGATORIA] Genera con: echo "base64:$(openssl rand -base64 32)" # I dati personali dei proclamatori sono cifrati con questa chiave: se la @@ -77,8 +81,18 @@ CACHE_STORE=redis SESSION_DRIVER=redis QUEUE_CONNECTION=redis -# true in produzione (HTTPS), false se accedi in HTTP su localhost. -SESSION_SECURE_COOKIE=true +# DEVE essere false se accedi in HTTP (tipicamente per IP:porta). +# +# Con true, il cookie di sessione viene marcato "secure" e il browser non lo +# rimanda mai su una connessione HTTP: il login risponde 419 "Page Expired" +# e si torna alla schermata di accesso, senza spiegazioni. +# +# Attenzione: provandolo su http://localhost NON te ne accorgi, perche' i +# browser trattano localhost come contesto sicuro. Il problema salta fuori +# solo accedendo per IP. +# +# Mettilo a true quando servirai l'app in HTTPS dietro un dominio. +SESSION_SECURE_COOKIE=false # ----------------------------------------------------------------------------- # Posta @@ -96,10 +110,32 @@ MAIL_FROM_ADDRESS=noreply@example.org MAIL_FROM_NAME="${APP_NAME}" # ----------------------------------------------------------------------------- -# Porte +# Porte esposte sull'host # ----------------------------------------------------------------------------- -# Di default l'app ascolta solo su localhost. In produzione va bene cosi': il -# traffico arriva da Traefik sulla rete interna, non da questa porta. -# Per esporla davvero (server senza reverse proxy): APP_PORT=0.0.0.0:8080 -APP_PORT=127.0.0.1:8080 -MAILPIT_UI_PORT=127.0.0.1:8025 +# Per ogni servizio decidi separatamente SU QUALE INTERFACCIA ascoltare (_BIND) +# e SU QUALE PORTA (_PORT): +# +# 0.0.0.0 raggiungibile da fuori, cioe' da Internet se il server e' +# pubblico e non c'e' un firewall davanti +# 127.0.0.1 solo dalla macchina stessa; da remoto ci arrivi con un +# tunnel SSH: ssh -L 8080:127.0.0.1:8080 utente@server + +# --- Applicazione --- +# Esposta, cosi' l'app risponde su http://IP-DEL-SERVER:8080 +# Quando passerai a un dominio con HTTPS, rimetti APP_BIND=127.0.0.1: il +# traffico arrivera' dal reverse proxy sulla rete interna e la porta non +# servira' piu'. +APP_BIND=0.0.0.0 +APP_PORT=8080 + +# --- Database --- +# Legato a localhost: per collegare un client grafico (DBeaver, TablePlus) +# apri un tunnel: ssh -L 3306:127.0.0.1:3306 utente@server +# Se la 3306 e' gia' occupata sull'host, cambia DB_PORT_HOST (es. 3307). +# Sconsigliato mettere DB_BIND=0.0.0.0: esporrebbe il database a Internet. +DB_BIND=127.0.0.1 +DB_PORT_HOST=3306 + +# --- Mailpit (solo con --profile dev) --- +MAILPIT_BIND=127.0.0.1 +MAILPIT_UI_PORT=8025 diff --git a/README.md b/README.md index 1ff5af3..c8ab211 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,13 @@ valori: APP_NAME=TerManager2 APP_ENV=production APP_DEBUG=false -APP_URL=https://territori.tuodominio.it APP_TIMEZONE=Europe/Rome +# Deve combaciare con l'indirizzo da cui accedi, porta inclusa. +# per IP:porta -> http://IP-DEL-SERVER:8080 +# con dominio -> https://territori.tuodominio.it +APP_URL=http://IP-DEL-SERVER:8080 + # --- Chiavi e password (dal Passo 2) --- APP_KEY=base64:... DB_PASSWORD=... @@ -121,8 +125,15 @@ INITIAL_ADMIN_NAME=Nome Cognome INITIAL_ADMIN_EMAIL=admin@tuodominio.it INITIAL_ADMIN_PASSWORD=UnaPasswordSolida +# --- Porte esposte sull'host --- +APP_BIND=0.0.0.0 # 127.0.0.1 se ci metti davanti un reverse proxy +APP_PORT=8080 +DB_BIND=127.0.0.1 # database raggiungibile solo via tunnel SSH +DB_PORT_HOST=3306 + # --- Sessioni e log --- -SESSION_SECURE_COOKIE=true +# false con accesso HTTP, true solo in HTTPS: vedi .env.example +SESSION_SECURE_COOKIE=false LOG_CHANNEL=stderr LOG_LEVEL=warning @@ -141,7 +152,15 @@ MAIL_FROM_ADDRESS=noreply@tuodominio.it > la protezione contro l'host header injection, ed è la causa più comune di > "pagina che non si apre" al primo deploy. -### Passo 4 — Assegna il dominio +### Passo 4 — Assegna il dominio (facoltativo) + +Con la configurazione qui sopra l'applicazione risponde gia' su +`http://IP-DEL-SERVER:8080`. Questo passo serve solo se vuoi un dominio con +HTTPS — **consigliato**, perche' altrimenti password e sessioni viaggiano in +chiaro. + +Quando lo fai, cambia anche: `APP_URL=https://tuodominio.it`, +`SESSION_SECURE_COOKIE=true` e `APP_BIND=127.0.0.1`. **Domains** → **Add Domain**: @@ -237,9 +256,9 @@ docker network create dokploy-network # una volta sola # nel .env, per l'uso locale: # APP_URL=http://localhost:8080 # APP_ENV=local -# SESSION_SECURE_COOKIE=false # MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 # e le tre password a piacere +# (SESSION_SECURE_COOKIE=false e APP_BIND/APP_PORT vanno gia' bene) docker compose --profile dev up -d --build ``` @@ -310,7 +329,11 @@ ridichiara le variabili applicative: le prende dal `.env` così com'è. | `APP_DEBUG` | `false` | **Mai `true` in produzione** | | `APP_TIMEZONE` | `Europe/Rome` | Fuso orario | | `LOG_LEVEL` | `warning` | `debug` per diagnosticare | -| `SESSION_SECURE_COOKIE` | `true` | `false` solo su HTTP locale | +| `SESSION_SECURE_COOKIE` | `false` | `true` solo servendo in HTTPS | +| `APP_BIND` | `0.0.0.0` | `127.0.0.1` dietro un reverse proxy | +| `APP_PORT` | `8080` | Porta dell'applicazione sull'host | +| `DB_BIND` | `127.0.0.1` | Interfaccia della porta MariaDB | +| `DB_PORT_HOST` | `3306` | Cambiala se la 3306 e' occupata | | `INITIAL_ADMIN_EMAIL` | — | Admin creato al primo avvio | | `INITIAL_ADMIN_PASSWORD` | — | Idem | | `INITIAL_ADMIN_NAME` | `Amministratore`| Idem | diff --git a/docker-compose.yml b/docker-compose.yml index 2b7c415..93b2901 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,11 +24,11 @@ services: # I sorgenti non cambiano dentro un'immagine gia' costruita. PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0 ports: - # Di default ascolta solo su localhost: in produzione il traffico passa - # da Traefik sulla rete interna, non da questa porta. In locale invece - # apri http://localhost:8080. - # Per esporlo davvero: APP_PORT=0.0.0.0:8080 nel .env - - "${APP_PORT:-127.0.0.1:8080}:80" + # Interfaccia e porta si decidono separatamente nel .env: + # APP_BIND=0.0.0.0 raggiungibile dall'esterno (http://IP:8080) + # APP_BIND=127.0.0.1 solo dalla macchina stessa, per quando il + # traffico arriva da un reverse proxy + - "${APP_BIND:-0.0.0.0}:${APP_PORT:-8080}:80" volumes: # PDF territori, miniature, log e chiave di fallback. - storage_data:/var/www/html/storage @@ -121,6 +121,13 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --innodb-buffer-pool-size=256M + ports: + # Legata a localhost: ci arrivi con un client grafico via tunnel SSH + # ssh -L 3306:127.0.0.1:3306 utente@server + # senza esporre il database a Internet. Per esporlo davvero (sconsigliato) + # metti DB_BIND=0.0.0.0 nel .env. + # Se la 3306 e' gia' occupata sull'host, cambia DB_PORT_HOST. + - "${DB_BIND:-127.0.0.1}:${DB_PORT_HOST:-3306}:3306" volumes: - db_data:/var/lib/mysql networks: @@ -167,7 +174,7 @@ services: profiles: ["dev"] restart: unless-stopped ports: - - "${MAILPIT_UI_PORT:-127.0.0.1:8025}:8025" + - "${MAILPIT_BIND:-127.0.0.1}:${MAILPIT_UI_PORT:-8025}:8025" networks: - internal @@ -177,7 +184,8 @@ volumes: storage_data: networks: - # Database e Redis restano qui, senza rotta verso l'esterno. + # Rete di servizio fra i container. Redis vive solo qui; MariaDB pubblica + # anche una porta sull'host, legata a localhost salvo diversa indicazione. internal: driver: bridge # Rete condivisa del Traefik di Dokploy: e' cosi' che il dominio raggiunge