HTTPS im lokalen Netz (Port 8443, selbstsigniertes Zertifikat, optional mkcert)

This commit is contained in:
2026-07-19 16:03:29 +02:00
parent fc1ea4fa70
commit a695159b1d
8 changed files with 185 additions and 34 deletions

View File

@@ -24,7 +24,12 @@ RUN pnpm --filter @launchpad/frontend build
# ---- runtime: nginx --------------------------------------------------------
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-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
EXPOSE 80
EXPOSE 80 443
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View 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 "$@"

View 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";
}

View File

@@ -1,39 +1,26 @@
# HTTP bleibt zusätzlich zu HTTPS verfügbar (kein erzwungener Redirect,
# damit bestehende http://-Zugriffe nicht plötzlich brechen).
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# 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";
}
include /etc/nginx/launchpad-locations.conf;
}
# HTTPS Zertifikat wird vom Entrypoint-Skript automatisch erzeugt
# (selbstsigniert) oder ist vom Nutzer eingebunden, siehe README.
server {
listen 443 ssl;
http2 on;
server_name _;
root /usr/share/nginx/html;
index index.html;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
include /etc/nginx/launchpad-locations.conf;
}