generated from Dicken/dickendock
HTTPS im lokalen Netz (Port 8443, selbstsigniertes Zertifikat, optional mkcert)
This commit is contained in:
@@ -39,3 +39,12 @@ FRITZBOX_PASSWORD=
|
|||||||
# ─── Frontend (nur für lokale Entwicklung, `pnpm dev:frontend`) ─────
|
# ─── Frontend (nur für lokale Entwicklung, `pnpm dev:frontend`) ─────
|
||||||
# Ziel für den Vite-Dev-Proxy von /api -> Backend
|
# Ziel für den Vite-Dev-Proxy von /api -> Backend
|
||||||
BACKEND_URL=http://localhost:3001
|
BACKEND_URL=http://localhost:3001
|
||||||
|
|
||||||
|
# ─── HTTPS im lokalen Netz ──────────────────────────────────────────
|
||||||
|
# IP-Adresse oder Hostname, unter der du LaunchPad im LAN aufrufst
|
||||||
|
# (z. B. https://192.168.1.49:8443). Wird für das beim Containerstart
|
||||||
|
# automatisch erzeugte selbstsignierte Zertifikat verwendet. Ohne eigenes
|
||||||
|
# Zertifikat zeigt der Browser trotzdem eine Warnung – siehe README für eine
|
||||||
|
# Variante ohne Warnung (mkcert). Ändern erfordert einen Neustart des
|
||||||
|
# Frontend-Containers: docker compose up -d --force-recreate frontend
|
||||||
|
LAUNCHPAD_HOST=192.168.1.49
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -14,9 +14,60 @@ cp .env.example .env # optional anpassen, siehe unten
|
|||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
- Frontend: http://localhost:8080
|
- Frontend: http://localhost:8080 (HTTPS: https://localhost:8443, siehe unten)
|
||||||
- Backend / Health-API: http://localhost:3001/api/health
|
- Backend / Health-API: http://localhost:3001/api/health
|
||||||
|
|
||||||
|
## HTTPS im lokalen Netz
|
||||||
|
|
||||||
|
LaunchPad ist per HTTPS auf Port `8443` erreichbar (`https://<deine-ip>:8443`),
|
||||||
|
zusätzlich zu HTTP auf `8080` – kein erzwungener Redirect. Beim ersten Start
|
||||||
|
erzeugt der Frontend-Container automatisch ein selbstsigniertes Zertifikat für
|
||||||
|
die in `.env` hinterlegte `LAUNCHPAD_HOST` (IP oder Hostname). Der Browser
|
||||||
|
zeigt dafür trotzdem eine Sicherheitswarnung – das ist bei selbstsignierten
|
||||||
|
Zertifikaten normal, nicht per se unsicher fürs eigene LAN.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .env: IP/Hostname eintragen, unter der du zugreifst
|
||||||
|
LAUNCHPAD_HOST=192.168.1.49
|
||||||
|
|
||||||
|
docker compose up -d --force-recreate frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach: `https://192.168.1.49:8443` (Warnung einmalig bestätigen/Ausnahme
|
||||||
|
hinzufügen).
|
||||||
|
|
||||||
|
### Ohne Browser-Warnung (empfohlen): eigenes Zertifikat per mkcert
|
||||||
|
|
||||||
|
[mkcert](https://github.com/FiloSottile/mkcert) erzeugt lokal vertrauenswürdige
|
||||||
|
Zertifikate – dein Browser vertraut ihnen automatisch, keine Warnung mehr.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Einmalig auf dem Rechner, von dem aus du zugreifst (nicht auf dem Server):
|
||||||
|
mkcert -install
|
||||||
|
|
||||||
|
# Zertifikat für IP/Hostname erzeugen:
|
||||||
|
mkcert -cert-file fullchain.pem -key-file privkey.pem 192.168.1.49 launchpad.home localhost 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Die beiden erzeugten Dateien nach `/opt/LaunchPad/certs/` kopieren, dann in
|
||||||
|
`docker-compose.yml` die auskommentierten `volumes:`-Zeilen beim
|
||||||
|
`frontend`-Service aktivieren:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./certs/fullchain.pem:/etc/nginx/certs/fullchain.pem:ro
|
||||||
|
- ./certs/privkey.pem:/etc/nginx/certs/privkey.pem:ro
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --force-recreate frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
`mkcert -install` legt eine lokale Root-CA an, der nur *dieser* Rechner
|
||||||
|
vertraut. Für weitere Geräte (Handy etc.) muss die Root-CA
|
||||||
|
(`mkcert -CAROOT` zeigt den Pfad) dort zusätzlich installiert werden, oder es
|
||||||
|
bleibt bei der Browser-Warnung des automatisch generierten Zertifikats.
|
||||||
|
|
||||||
## Quickstart (lokale Entwicklung)
|
## Quickstart (lokale Entwicklung)
|
||||||
|
|
||||||
Voraussetzungen: Node.js ≥ 20, pnpm ≥ 9.
|
Voraussetzungen: Node.js ≥ 20, pnpm ≥ 9.
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ RUN pnpm --filter @launchpad/frontend build
|
|||||||
|
|
||||||
# ---- runtime: nginx --------------------------------------------------------
|
# ---- runtime: nginx --------------------------------------------------------
|
||||||
FROM nginx:1.27-alpine AS runtime
|
FROM nginx:1.27-alpine AS runtime
|
||||||
|
RUN apk add --no-cache openssl
|
||||||
COPY apps/frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY apps/frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY apps/frontend/nginx-locations.conf /etc/nginx/launchpad-locations.conf
|
||||||
|
COPY apps/frontend/docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
COPY --from=build /app/apps/frontend/dist /usr/share/nginx/html
|
COPY --from=build /app/apps/frontend/dist /usr/share/nginx/html
|
||||||
EXPOSE 80
|
EXPOSE 80 443
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|||||||
38
apps/frontend/docker-entrypoint.sh
Normal file
38
apps/frontend/docker-entrypoint.sh
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CERT_DIR="/etc/nginx/certs"
|
||||||
|
CERT_FILE="$CERT_DIR/fullchain.pem"
|
||||||
|
KEY_FILE="$CERT_DIR/privkey.pem"
|
||||||
|
|
||||||
|
# Falls der Nutzer eigene Zertifikate eingebunden hat (siehe README, z. B.
|
||||||
|
# per mkcert erzeugt), diese nicht überschreiben.
|
||||||
|
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||||
|
HOST="${LAUNCHPAD_HOST:-localhost}"
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
|
||||||
|
# IP-Adresse oder Hostname? Browser verlangen bei IP-Zugriff eine
|
||||||
|
# IP-SAN, ein DNS-Name genügt da nicht.
|
||||||
|
case "$HOST" in
|
||||||
|
*[0-9]*.*[0-9]*.*[0-9]*.*[0-9]*)
|
||||||
|
SAN="IP:$HOST,DNS:localhost,IP:127.0.0.1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
SAN="DNS:$HOST,DNS:localhost,IP:127.0.0.1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||||
|
-keyout "$KEY_FILE" \
|
||||||
|
-out "$CERT_FILE" \
|
||||||
|
-subj "/CN=$HOST" \
|
||||||
|
-addext "subjectAltName=$SAN" \
|
||||||
|
2>/dev/null
|
||||||
|
|
||||||
|
echo "[entrypoint] Selbstsigniertes HTTPS-Zertifikat für '$HOST' erzeugt."
|
||||||
|
echo "[entrypoint] Der Browser zeigt dafür eine Sicherheitswarnung - siehe README fuer eine Variante ohne Warnung (mkcert)."
|
||||||
|
else
|
||||||
|
echo "[entrypoint] Verwende eingebundenes Zertifikat unter $CERT_DIR."
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
32
apps/frontend/nginx-locations.conf
Normal file
32
apps/frontend/nginx-locations.conf
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# PWA / SPA: alle unbekannten Routen auf index.html zurückführen
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API-Anfragen an den Backend-Container weiterleiten
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:3001/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Service Worker und Manifest dürfen nicht langfristig gecacht werden,
|
||||||
|
# sonst bekommen installierte PWA-Nutzer nie ein Update mit.
|
||||||
|
location = /sw.js {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /manifest.webmanifest {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
default_type application/manifest+json;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(?:css|js|svg|png|jpg|jpeg|gif|ico|woff2?)$ {
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, max-age=604800, immutable";
|
||||||
|
}
|
||||||
@@ -1,39 +1,26 @@
|
|||||||
|
# HTTP – bleibt zusätzlich zu HTTPS verfügbar (kein erzwungener Redirect,
|
||||||
|
# damit bestehende http://-Zugriffe nicht plötzlich brechen).
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
server_name _;
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
# PWA / SPA: alle unbekannten Routen auf index.html zurückführen
|
include /etc/nginx/launchpad-locations.conf;
|
||||||
location / {
|
}
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
# HTTPS – Zertifikat wird vom Entrypoint-Skript automatisch erzeugt
|
||||||
|
# (selbstsigniert) oder ist vom Nutzer eingebunden, siehe README.
|
||||||
# API-Anfragen an den Backend-Container weiterleiten
|
server {
|
||||||
location /api/ {
|
listen 443 ssl;
|
||||||
proxy_pass http://backend:3001/api/;
|
http2 on;
|
||||||
proxy_http_version 1.1;
|
server_name _;
|
||||||
proxy_set_header Host $host;
|
root /usr/share/nginx/html;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
index index.html;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||||
}
|
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
# Service Worker und Manifest dürfen nicht langfristig gecacht werden,
|
|
||||||
# sonst bekommen installierte PWA-Nutzer nie ein Update mit.
|
include /etc/nginx/launchpad-locations.conf;
|
||||||
location = /sw.js {
|
|
||||||
add_header Cache-Control "no-cache";
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /manifest.webmanifest {
|
|
||||||
add_header Cache-Control "no-cache";
|
|
||||||
default_type application/manifest+json;
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(?:css|js|svg|png|jpg|jpeg|gif|ico|woff2?)$ {
|
|
||||||
expires 7d;
|
|
||||||
add_header Cache-Control "public, max-age=604800, immutable";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,19 @@ services:
|
|||||||
dockerfile: apps/frontend/Dockerfile
|
dockerfile: apps/frontend/Dockerfile
|
||||||
container_name: launchpad-frontend
|
container_name: launchpad-frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- path: ./.env
|
||||||
|
required: false
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:80"
|
||||||
|
- "8443:443"
|
||||||
|
# Optional: eigenes Zertifikat statt des automatisch generierten
|
||||||
|
# selbstsignierten einbinden (z. B. per mkcert erzeugt, siehe README):
|
||||||
|
# volumes:
|
||||||
|
# - ./certs/fullchain.pem:/etc/nginx/certs/fullchain.pem:ro
|
||||||
|
# - ./certs/privkey.pem:/etc/nginx/certs/privkey.pem:ro
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
launchpad-data:
|
launchpad-data:
|
||||||
|
|||||||
@@ -181,3 +181,23 @@ Geräte):
|
|||||||
(3) Umbenennen einer Kategorie im Adminbereich aktualisiert automatisch
|
(3) Umbenennen einer Kategorie im Adminbereich aktualisiert automatisch
|
||||||
alle Dienste mit dem alten Namen. Einschränkung: manuell per API angelegte
|
alle Dienste mit dem alten Namen. Einschränkung: manuell per API angelegte
|
||||||
Dienste (nicht über einen Scan) lösen diesen Sync aktuell nicht aus.
|
Dienste (nicht über einen Scan) lösen diesen Sync aktuell nicht aus.
|
||||||
|
|
||||||
|
## HTTPS im lokalen Netz
|
||||||
|
|
||||||
|
- Frontend-Container liefert jetzt zusätzlich zu HTTP (Port 8080) auch HTTPS
|
||||||
|
auf Port 8443 aus, kein erzwungener Redirect.
|
||||||
|
- Entrypoint-Skript (`apps/frontend/docker-entrypoint.sh`) erzeugt beim
|
||||||
|
Containerstart automatisch ein selbstsigniertes Zertifikat für die in
|
||||||
|
`.env` hinterlegte `LAUNCHPAD_HOST` (IP-SAN bei IP-Adressen, DNS-SAN bei
|
||||||
|
Hostnamen) – übernimmt nichts, falls unter `/etc/nginx/certs/` bereits
|
||||||
|
eigene Zertifikate eingebunden sind (siehe README, z. B. per mkcert für
|
||||||
|
eine Variante ohne Browser-Warnung).
|
||||||
|
- `nginx.conf` in HTTP- und HTTPS-Serverblock aufgeteilt, gemeinsame
|
||||||
|
Location-Regeln in `nginx-locations.conf` ausgelagert (kein doppelt
|
||||||
|
gepflegter Proxy-/Cache-Code).
|
||||||
|
|
||||||
|
> Verifiziert: Zertifikatserzeugung für IP-, Hostname- und Fallback-Fall
|
||||||
|
> getestet, komplette nginx-Konfiguration mit einem echten nginx-Binary
|
||||||
|
> validiert (`nginx -t` erfolgreich), echter TLS-Handshake gegen den
|
||||||
|
> laufenden Server durchgeführt und geprüft, dass das ausgelieferte
|
||||||
|
> Zertifikat die korrekte IP als Subject Alternative Name trägt.
|
||||||
|
|||||||
Reference in New Issue
Block a user