From 3839d8d4385eb7ff433c99c0ca8cd403528f7971 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 08:59:58 -0600 Subject: [PATCH 01/29] docker: reduce common statements, separate concerns Rely on sensible default behavior provided by nginx's docker image. --- Dockerfile | 3 +- docker-compose.yml | 3 +- docker/dashboard.nginx | 55 ------------------- docker/nginx/templates/default.conf.template | 20 +++++++ .../snippets/dashboard.conf.template | 22 ++++++++ .../snippets}/runtime-config-template.js | 0 .../snippets/simdb_proxy.conf.template | 15 +++++ docs/installation.md | 2 +- 8 files changed, 60 insertions(+), 60 deletions(-) delete mode 100644 docker/dashboard.nginx create mode 100644 docker/nginx/templates/default.conf.template create mode 100644 docker/nginx/templates/snippets/dashboard.conf.template rename docker/{ => nginx/templates/snippets}/runtime-config-template.js (100%) create mode 100644 docker/nginx/templates/snippets/simdb_proxy.conf.template diff --git a/Dockerfile b/Dockerfile index 17138c9..fa416af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,8 +31,7 @@ LABEL org.opencontainers.image.title="SimDB Dashboard" \ org.opencontainers.image.licenses="LGPL-3.0-only" \ org.opencontainers.image.version="${APP_VERSION}" \ io.simdb.component="dashboard" -COPY docker/dashboard.nginx /etc/nginx/templates/default.conf.template -COPY docker/runtime-config-template.js /usr/share/nginx/html/runtime-config-template.js +COPY docker/nginx/templates/ /etc/nginx/templates/ # App expects itself at urlpath /dashboard COPY --from=build /app/dist /usr/share/nginx/html/dashboard # NOTE: nginx base image already exposes port 80: diff --git a/docker-compose.yml b/docker-compose.yml index e179dad..390c0ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,6 @@ services: ports: - "${DASHBOARD_PORT:-80}:80" volumes: - - ./docker/dashboard.nginx:/etc/nginx/templates/default.conf.template - - ./docker/runtime-config-template.js:/usr/share/nginx/html/runtime-config-template.js + - ./docker/nginx/templates:/etc/nginx/templates extra_hosts: - "host.docker.internal:host-gateway" diff --git a/docker/dashboard.nginx b/docker/dashboard.nginx deleted file mode 100644 index 639db72..0000000 --- a/docker/dashboard.nginx +++ /dev/null @@ -1,55 +0,0 @@ -# save this template as /etc/nginx/templates/*.template and -# envsubst will substitute ${SOMETHING} from environment at nginx startup - -# Hostnames inside upstream blocks are resolved at config-load time using the -# system resolver, which does read /etc/hosts. -upstream simdb_backend { - server ${API_HOST}:${API_PORT}; -} - -server { - listen 80; - # listen [::]:80; - server_name _; - root /usr/share/nginx/html; - absolute_redirect off; - - # dashboard app expects request url path to start with /dashboard/ - location = / { return 302 /dashboard/; } - location = /dashboard { return 302 /dashboard/; } - location /dashboard/ { - try_files $uri $uri/ /dashboard/index.html; - } - - # proxy to simdb backend - # detect accepted prefix: (?:scenarios/api|scenarios|api|swaggerui) - location ~ "^/(?:scenarios/api|scenarios|api|swaggerui).*$" { - - # strip prefixes except swaggerui - rewrite ^/(?:scenarios/api|scenarios|api)?/?(.*) /$1 break; - - # typical proxy params - proxy_set_header Host $http_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; - - proxy_pass http://simdb_backend; - } - - # dashboard is a simdb client; - # runtime-config.js defines the simdb remote url to use. - location = /dashboard/runtime-config.js { - default_type application/javascript; - add_header Cache-Control "no-cache, no-store, must-revalidate"; - try_files /runtime-config-template.js =404; - sub_filter_once off; - sub_filter_types application/javascript; - # Optional tokens to replace in template: - sub_filter 'SCHEME' '$scheme'; - sub_filter 'SIMDB_SERVER_URL' '${SIMDB_SERVER_URL}'; - sub_filter 'DASHBOARD_PORT' '${DASHBOARD_PORT}'; - sub_filter 'API_HOST' '${API_HOST}'; - sub_filter 'API_PORT' '${API_PORT}'; - } -} diff --git a/docker/nginx/templates/default.conf.template b/docker/nginx/templates/default.conf.template new file mode 100644 index 0000000..8b88415 --- /dev/null +++ b/docker/nginx/templates/default.conf.template @@ -0,0 +1,20 @@ +# Relies on official nginx image default behavior: /etc/nginx/templates/*.template +# are envsubst-processed into /etc/nginx/conf.d at container startup. +# See: https://github.com/docker-library/docs/tree/master/nginx + +# Hostnames inside upstream blocks are resolved at config-load time using the +# system resolver, which does read /etc/hosts. +upstream simdb_backend { + server ${API_HOST}:${API_PORT}; +} + +server { + listen 80; + # listen [::]:80; + server_name _; + root /usr/share/nginx/html; + absolute_redirect off; + + include /etc/nginx/conf.d/snippets/dashboard.conf; + include /etc/nginx/conf.d/snippets/simdb_proxy.conf; +} diff --git a/docker/nginx/templates/snippets/dashboard.conf.template b/docker/nginx/templates/snippets/dashboard.conf.template new file mode 100644 index 0000000..446413c --- /dev/null +++ b/docker/nginx/templates/snippets/dashboard.conf.template @@ -0,0 +1,22 @@ +# dashboard app expects request url path to start with /dashboard/ +location = / { return 302 /dashboard/; } +location = /dashboard { return 302 /dashboard/; } +location /dashboard/ { + try_files $uri $uri/ /dashboard/index.html; +} + +# dashboard is a simdb client; +# runtime-config.js defines the simdb remote url to use. +location = /dashboard/runtime-config.js { + default_type application/javascript; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + alias /etc/nginx/templates/snippets/runtime-config-template.js; + sub_filter_once off; + sub_filter_types application/javascript; + # Optional tokens to replace in template: + sub_filter 'SCHEME' '$scheme'; + sub_filter 'SIMDB_SERVER_URL' '${SIMDB_SERVER_URL}'; + sub_filter 'DASHBOARD_PORT' '${DASHBOARD_PORT}'; + sub_filter 'API_HOST' '${API_HOST}'; + sub_filter 'API_PORT' '${API_PORT}'; +} diff --git a/docker/runtime-config-template.js b/docker/nginx/templates/snippets/runtime-config-template.js similarity index 100% rename from docker/runtime-config-template.js rename to docker/nginx/templates/snippets/runtime-config-template.js diff --git a/docker/nginx/templates/snippets/simdb_proxy.conf.template b/docker/nginx/templates/snippets/simdb_proxy.conf.template new file mode 100644 index 0000000..794399f --- /dev/null +++ b/docker/nginx/templates/snippets/simdb_proxy.conf.template @@ -0,0 +1,15 @@ +# proxy to simdb backend +# detect accepted prefix: (?:scenarios/api|scenarios|api|swaggerui) +location ~ "^/(?:scenarios/api|scenarios|api|swaggerui).*$" { + + # strip prefixes except swaggerui + rewrite ^/(?:scenarios/api|scenarios|api)?/?(.*) /$1 break; + + # typical proxy params + proxy_set_header Host $http_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; + + proxy_pass http://simdb_backend; +} diff --git a/docs/installation.md b/docs/installation.md index a5b9646..15984c0 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -66,7 +66,7 @@ Notes: - Requests under `/scenarios/api/` are proxied by nginx to a simdb server expected at `API_HOST:API_PORT` (defaults to `host.docker.internal:5000`). - You can start multiple dashboards if you change the host port with `DASHBOARD_PORT`. -- Use `PUBLIC_SIMDB_URL` or edit `docker\runtime-config-template.js` for adjusting the simdb server: +- Use `PUBLIC_SIMDB_URL` or edit `docker\nginx\templates\snippets\runtime-config-template.js` for adjusting the simdb server: ```sh DASHBOARD_PORT=8080 make up From b210f031cd21d4f8ff168a12b1176a6ee94646b8 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 09:09:41 -0600 Subject: [PATCH 02/29] docker: separate server-http config --- docker/nginx/templates/default.conf.template | 12 ++---------- .../templates/snippets/server-http.conf.template | 10 ++++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 docker/nginx/templates/snippets/server-http.conf.template diff --git a/docker/nginx/templates/default.conf.template b/docker/nginx/templates/default.conf.template index 8b88415..e028e48 100644 --- a/docker/nginx/templates/default.conf.template +++ b/docker/nginx/templates/default.conf.template @@ -8,13 +8,5 @@ upstream simdb_backend { server ${API_HOST}:${API_PORT}; } -server { - listen 80; - # listen [::]:80; - server_name _; - root /usr/share/nginx/html; - absolute_redirect off; - - include /etc/nginx/conf.d/snippets/dashboard.conf; - include /etc/nginx/conf.d/snippets/simdb_proxy.conf; -} +# HTTP server block +include /etc/nginx/conf.d/snippets/server-http.conf; diff --git a/docker/nginx/templates/snippets/server-http.conf.template b/docker/nginx/templates/snippets/server-http.conf.template new file mode 100644 index 0000000..57abb86 --- /dev/null +++ b/docker/nginx/templates/snippets/server-http.conf.template @@ -0,0 +1,10 @@ +server { + listen 80; + # listen [::]:80; + server_name _; + root /usr/share/nginx/html; + absolute_redirect off; + + include /etc/nginx/conf.d/snippets/dashboard.conf; + include /etc/nginx/conf.d/snippets/simdb_proxy.conf; +} From 50ca09b713d33afa86f6fe02cb5e91854f89d3ac Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 10:25:14 -0600 Subject: [PATCH 03/29] docker: rename location snippets --- ...shboard.conf.template => location-dashboard.conf.template} | 0 ...proxy.conf.template => location-simdb_proxy.conf.template} | 0 docker/nginx/templates/snippets/server-http.conf.template | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename docker/nginx/templates/snippets/{dashboard.conf.template => location-dashboard.conf.template} (100%) rename docker/nginx/templates/snippets/{simdb_proxy.conf.template => location-simdb_proxy.conf.template} (100%) diff --git a/docker/nginx/templates/snippets/dashboard.conf.template b/docker/nginx/templates/snippets/location-dashboard.conf.template similarity index 100% rename from docker/nginx/templates/snippets/dashboard.conf.template rename to docker/nginx/templates/snippets/location-dashboard.conf.template diff --git a/docker/nginx/templates/snippets/simdb_proxy.conf.template b/docker/nginx/templates/snippets/location-simdb_proxy.conf.template similarity index 100% rename from docker/nginx/templates/snippets/simdb_proxy.conf.template rename to docker/nginx/templates/snippets/location-simdb_proxy.conf.template diff --git a/docker/nginx/templates/snippets/server-http.conf.template b/docker/nginx/templates/snippets/server-http.conf.template index 57abb86..9dbdb5f 100644 --- a/docker/nginx/templates/snippets/server-http.conf.template +++ b/docker/nginx/templates/snippets/server-http.conf.template @@ -5,6 +5,6 @@ server { root /usr/share/nginx/html; absolute_redirect off; - include /etc/nginx/conf.d/snippets/dashboard.conf; - include /etc/nginx/conf.d/snippets/simdb_proxy.conf; + include /etc/nginx/conf.d/snippets/location-dashboard.conf; + include /etc/nginx/conf.d/snippets/location-simdb_proxy.conf; } From 9569da32024721b5019c2c0dee09b7950431d5b7 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 11:18:32 -0600 Subject: [PATCH 04/29] docker: add server-https config --- Dockerfile | 9 ++++++++ docker-compose-https.yml | 16 +++++++++++++ docker-compose.yml | 3 ++- .../nginx/entrypoint/25-select-server-conf.sh | 4 ++++ docker/nginx/templates/default.conf.template | 5 ++-- .../snippets/server-https.conf.template | 23 +++++++++++++++++++ 6 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 docker-compose-https.yml create mode 100644 docker/nginx/entrypoint/25-select-server-conf.sh create mode 100644 docker/nginx/templates/snippets/server-https.conf.template diff --git a/Dockerfile b/Dockerfile index fa416af..e86b733 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ ARG APP_VERSION=0.0.0-unknown ENV SIMDB_SERVER_URL=/scenarios/api \ API_HOST=host.docker.internal \ API_PORT=5000 \ + SERVER_CONF=server-http.conf \ DASHBOARD_PORT=80 LABEL org.opencontainers.image.title="SimDB Dashboard" \ org.opencontainers.image.description="Web frontend for the SimDB simulation management tool" \ @@ -32,8 +33,16 @@ LABEL org.opencontainers.image.title="SimDB Dashboard" \ org.opencontainers.image.version="${APP_VERSION}" \ io.simdb.component="dashboard" COPY docker/nginx/templates/ /etc/nginx/templates/ +COPY docker/nginx/entrypoint/ /docker-entrypoint.d/ +RUN chmod +x /docker-entrypoint.d/*.sh # App expects itself at urlpath /dashboard COPY --from=build /app/dist /usr/share/nginx/html/dashboard # NOTE: nginx base image already exposes port 80: EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] + +# HTTPS service stage: reuse the HTTP service image and expose TLS port 443. +FROM service AS service-https +ENV SERVER_CONF=server-https.conf \ + DASHBOARD_HTTPS_PORT=443 +EXPOSE 443 diff --git a/docker-compose-https.yml b/docker-compose-https.yml new file mode 100644 index 0000000..a4c258b --- /dev/null +++ b/docker-compose-https.yml @@ -0,0 +1,16 @@ +# HTTPS override for docker-compose.yml. +# Reuses the base dashboard service definition and only adds or overrides SSL-specific settings. +services: + dashboard: + image: simdb-dashboard:service-https + build: + target: service-https + environment: + # Use server-https.conf when SSL certificates (cert.crt and cert.key) are provided. + SERVER_CONF: ${SERVER_CONF:-server-https.conf} + ports: + - "${DASHBOARD_PORT:-80}:80" + - "${DASHBOARD_HTTPS_PORT:-443}:443" + volumes: + - ./docker/nginx/templates:/etc/nginx/templates + - ./docker/nginx/ssl:/etc/nginx/ssl:ro \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 390c0ff..8ecb176 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,9 @@ services: image: simdb-dashboard:service labels: io.simdb.component: "dashboard" - build: + build: context: . + target: service args: APP_VERSION: ${APP_VERSION:-0.0.0-unknown} environment: diff --git a/docker/nginx/entrypoint/25-select-server-conf.sh b/docker/nginx/entrypoint/25-select-server-conf.sh new file mode 100644 index 0000000..b296379 --- /dev/null +++ b/docker/nginx/entrypoint/25-select-server-conf.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +set -eu + +cp "/etc/nginx/conf.d/snippets/${SERVER_CONF:-server-http.conf}" /etc/nginx/conf.d/snippets/server.conf diff --git a/docker/nginx/templates/default.conf.template b/docker/nginx/templates/default.conf.template index e028e48..a1e769f 100644 --- a/docker/nginx/templates/default.conf.template +++ b/docker/nginx/templates/default.conf.template @@ -8,5 +8,6 @@ upstream simdb_backend { server ${API_HOST}:${API_PORT}; } -# HTTP server block -include /etc/nginx/conf.d/snippets/server-http.conf; +# Server block include is selected at startup by /docker-entrypoint.d/25-select-server-conf.sh +# using SERVER_CONF (default: server-http.conf). +include /etc/nginx/conf.d/snippets/server.conf; diff --git a/docker/nginx/templates/snippets/server-https.conf.template b/docker/nginx/templates/snippets/server-https.conf.template new file mode 100644 index 0000000..896c6ba --- /dev/null +++ b/docker/nginx/templates/snippets/server-https.conf.template @@ -0,0 +1,23 @@ +server { + listen 443 ssl http2; + # listen [::]:443 ssl http2; + server_name _; + root /usr/share/nginx/html; + absolute_redirect off; + + ssl_certificate /etc/nginx/ssl/cert.crt; + ssl_certificate_key /etc/nginx/ssl/cert.key; + + # Modern TLS baseline for broad compatibility and strong security. + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # Session settings aligned with current hardening guidance. + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; + + include /etc/nginx/conf.d/snippets/location-dashboard.conf; + include /etc/nginx/conf.d/snippets/location-simdb_proxy.conf; +} From 5b8600923b3ce3b1eebcc918c0349b49fe97c8ae Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 19:24:33 -0600 Subject: [PATCH 05/29] docker: add http redirect to https --- docker/nginx/templates/snippets/server-https.conf.template | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docker/nginx/templates/snippets/server-https.conf.template b/docker/nginx/templates/snippets/server-https.conf.template index 896c6ba..1904fdc 100644 --- a/docker/nginx/templates/snippets/server-https.conf.template +++ b/docker/nginx/templates/snippets/server-https.conf.template @@ -1,3 +1,10 @@ +server { + listen 80; + # listen [::]:80; + server_name _; + return 301 https://$host$request_uri; +} + server { listen 443 ssl http2; # listen [::]:443 ssl http2; From 1286eb4e4c803cd086cc138885ddbceec802dffc Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 11:41:18 -0600 Subject: [PATCH 06/29] make: add USE_HTTPS=1 switch, update docs --- Makefile | 31 ++++++++++++++++++++++++++----- docs/installation.md | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 2e7ad7b..e3c8dc3 100644 --- a/Makefile +++ b/Makefile @@ -4,16 +4,27 @@ VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo 0.0.0-unknow DASHBOARD_PORT ?= 80 COMPOSE_PROJECT_NAME ?= simdb-dashboard-$(DASHBOARD_PORT) +ifeq ($(USE_HTTPS),1) +DASHBOARD_HTTPS_PORT ?= 443 +export DASHBOARD_HTTPS_PORT +COMPOSE_FILE ?= docker-compose.yml:docker-compose-https.yml +SERVICE_IMAGE := simdb-dashboard:service-https +SERVICE_TARGET := service-https +else +COMPOSE_FILE ?= docker-compose.yml +SERVICE_IMAGE := simdb-dashboard:service +SERVICE_TARGET := service +endif + export DASHBOARD_PORT export COMPOSE_PROJECT_NAME DOCKER_CMD ?= docker DOCKER_BUILD ?= $(DOCKER_CMD) build --build-arg APP_VERSION="$(VERSION)" -DOCKER_COMPOSE ?= APP_VERSION="$(VERSION)" $(DOCKER_CMD) compose +DOCKER_COMPOSE ?= APP_VERSION="$(VERSION)" COMPOSE_FILE="$(COMPOSE_FILE)" $(DOCKER_CMD) compose BUILD_IMAGE := simdb-dashboard:build DEV_IMAGE := simdb-dashboard:dev -SERVICE_IMAGE := simdb-dashboard:service .DEFAULT_GOAL := service @@ -52,6 +63,13 @@ help: @echo " make logs-f Follow logs of the started simdb-dashboard service" @echo " make shell Enter shell in the started simdb-dashboard service" @echo "" + @echo "HTTPS toggle (set USE_HTTPS=1):" + @echo " USE_HTTPS=1 make service Build HTTPS service stage and tag simdb-dashboard:service-https" + @echo " USE_HTTPS=1 make up Start dashboard with docker-compose-https.yml override" + @echo " USE_HTTPS=1 make down Stop dashboard started with the HTTPS compose override" + @echo " USE_HTTPS=1 make logs-f Follow logs of the HTTPS compose service" + @echo " USE_HTTPS=1 make shell Enter shell in the started HTTPS compose service" + @echo "" @echo "Dockerfile stage targets:" @echo " make builder Build builder stage (dependency setup + source prep)" @echo " make build Build application build stage and tag $(BUILD_IMAGE)" @@ -74,6 +92,8 @@ help: @echo "Environment variable examples:" @echo " Start simdb-dashboard at alternative DASHBOARD_PORT, with simdb server at API_PORT:" @echo " DASHBOARD_PORT=8080 API_PORT=5100 make up" + @echo " Start HTTPS dashboard with alternative HTTP/HTTPS host ports:" + @echo " DASHBOARD_PORT=8080 DASHBOARD_HTTPS_PORT=8443 USE_HTTPS=1 make up" # Compose targets up: @@ -105,7 +125,7 @@ build: $(DOCKER_BUILD) --target build -t $(BUILD_IMAGE) . service: - $(DOCKER_BUILD) --target service -t $(SERVICE_IMAGE) . + $(DOCKER_BUILD) --target $(SERVICE_TARGET) -t $(SERVICE_IMAGE) . # Developer utilities lint: @@ -153,8 +173,9 @@ dashboard/package-lock.json: dashboard/package.json "npm install --package-lock-only && npm audit fix && npm list" distclean: - $(DOCKER_COMPOSE) down --volumes --remove-orphans --rmi local - $(DOCKER_CMD) rmi -f $(BUILD_IMAGE) $(SERVICE_IMAGE) >/dev/null 2>&1 || true + APP_VERSION="$(VERSION)" COMPOSE_FILE="docker-compose.yml" $(DOCKER_CMD) compose down --volumes --remove-orphans --rmi local + APP_VERSION="$(VERSION)" COMPOSE_FILE="docker-compose.yml:docker-compose-https.yml" $(DOCKER_CMD) compose down --volumes --remove-orphans --rmi local + $(DOCKER_CMD) rmi -f $(BUILD_IMAGE) simdb-dashboard:service simdb-dashboard:service-https >/dev/null 2>&1 || true $(DOCKER_CMD) volume rm -f simdb_dashboard_node_modules >/dev/null 2>&1 || true rm -rf dist diff --git a/docs/installation.md b/docs/installation.md index 15984c0..1c18ca5 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -48,9 +48,38 @@ make up make down ``` +## HTTPS installation workflow (Docker image + Compose override) + +The repository also includes an HTTPS-specific Docker stage and a Compose override in `docker-compose-https.yml`. + +1. Provide TLS certificate files at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. + +2. Build the HTTPS service image: + +```sh +USE_HTTPS=1 make service +``` + +3. Start the dashboard with the HTTPS override: + +```sh +USE_HTTPS=1 make up +``` + +4. Open the dashboard: + +- HTTP: http://localhost:80/dashboard/ (redirects to https) +- HTTPS: https://localhost:443/dashboard/ + +5. Stop the HTTPS service when needed: + +```sh +USE_HTTPS=1 make down +``` + ## Adjusting the installation -Did something change in the `docker-compose.yml` or `docker/*` files? Stop the service, and restart service (rebuild not necessary): +Did something change in the `docker-compose.yml`, `docker-compose-https.yml`, or `docker/*` files? Stop the service, and restart service (rebuild not necessary): ```sh make down up @@ -66,12 +95,16 @@ Notes: - Requests under `/scenarios/api/` are proxied by nginx to a simdb server expected at `API_HOST:API_PORT` (defaults to `host.docker.internal:5000`). - You can start multiple dashboards if you change the host port with `DASHBOARD_PORT`. +- The HTTPS compose override also publishes `DASHBOARD_HTTPS_PORT` (default `443`) and switches `SERVER_CONF` to `server-https.conf`. +- `docker-compose-https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`. +- Set `USE_HTTPS=1` to switch shared Make targets such as `up`, `down`, `logs-f`, and `shell` to the HTTPS compose chain. - Use `PUBLIC_SIMDB_URL` or edit `docker\nginx\templates\snippets\runtime-config-template.js` for adjusting the simdb server: ```sh DASHBOARD_PORT=8080 make up DASHBOARD_PORT=8081 API_PORT=5001 make up DASHBOARD_PORT=8082 API_HOST=172.20.0.1 API_PORT=5001 make up +DASHBOARD_PORT=8080 DASHBOARD_HTTPS_PORT=8443 USE_HTTPS=1 make up PUBLIC_SIMDB_URL=https://simdb.iter.org/scenarios/api make up ``` @@ -82,6 +115,8 @@ make list-all # list all running dashboard containers DASHBOARD_PORT=8081 make list # list container for the selected container DASHBOARD_PORT=8081 make logs-f # follow compose logs for the selected container DASHBOARD_PORT=8081 make shell # open sh inside running dashboard container +DASHBOARD_PORT=8081 USE_HTTPS=1 make logs-f # follow HTTPS compose logs +DASHBOARD_PORT=8081 USE_HTTPS=1 make shell # open sh inside HTTPS container ``` ## Static artifact installation (non-Compose nginx deployments) From 0f79db538bea346a5331f7679d0b19dad7b46a36 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 18:32:58 -0600 Subject: [PATCH 07/29] docker: add nginx/ssl cert.* generation --- .dockerignore | 8 +++--- .gitignore | 6 +++-- Dockerfile | 1 + docker/nginx/ssl/.gitkeep | 1 + scripts/generate-self-signed-certs.sh | 37 +++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 docker/nginx/ssl/.gitkeep create mode 100755 scripts/generate-self-signed-certs.sh diff --git a/.dockerignore b/.dockerignore index 9081a5d..af4da6fe 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,9 @@ .git .github +dashboard/dist +dashboard/node_modules +docker/nginx/ssl/*.crt +docker/nginx/ssl/*.key docs -scripts node_modules -dashboard/node_modules -dashboard/dist +scripts diff --git a/.gitignore b/.gitignore index 5016433..86d755a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ -dist/ +.idea/ dashboard/dist/ dashboard/node_modules/ -.idea/ +docker/nginx/ssl/*.crt +docker/nginx/ssl/*.key +dist/ diff --git a/Dockerfile b/Dockerfile index e86b733..5465790 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,4 +45,5 @@ CMD ["nginx", "-g", "daemon off;"] FROM service AS service-https ENV SERVER_CONF=server-https.conf \ DASHBOARD_HTTPS_PORT=443 +COPY docker/nginx/ssl/ /etc/nginx/ssl/ EXPOSE 443 diff --git a/docker/nginx/ssl/.gitkeep b/docker/nginx/ssl/.gitkeep new file mode 100644 index 0000000..58dc66b --- /dev/null +++ b/docker/nginx/ssl/.gitkeep @@ -0,0 +1 @@ +# This directory expects cert.crt and cert.key files. diff --git a/scripts/generate-self-signed-certs.sh b/scripts/generate-self-signed-certs.sh new file mode 100755 index 0000000..cbb61fa --- /dev/null +++ b/scripts/generate-self-signed-certs.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) +SSL_DIR="$REPO_ROOT/docker/nginx/ssl" +CERT_FILE="$SSL_DIR/cert.crt" +KEY_FILE="$SSL_DIR/cert.key" + +DAYS="${CERT_DAYS:-3650}" + +DOMAIN_NAME="${DOMAIN_NAME:-$(hostname -f 2>/dev/null || hostname --fqdn 2>/dev/null || hostname 2>/dev/null || echo localhost)}" +CERT_CN=${CERT_CN:-$DOMAIN_NAME} + +if ! command -v openssl >/dev/null 2>&1; then + echo "Error: openssl is required but was not found in PATH." >&2 + exit 1 +fi + +mkdir -p "$SSL_DIR" + +openssl req \ + -x509 \ + -nodes \ + -newkey rsa:2048 \ + -sha256 \ + -days "$DAYS" \ + -keyout "$KEY_FILE" \ + -out "$CERT_FILE" \ + -subj "/CN=$CERT_CN" + +chmod 600 "$KEY_FILE" +chmod 644 "$CERT_FILE" + +echo "Created self-signed certificate files:" +echo "- $CERT_FILE" +echo "- $KEY_FILE" From a5029c13201cf0d898ef28151a3a0ea83b4b3379 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Sun, 5 Jul 2026 21:18:46 -0600 Subject: [PATCH 08/29] docker: merge stage service-https into service remove DASHBOARD_PORT from container environment --- Dockerfile | 13 ++++--------- Makefile | 4 +--- docker-compose-https.yml | 4 +--- docker-compose.yml | 1 - .../snippets/location-dashboard.conf.template | 2 -- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5465790..a0ff879 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,8 +24,7 @@ ARG APP_VERSION=0.0.0-unknown ENV SIMDB_SERVER_URL=/scenarios/api \ API_HOST=host.docker.internal \ API_PORT=5000 \ - SERVER_CONF=server-http.conf \ - DASHBOARD_PORT=80 + SERVER_CONF=server-http.conf LABEL org.opencontainers.image.title="SimDB Dashboard" \ org.opencontainers.image.description="Web frontend for the SimDB simulation management tool" \ org.opencontainers.image.source="https://github.com/iterorganization/SimDB-Dashboard" \ @@ -34,16 +33,12 @@ LABEL org.opencontainers.image.title="SimDB Dashboard" \ io.simdb.component="dashboard" COPY docker/nginx/templates/ /etc/nginx/templates/ COPY docker/nginx/entrypoint/ /docker-entrypoint.d/ +COPY docker/nginx/ssl/ /etc/nginx/ssl/ RUN chmod +x /docker-entrypoint.d/*.sh # App expects itself at urlpath /dashboard COPY --from=build /app/dist /usr/share/nginx/html/dashboard # NOTE: nginx base image already exposes port 80: EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] - -# HTTPS service stage: reuse the HTTP service image and expose TLS port 443. -FROM service AS service-https -ENV SERVER_CONF=server-https.conf \ - DASHBOARD_HTTPS_PORT=443 -COPY docker/nginx/ssl/ /etc/nginx/ssl/ +# HTTPS service at port 443 (only utilized when SERVER_CONF=server-https.conf) EXPOSE 443 +CMD ["nginx", "-g", "daemon off;"] diff --git a/Makefile b/Makefile index e3c8dc3..0dafb85 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,9 @@ DASHBOARD_HTTPS_PORT ?= 443 export DASHBOARD_HTTPS_PORT COMPOSE_FILE ?= docker-compose.yml:docker-compose-https.yml SERVICE_IMAGE := simdb-dashboard:service-https -SERVICE_TARGET := service-https else COMPOSE_FILE ?= docker-compose.yml SERVICE_IMAGE := simdb-dashboard:service -SERVICE_TARGET := service endif export DASHBOARD_PORT @@ -125,7 +123,7 @@ build: $(DOCKER_BUILD) --target build -t $(BUILD_IMAGE) . service: - $(DOCKER_BUILD) --target $(SERVICE_TARGET) -t $(SERVICE_IMAGE) . + $(DOCKER_BUILD) --target service -t $(SERVICE_IMAGE) . # Developer utilities lint: diff --git a/docker-compose-https.yml b/docker-compose-https.yml index a4c258b..ba1d632 100644 --- a/docker-compose-https.yml +++ b/docker-compose-https.yml @@ -3,8 +3,6 @@ services: dashboard: image: simdb-dashboard:service-https - build: - target: service-https environment: # Use server-https.conf when SSL certificates (cert.crt and cert.key) are provided. SERVER_CONF: ${SERVER_CONF:-server-https.conf} @@ -13,4 +11,4 @@ services: - "${DASHBOARD_HTTPS_PORT:-443}:443" volumes: - ./docker/nginx/templates:/etc/nginx/templates - - ./docker/nginx/ssl:/etc/nginx/ssl:ro \ No newline at end of file + - ./docker/nginx/ssl:/etc/nginx/ssl:ro diff --git a/docker-compose.yml b/docker-compose.yml index 8ecb176..a2f26cd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,6 @@ services: io.simdb.component: "dashboard" build: context: . - target: service args: APP_VERSION: ${APP_VERSION:-0.0.0-unknown} environment: diff --git a/docker/nginx/templates/snippets/location-dashboard.conf.template b/docker/nginx/templates/snippets/location-dashboard.conf.template index 446413c..b2d05c4 100644 --- a/docker/nginx/templates/snippets/location-dashboard.conf.template +++ b/docker/nginx/templates/snippets/location-dashboard.conf.template @@ -14,9 +14,7 @@ location = /dashboard/runtime-config.js { sub_filter_once off; sub_filter_types application/javascript; # Optional tokens to replace in template: - sub_filter 'SCHEME' '$scheme'; sub_filter 'SIMDB_SERVER_URL' '${SIMDB_SERVER_URL}'; - sub_filter 'DASHBOARD_PORT' '${DASHBOARD_PORT}'; sub_filter 'API_HOST' '${API_HOST}'; sub_filter 'API_PORT' '${API_PORT}'; } From 7991af17139862b0da4eea6a0b7655d8837fd897 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Wed, 29 Jul 2026 01:19:17 +0200 Subject: [PATCH 09/29] docs: mention scripts/generate-self-signed-certs.sh --- docs/installation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/installation.md b/docs/installation.md index 1c18ca5..f85e606 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -98,6 +98,7 @@ Notes: - The HTTPS compose override also publishes `DASHBOARD_HTTPS_PORT` (default `443`) and switches `SERVER_CONF` to `server-https.conf`. - `docker-compose-https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`. - Set `USE_HTTPS=1` to switch shared Make targets such as `up`, `down`, `logs-f`, and `shell` to the HTTPS compose chain. +- The `USE_HTTPS=1` flag expects **TLS certificate files** at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. If you do not have certificate files yet, a self-signed key pair can be generated with the provided script at `scripts/generate-self-signed-certs.sh` (see the [HTTPS installation workflow](#https-installation-workflow-docker-image--compose-override) section for details). - Use `PUBLIC_SIMDB_URL` or edit `docker\nginx\templates\snippets\runtime-config-template.js` for adjusting the simdb server: ```sh From 92dc37b8ecbcad8fe03cba1153ee1d8c59650509 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Wed, 29 Jul 2026 03:14:50 +0200 Subject: [PATCH 10/29] systemd: service using compose override and make install targets. --- Makefile | 58 +++++++++++++++ docker-compose.systemd.yml | 14 ++++ scripts/dashboard.service | 23 ------ scripts/dashboard.socket | 14 ---- scripts/simdb-dashboard.service | 120 ++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 37 deletions(-) create mode 100644 docker-compose.systemd.yml delete mode 100644 scripts/dashboard.service delete mode 100644 scripts/dashboard.socket create mode 100644 scripts/simdb-dashboard.service diff --git a/Makefile b/Makefile index 0dafb85..04c47c6 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,12 @@ DEV_IMAGE := simdb-dashboard:dev logs-f \ service \ shell \ + systemd-disable \ + systemd-enable \ + systemd-install \ + systemd-start \ + systemd-stop \ + systemd-uninstall \ test \ type-check \ up \ @@ -87,6 +93,15 @@ help: @echo " make distclean Remove local artifacts and compose runtime state" @echo " make deploy Deploy project (placeholder)" @echo "" + @echo "Systemd integration (run with sudo):" + @echo " sudo make systemd-install Copy files to /opt/simdb-dashboard and /etc/simdb-dashboard" + @echo " sudo make systemd-enable systemctl daemon-reload, enable, and start the service" + @echo " sudo make systemd-start systemctl start simdb-dashboard" + @echo " sudo make systemd-stop systemctl stop simdb-dashboard" + @echo " sudo make systemd-disable systemctl stop && systemctl disable simdb-dashboard" + @echo " sudo make systemd-uninstall Disable, remove unit file, delete /opt/ and /etc/ files" + @echo " USE_HTTPS=1 sudo make systemd-install Include HTTPS compose override and SSL setup" + @echo "" @echo "Environment variable examples:" @echo " Start simdb-dashboard at alternative DASHBOARD_PORT, with simdb server at API_PORT:" @echo " DASHBOARD_PORT=8080 API_PORT=5100 make up" @@ -177,6 +192,49 @@ distclean: $(DOCKER_CMD) volume rm -f simdb_dashboard_node_modules >/dev/null 2>&1 || true rm -rf dist +# Systemd integration (run with sudo) +systemd-install: + mkdir -p /opt/simdb-dashboard + cp docker-compose.yml /opt/simdb-dashboard/ + cp docker-compose.systemd.yml /opt/simdb-dashboard/ + cp -r docker/nginx/templates /opt/simdb-dashboard/ + mkdir -p /etc/simdb-dashboard + [ -f /etc/simdb-dashboard/simdb-dashboard.env ] || \ + { echo '# Created by make systemd-install' > /etc/simdb-dashboard/simdb-dashboard.env; \ + echo 'API_HOST=host.docker.internal' >> /etc/simdb-dashboard/simdb-dashboard.env; \ + echo 'SIMDB_SERVER_URL=/scenarios/api' >> /etc/simdb-dashboard/simdb-dashboard.env; } +ifeq ($(USE_HTTPS),1) + cp docker-compose-https.yml /opt/simdb-dashboard/ + mkdir -p /etc/ssl/simdb-dashboard + cp docker/nginx/ssl/cert.* /etc/ssl/simdb-dashboard/ 2>/dev/null || \ + echo " *** No certificate files found at docker/nginx/ssl/ — generate them with:"; \ + echo " *** scripts/generate-self-signed-certs.sh"; \ + echo " *** cp docker/nginx/ssl/cert.* /etc/ssl/simdb-dashboard/" + mkdir -p /opt/simdb-dashboard/docker/nginx + ln -sf /etc/ssl/simdb-dashboard /opt/simdb-dashboard/docker/nginx/ssl +endif + +systemd-enable: + systemctl daemon-reload + systemctl enable simdb-dashboard + systemctl start simdb-dashboard + +systemd-start: + systemctl start simdb-dashboard + +systemd-stop: + systemctl stop simdb-dashboard + +systemd-disable: systemd-stop + systemctl disable simdb-dashboard + +systemd-uninstall: systemd-disable + rm -f /etc/systemd/system/simdb-dashboard.service + systemctl daemon-reload + rm -rf /opt/simdb-dashboard + rm -rf /etc/simdb-dashboard + [ ! -e /etc/ssl/simdb-dashboard ] || rm -rf /etc/ssl/simdb-dashboard + # Deployment deploy: @echo "TODO define deploy workflow here" diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml new file mode 100644 index 0000000..6ff4dba --- /dev/null +++ b/docker-compose.systemd.yml @@ -0,0 +1,14 @@ +# systemd deployment override — use the GHCR-published image instead of a local build. +# +# Include this file last in COMPOSE_FILE so its image: value wins over +# both docker-compose.yml (simdb-dashboard:service) and +# docker-compose-https.yml (simdb-dashboard:service-https). +# +# COMPOSE_FILE="docker-compose.yml:docker-compose.systemd.yml" +# COMPOSE_FILE="docker-compose.yml:docker-compose-https.yml:docker-compose.systemd.yml" +# +# Set SIMDB_DASHBOARD_TAG in the environment file to pin a specific +# version. The default is the latest tag at the time of writing. +services: + dashboard: + image: ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} diff --git a/scripts/dashboard.service b/scripts/dashboard.service deleted file mode 100644 index bb8c353..0000000 --- a/scripts/dashboard.service +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Description=SimDB dashboard gunicorn daemon -Requires=dashboard.socket -After=network.target - -[Service] -Type=notify -# the specific user that our service will run as -User=dashboard -Group=dashboard -# another option for an even more restricted service is -# DynamicUser=yes -# see http://0pointer.net/blog/dynamic-users-with-systemd.html -RuntimeDirectory=gunicorn -WorkingDirectory=/home/dashboard/simdb-dashboard -ExecStart=/home/dashboard/env/bin/gunicorn wsgi:app -ExecReload=/bin/kill -s HUP $MAINPID -KillMode=mixed -TimeoutStopSec=5 -PrivateTmp=true - -[Install] -WantedBy=multi-user.target diff --git a/scripts/dashboard.socket b/scripts/dashboard.socket deleted file mode 100644 index 4f31315..0000000 --- a/scripts/dashboard.socket +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=SimDB dashboard socket - -[Socket] -ListenStream=/run/dashboard.sock -# Our service won't need permissions for the socket, since it -# inherits the file descriptor by socket activation -# only the nginx daemon will need access to the socket -SocketUser=nginx -# Optionally restrict the socket permissions even more. -# SocketMode=600 - -[Install] -WantedBy=sockets.target diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service new file mode 100644 index 0000000..2adc4f9 --- /dev/null +++ b/scripts/simdb-dashboard.service @@ -0,0 +1,120 @@ +# SimDB Dashboard — systemd service unit +# +# Runs the SimDB Dashboard via docker compose under systemd. +# +# ## Quick start (HTTP) +# +# See `make systemd-install` and `make systemd-enable`. +# +# 1. Populate the working directory: +# +# sudo mkdir -p /opt/simdb-dashboard +# sudo cp docker-compose.yml /opt/simdb-dashboard/ +# sudo cp docker-compose.systemd.yml /opt/simdb-dashboard/ +# sudo cp -r docker/nginx/templates /opt/simdb-dashboard/ +# +# 2. Create the environment file (only what differs from defaults): +# +# sudo mkdir -p /etc/simdb-dashboard +# sudo tee /etc/simdb-dashboard/simdb-dashboard.env < Date: Fri, 7 Aug 2026 03:00:16 +0200 Subject: [PATCH 11/29] mv docker-compose.https.yml --- ...ompose-https.yml => docker-compose.https.yml | 11 +++++++++-- docker-compose.systemd.yml | 17 +++++++++-------- docs/installation.md | 6 +++--- scripts/simdb-dashboard.service | 4 ++-- 4 files changed, 23 insertions(+), 15 deletions(-) rename docker-compose-https.yml => docker-compose.https.yml (54%) diff --git a/docker-compose-https.yml b/docker-compose.https.yml similarity index 54% rename from docker-compose-https.yml rename to docker-compose.https.yml index ba1d632..41a3802 100644 --- a/docker-compose-https.yml +++ b/docker-compose.https.yml @@ -1,8 +1,15 @@ # HTTPS override for docker-compose.yml. -# Reuses the base dashboard service definition and only adds or overrides SSL-specific settings. +# +# Only adds or overrides SSL-specific settings. +# +# Enable this override by including it in COMPOSE_FILE, +# after the original, e.g. +# COMPOSE_FILE="docker-compose.yml:docker-compose.https.yml" +# COMPOSE_FILE="docker-compose.yml:docker-compose.https.yml:docker-compose.systemd.yml" +# services: dashboard: - image: simdb-dashboard:service-https + container_name: ${DASHBOARD_CONTAINER_NAME:-simdb-dashboard-https} environment: # Use server-https.conf when SSL certificates (cert.crt and cert.key) are provided. SERVER_CONF: ${SERVER_CONF:-server-https.conf} diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml index 6ff4dba..8aedee9 100644 --- a/docker-compose.systemd.yml +++ b/docker-compose.systemd.yml @@ -1,14 +1,15 @@ -# systemd deployment override — use the GHCR-published image instead of a local build. +# Systemd override for docker-compose.yml. # -# Include this file last in COMPOSE_FILE so its image: value wins over -# both docker-compose.yml (simdb-dashboard:service) and -# docker-compose-https.yml (simdb-dashboard:service-https). -# -# COMPOSE_FILE="docker-compose.yml:docker-compose.systemd.yml" -# COMPOSE_FILE="docker-compose.yml:docker-compose-https.yml:docker-compose.systemd.yml" +# Uses the GHCR-published image instead of a local build. # # Set SIMDB_DASHBOARD_TAG in the environment file to pin a specific -# version. The default is the latest tag at the time of writing. +# version. The default is the latest tag at the time of writing. +# +# Enable this override by including it in COMPOSE_FILE, +# after the original, e.g. +# COMPOSE_FILE="docker-compose.yml:docker-compose.https.yml" +# COMPOSE_FILE="docker-compose.yml:docker-compose.https.yml:docker-compose.systemd.yml" +# services: dashboard: image: ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} diff --git a/docs/installation.md b/docs/installation.md index f85e606..e9de2b6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -50,7 +50,7 @@ make down ## HTTPS installation workflow (Docker image + Compose override) -The repository also includes an HTTPS-specific Docker stage and a Compose override in `docker-compose-https.yml`. +The repository also includes an HTTPS-specific Docker stage and a Compose override in `docker-compose.https.yml`. 1. Provide TLS certificate files at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. @@ -79,7 +79,7 @@ USE_HTTPS=1 make down ## Adjusting the installation -Did something change in the `docker-compose.yml`, `docker-compose-https.yml`, or `docker/*` files? Stop the service, and restart service (rebuild not necessary): +Did something change in the `docker-compose.yml`, `docker-compose.https.yml`, or `docker/*` files? Stop the service, and restart service (rebuild not necessary): ```sh make down up @@ -96,7 +96,7 @@ Notes: - Requests under `/scenarios/api/` are proxied by nginx to a simdb server expected at `API_HOST:API_PORT` (defaults to `host.docker.internal:5000`). - You can start multiple dashboards if you change the host port with `DASHBOARD_PORT`. - The HTTPS compose override also publishes `DASHBOARD_HTTPS_PORT` (default `443`) and switches `SERVER_CONF` to `server-https.conf`. -- `docker-compose-https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`. +- `docker-compose.https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`. - Set `USE_HTTPS=1` to switch shared Make targets such as `up`, `down`, `logs-f`, and `shell` to the HTTPS compose chain. - The `USE_HTTPS=1` flag expects **TLS certificate files** at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. If you do not have certificate files yet, a self-signed key pair can be generated with the provided script at `scripts/generate-self-signed-certs.sh` (see the [HTTPS installation workflow](#https-installation-workflow-docker-image--compose-override) section for details). - Use `PUBLIC_SIMDB_URL` or edit `docker\nginx\templates\snippets\runtime-config-template.js` for adjusting the simdb server: diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service index 2adc4f9..2af3941 100644 --- a/scripts/simdb-dashboard.service +++ b/scripts/simdb-dashboard.service @@ -32,7 +32,7 @@ # Follow the HTTP steps above, but additionally copy the HTTPS override # and update COMPOSE_FILE to include it before the systemd override: # -# sudo cp docker-compose-https.yml /opt/simdb-dashboard/ +# sudo cp docker-compose.https.yml /opt/simdb-dashboard/ # # sudo mkdir -p /etc/ssl/simdb-dashboard # sudo cp docker/nginx/ssl/cert.* /etc/ssl/simdb-dashboard/ @@ -41,7 +41,7 @@ # # # Update the env file # sudo tee /etc/simdb-dashboard/simdb-dashboard.env < Date: Fri, 7 Aug 2026 03:00:45 +0200 Subject: [PATCH 12/29] mv .github/workflows/ci.yml --- .github/workflows/{node.js.yml => ci.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{node.js.yml => ci.yml} (99%) diff --git a/.github/workflows/node.js.yml b/.github/workflows/ci.yml similarity index 99% rename from .github/workflows/node.js.yml rename to .github/workflows/ci.yml index 8782e5b..85a841e 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: uses: docker/setup-buildx-action@v4 # APP_VERSION is embedded into the static assets at build time - - name: Build build target image + - name: Build build image uses: docker/build-push-action@v7 with: target: build From cf1b8ba07a576c96f31e6240246b7858f5caa525 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 21:18:27 +0200 Subject: [PATCH 13/29] update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9cce414..f8663cb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,4 +5,4 @@ # Do not edit unless specifically mandated to do so. # Global/fallback and technical modifications. -* @SimonPinches @olivhoenen @prasad-sawantdesai +* @SimonPinches @olivhoenen @prasad-sawantdesai @Louwrensth From e305deb2880675fc8237ed9f15e10abb8f48780d Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 21:19:25 +0200 Subject: [PATCH 14/29] .dockerignore: ignore some more --- .dockerignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.dockerignore b/.dockerignore index af4da6fe..110140f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,13 @@ .git .github +.vscode dashboard/dist dashboard/node_modules +dashboard/.vite docker/nginx/ssl/*.crt docker/nginx/ssl/*.key docs +.env +.env.* node_modules scripts From dbc3636d0d29c382b3b7509bc6274a40c3ac2ce8 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 21:26:28 +0200 Subject: [PATCH 15/29] add env file for systemd service --- scripts/simdb-dashboard.env.example | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 scripts/simdb-dashboard.env.example diff --git a/scripts/simdb-dashboard.env.example b/scripts/simdb-dashboard.env.example new file mode 100644 index 0000000..3949f05 --- /dev/null +++ b/scripts/simdb-dashboard.env.example @@ -0,0 +1,28 @@ +## Use this env file to tweak the behavior of your `docker compose` command. +## In particular, the systemd service file uses an EnvironmentFile definition, +## see scripts/simdb-dashboard.service. +## Everything here is commented out (to illustrate the default). + +## Select compose file overrides. `docker compose` will use docker-compose.yml by default, +## unless COMPOSE_FILE or --file is set. +## +## The systemd variant (prefers online published image), default: +#COMPOSE_FILE=docker-compose.yml:docker-compose.systemd.yml +## The https (enables https config and ports) + systemd variant, use in this order: +#COMPOSE_FILE=docker-compose.yml:docker-compose.https.yml:docker-compose.systemd.yml + +## Used in docker-compose.systemd.yml +#SIMDB_DASHBOARD_TAG=service-0.9.0-68-g8819049 + +## Used in docker-compose*.yml +#DASHBOARD_PORT=80 +#DASHBOARD_HTTPS_PORT=443 + +## Used in docker/nginx/templates/default.conf.template +## and potentially in docker/nginx/templates/snippets/location-dashboard.conf.template +#API_HOST=host.docker.internal +#API_PORT=5000 + +## Used in docker/nginx/templates/snippets/runtime-config-template.js +## via docker/nginx/templates/snippets/location-dashboard.conf.template +#SIMDB_SERVER_URL=/scenarios/api From 552ba231e83765c6d991a0b9a207081f3757c7e5 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 21:27:11 +0200 Subject: [PATCH 16/29] systemd: simplify Makefile --- Makefile | 120 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 51 deletions(-) diff --git a/Makefile b/Makefile index 04c47c6..461c454 100644 --- a/Makefile +++ b/Makefile @@ -2,18 +2,18 @@ SHELL := /bin/sh VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo 0.0.0-unknown) DASHBOARD_PORT ?= 80 -COMPOSE_PROJECT_NAME ?= simdb-dashboard-$(DASHBOARD_PORT) +PROJECT_NAME ?= simdb-dashboard +COMPOSE_PROJECT_NAME ?= $(PROJECT_NAME)-$(DASHBOARD_PORT) ifeq ($(USE_HTTPS),1) DASHBOARD_HTTPS_PORT ?= 443 export DASHBOARD_HTTPS_PORT -COMPOSE_FILE ?= docker-compose.yml:docker-compose-https.yml -SERVICE_IMAGE := simdb-dashboard:service-https +COMPOSE_FILE ?= docker-compose.yml:docker-compose.https.yml else COMPOSE_FILE ?= docker-compose.yml -SERVICE_IMAGE := simdb-dashboard:service endif +# Used by docker-compose export DASHBOARD_PORT export COMPOSE_PROJECT_NAME @@ -21,15 +21,20 @@ DOCKER_CMD ?= docker DOCKER_BUILD ?= $(DOCKER_CMD) build --build-arg APP_VERSION="$(VERSION)" DOCKER_COMPOSE ?= APP_VERSION="$(VERSION)" COMPOSE_FILE="$(COMPOSE_FILE)" $(DOCKER_CMD) compose -BUILD_IMAGE := simdb-dashboard:build +# systemd-install destinations +package_optdir ?= /opt/$(PROJECT_NAME) +package_etcdir ?= /etc/$(PROJECT_NAME) +systemd_unitdir ?= /etc/systemd/system + DEV_IMAGE := simdb-dashboard:dev +BUILD_IMAGE := simdb-dashboard:build +SERVICE_IMAGE := simdb-dashboard:service .DEFAULT_GOAL := service .PHONY: \ build \ builder \ - clean-images \ deploy \ dev \ dist \ @@ -43,8 +48,11 @@ DEV_IMAGE := simdb-dashboard:dev service \ shell \ systemd-disable \ + systemd-daemon-reload \ systemd-enable \ + systemd-installdirs \ systemd-install \ + systemd-status \ systemd-start \ systemd-stop \ systemd-uninstall \ @@ -69,7 +77,7 @@ help: @echo "" @echo "HTTPS toggle (set USE_HTTPS=1):" @echo " USE_HTTPS=1 make service Build HTTPS service stage and tag simdb-dashboard:service-https" - @echo " USE_HTTPS=1 make up Start dashboard with docker-compose-https.yml override" + @echo " USE_HTTPS=1 make up Start dashboard with docker-compose.https.yml override" @echo " USE_HTTPS=1 make down Stop dashboard started with the HTTPS compose override" @echo " USE_HTTPS=1 make logs-f Follow logs of the HTTPS compose service" @echo " USE_HTTPS=1 make shell Enter shell in the started HTTPS compose service" @@ -94,13 +102,14 @@ help: @echo " make deploy Deploy project (placeholder)" @echo "" @echo "Systemd integration (run with sudo):" - @echo " sudo make systemd-install Copy files to /opt/simdb-dashboard and /etc/simdb-dashboard" - @echo " sudo make systemd-enable systemctl daemon-reload, enable, and start the service" - @echo " sudo make systemd-start systemctl start simdb-dashboard" - @echo " sudo make systemd-stop systemctl stop simdb-dashboard" - @echo " sudo make systemd-disable systemctl stop && systemctl disable simdb-dashboard" - @echo " sudo make systemd-uninstall Disable, remove unit file, delete /opt/ and /etc/ files" - @echo " USE_HTTPS=1 sudo make systemd-install Include HTTPS compose override and SSL setup" + @echo " sudo make systemd-install Copy files to $(package_optdir) and $(package_etcdir)" + @echo " sudo make systemd-enable systemctl daemon-reload && systemctl enable simdb-dashboard" + @echo " sudo make systemd-start systemctl start simdb-dashboard" + @echo " sudo make systemd-status systemctl status simdb-dashboard" + @echo " sudo make systemd-stop systemctl stop simdb-dashboard" + @echo " sudo make systemd-disable systemctl stop && systemctl disable simdb-dashboard" + @echo " sudo make systemd-uninstall Remove files installed by systemd-install" + @echo " USE_HTTPS=1 sudo make systemd-install Include HTTPS compose override and SSL setup" @echo "" @echo "Environment variable examples:" @echo " Start simdb-dashboard at alternative DASHBOARD_PORT, with simdb server at API_PORT:" @@ -187,53 +196,62 @@ dashboard/package-lock.json: dashboard/package.json distclean: APP_VERSION="$(VERSION)" COMPOSE_FILE="docker-compose.yml" $(DOCKER_CMD) compose down --volumes --remove-orphans --rmi local - APP_VERSION="$(VERSION)" COMPOSE_FILE="docker-compose.yml:docker-compose-https.yml" $(DOCKER_CMD) compose down --volumes --remove-orphans --rmi local + APP_VERSION="$(VERSION)" COMPOSE_FILE="docker-compose.yml:docker-compose.https.yml" $(DOCKER_CMD) compose down --volumes --remove-orphans --rmi local $(DOCKER_CMD) rmi -f $(BUILD_IMAGE) simdb-dashboard:service simdb-dashboard:service-https >/dev/null 2>&1 || true $(DOCKER_CMD) volume rm -f simdb_dashboard_node_modules >/dev/null 2>&1 || true rm -rf dist # Systemd integration (run with sudo) -systemd-install: - mkdir -p /opt/simdb-dashboard - cp docker-compose.yml /opt/simdb-dashboard/ - cp docker-compose.systemd.yml /opt/simdb-dashboard/ - cp -r docker/nginx/templates /opt/simdb-dashboard/ - mkdir -p /etc/simdb-dashboard - [ -f /etc/simdb-dashboard/simdb-dashboard.env ] || \ - { echo '# Created by make systemd-install' > /etc/simdb-dashboard/simdb-dashboard.env; \ - echo 'API_HOST=host.docker.internal' >> /etc/simdb-dashboard/simdb-dashboard.env; \ - echo 'SIMDB_SERVER_URL=/scenarios/api' >> /etc/simdb-dashboard/simdb-dashboard.env; } -ifeq ($(USE_HTTPS),1) - cp docker-compose-https.yml /opt/simdb-dashboard/ - mkdir -p /etc/ssl/simdb-dashboard - cp docker/nginx/ssl/cert.* /etc/ssl/simdb-dashboard/ 2>/dev/null || \ - echo " *** No certificate files found at docker/nginx/ssl/ — generate them with:"; \ - echo " *** scripts/generate-self-signed-certs.sh"; \ - echo " *** cp docker/nginx/ssl/cert.* /etc/ssl/simdb-dashboard/" - mkdir -p /opt/simdb-dashboard/docker/nginx - ln -sf /etc/ssl/simdb-dashboard /opt/simdb-dashboard/docker/nginx/ssl -endif - -systemd-enable: +systemd-installdirs: + mkdir -p \ + $(DESTDIR)/$(package_etcdir) \ + $(DESTDIR)/$(package_optdir) \ + $(DESTDIR)/$(package_optdir)/docker/templates \ + $(DESTDIR)/$(package_optdir)/docker/templates/snippets \ + $(DESTDIR)/$(systemd_unitdir) + +systemd-install: systemd-installdirs + cp -r \ + docker-compose.https.yml \ + docker-compose.systemd.yml \ + docker-compose.yml \ + $(DESTDIR)/$(package_optdir) + ls docker/nginx/ssl/* 2>/dev/null && \ + cp -r \ + docker/nginx/ssl/* \ + $(DESTDIR)/$(package_optdir)/docker/nginx/ssl/ || \ + echo "WARNING: Could not install missing cert files, see docker/nginx/ssl/*" + cp -r \ + docker/nginx/templates/default.conf.template \ + $(DESTDIR)/$(package_optdir)/docker/templates/ + cp -r \ + docker/nginx/templates/snippets/location-dashboard.conf.template \ + docker/nginx/templates/snippets/location-simdb_proxy.conf.template \ + docker/nginx/templates/snippets/runtime-config-template.js \ + docker/nginx/templates/snippets/server-http.conf.template \ + docker/nginx/templates/snippets/server-https.conf.template \ + $(DESTDIR)/$(package_optdir)/docker/templates/snippets + cp -r \ + scripts/simdb-dashboard.env.example \ + $(DESTDIR)/$(package_etcdir)/simdb-dashboard.env.example + cp -r \ + scripts/simdb-dashboard.service \ + $(DESTDIR)/$(systemd_unitdir)/simdb-dashboard.service + +systemd-uninstall: + -rm -f --interactive $(DESTDIR)/$(systemd_unitdir)/simdb-dashboard.service + -rm -rf --interactive $(DESTDIR)/$(package_etcdir) + -rm -rf --interactive $(DESTDIR)/$(package_optdir) + +systemd-daemon-reload: systemctl daemon-reload - systemctl enable simdb-dashboard - systemctl start simdb-dashboard -systemd-start: - systemctl start simdb-dashboard - -systemd-stop: - systemctl stop simdb-dashboard +systemd-enable: systemd-daemon-reload systemd-disable: systemd-stop - systemctl disable simdb-dashboard -systemd-uninstall: systemd-disable - rm -f /etc/systemd/system/simdb-dashboard.service - systemctl daemon-reload - rm -rf /opt/simdb-dashboard - rm -rf /etc/simdb-dashboard - [ ! -e /etc/ssl/simdb-dashboard ] || rm -rf /etc/ssl/simdb-dashboard +systemd-start systemd-status systemd-stop systemd-enable systemd-disable: + systemctl $(patsubst systemd-%,%,$@) simdb-dashboard # Deployment deploy: From b512dc5a6fabbababfa988d762d12bfce35f4df6 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 21:44:19 +0200 Subject: [PATCH 17/29] generate-self-signed-certs.sh: add x.509 extension and SAN --- Makefile | 12 ++++-- scripts/generate-self-signed-certs.sh | 55 +++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 461c454..7921c54 100644 --- a/Makefile +++ b/Makefile @@ -206,8 +206,8 @@ systemd-installdirs: mkdir -p \ $(DESTDIR)/$(package_etcdir) \ $(DESTDIR)/$(package_optdir) \ - $(DESTDIR)/$(package_optdir)/docker/templates \ - $(DESTDIR)/$(package_optdir)/docker/templates/snippets \ + $(DESTDIR)/$(package_optdir)/docker/nginx/ssl/ \ + $(DESTDIR)/$(package_optdir)/docker/nginx/templates/snippets \ $(DESTDIR)/$(systemd_unitdir) systemd-install: systemd-installdirs @@ -223,17 +223,21 @@ systemd-install: systemd-installdirs echo "WARNING: Could not install missing cert files, see docker/nginx/ssl/*" cp -r \ docker/nginx/templates/default.conf.template \ - $(DESTDIR)/$(package_optdir)/docker/templates/ + $(DESTDIR)/$(package_optdir)/docker/nginx/templates/ cp -r \ docker/nginx/templates/snippets/location-dashboard.conf.template \ docker/nginx/templates/snippets/location-simdb_proxy.conf.template \ docker/nginx/templates/snippets/runtime-config-template.js \ docker/nginx/templates/snippets/server-http.conf.template \ docker/nginx/templates/snippets/server-https.conf.template \ - $(DESTDIR)/$(package_optdir)/docker/templates/snippets + $(DESTDIR)/$(package_optdir)/docker/nginx/templates/snippets cp -r \ scripts/simdb-dashboard.env.example \ $(DESTDIR)/$(package_etcdir)/simdb-dashboard.env.example + cd $(DESTDIR)/$(package_etcdir) && \ + [ ! -f simdb-dashboard.env ] && \ + cp simdb-dashboard.env.example simdb-dashboard.env || \ + true cp -r \ scripts/simdb-dashboard.service \ $(DESTDIR)/$(systemd_unitdir)/simdb-dashboard.service diff --git a/scripts/generate-self-signed-certs.sh b/scripts/generate-self-signed-certs.sh index cbb61fa..7984f3a 100755 --- a/scripts/generate-self-signed-certs.sh +++ b/scripts/generate-self-signed-certs.sh @@ -3,13 +3,19 @@ set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) +# Directory where generated TLS assets are to be stored. SSL_DIR="$REPO_ROOT/docker/nginx/ssl" CERT_FILE="$SSL_DIR/cert.crt" KEY_FILE="$SSL_DIR/cert.key" +# Temporary OpenSSL config file used for SAN and extensions. +CNF_FILE="$SSL_DIR/san.cnf" -DAYS="${CERT_DAYS:-3650}" +# Certificate validity period in days. +CERT_DAYS="${CERT_DAYS:-3650}" +# Primary DNS name for SAN/CN; auto-detected with localhost fallback. DOMAIN_NAME="${DOMAIN_NAME:-$(hostname -f 2>/dev/null || hostname --fqdn 2>/dev/null || hostname 2>/dev/null || echo localhost)}" +# Certificate common name; defaults to DOMAIN_NAME. CERT_CN=${CERT_CN:-$DOMAIN_NAME} if ! command -v openssl >/dev/null 2>&1; then @@ -19,15 +25,55 @@ fi mkdir -p "$SSL_DIR" +cat > "$CNF_FILE" < Date: Fri, 7 Aug 2026 23:18:35 +0200 Subject: [PATCH 18/29] add more logging --- docker-compose.systemd.yml | 4 ++++ docker/nginx/templates/default.conf.template | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml index 8aedee9..f94527e 100644 --- a/docker-compose.systemd.yml +++ b/docker-compose.systemd.yml @@ -13,3 +13,7 @@ services: dashboard: image: ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} + logging: + driver: journald + options: + tag: simdb-dashboard \ No newline at end of file diff --git a/docker/nginx/templates/default.conf.template b/docker/nginx/templates/default.conf.template index a1e769f..4d97cf7 100644 --- a/docker/nginx/templates/default.conf.template +++ b/docker/nginx/templates/default.conf.template @@ -2,6 +2,11 @@ # are envsubst-processed into /etc/nginx/conf.d at container startup. # See: https://github.com/docker-library/docs/tree/master/nginx +# Log to container stdout/stderr so logs are visible via `docker compose logs`. +# Change `info` to `debug` for maximum verbosity (requires nginx debug build). +error_log /dev/stderr info; +access_log /dev/stdout; + # Hostnames inside upstream blocks are resolved at config-load time using the # system resolver, which does read /etc/hosts. upstream simdb_backend { From 2bb577c4c1c413097f5faad75c2308ae57dd6cd7 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 23:39:36 +0200 Subject: [PATCH 19/29] systemd: use DASHBOARD_CONTAINER_NAME, SIMDB_DASHBOARD_IMAGE --- docker-compose.https.yml | 1 - docker-compose.systemd.yml | 2 +- docker-compose.yml | 1 + scripts/simdb-dashboard.env.example | 4 ++++ scripts/simdb-dashboard.service | 2 ++ 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docker-compose.https.yml b/docker-compose.https.yml index 41a3802..cdcc093 100644 --- a/docker-compose.https.yml +++ b/docker-compose.https.yml @@ -9,7 +9,6 @@ # services: dashboard: - container_name: ${DASHBOARD_CONTAINER_NAME:-simdb-dashboard-https} environment: # Use server-https.conf when SSL certificates (cert.crt and cert.key) are provided. SERVER_CONF: ${SERVER_CONF:-server-https.conf} diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml index f94527e..385b18f 100644 --- a/docker-compose.systemd.yml +++ b/docker-compose.systemd.yml @@ -12,7 +12,7 @@ # services: dashboard: - image: ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} + image: ${SIMDB_DASHBOARD_IMAGE:-ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049}} logging: driver: journald options: diff --git a/docker-compose.yml b/docker-compose.yml index a2f26cd..019b046 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,6 @@ services: dashboard: + container_name: ${DASHBOARD_CONTAINER_NAME:-simdb-dashboard} image: simdb-dashboard:service labels: io.simdb.component: "dashboard" diff --git a/scripts/simdb-dashboard.env.example b/scripts/simdb-dashboard.env.example index 3949f05..e5c3959 100644 --- a/scripts/simdb-dashboard.env.example +++ b/scripts/simdb-dashboard.env.example @@ -11,8 +11,12 @@ ## The https (enables https config and ports) + systemd variant, use in this order: #COMPOSE_FILE=docker-compose.yml:docker-compose.https.yml:docker-compose.systemd.yml +## Make the simdb-dashboard.service use a different name in docker ps +#DASHBOARD_CONTAINER_NAME=simdb-dashboard-systemd" + ## Used in docker-compose.systemd.yml #SIMDB_DASHBOARD_TAG=service-0.9.0-68-g8819049 +#SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} ## Used in docker-compose*.yml #DASHBOARD_PORT=80 diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service index 2af3941..a85c037 100644 --- a/scripts/simdb-dashboard.service +++ b/scripts/simdb-dashboard.service @@ -86,7 +86,9 @@ WorkingDirectory=/opt/simdb-dashboard # --- defaults (overridden by env file when set) ----------------------- Environment="COMPOSE_FILE=docker-compose.yml:docker-compose.systemd.yml" +Environment="DASHBOARD_CONTAINER_NAME=simdb-dashboard-systemd" Environment="SIMDB_DASHBOARD_TAG=service-0.9.0-68-g8819049" +#Environment="SIMDB_DASHBOARD_IMAGE=" Environment="DASHBOARD_PORT=80" Environment="DASHBOARD_HTTPS_PORT=443" Environment="API_HOST=host.docker.internal" From 81bd9ebfdb770ceaf0fc1987ff4778943813a0fc Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 7 Aug 2026 23:52:20 +0200 Subject: [PATCH 20/29] nginx: listen ... http2 directive is deprecated --- docker/nginx/templates/snippets/server-https.conf.template | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/nginx/templates/snippets/server-https.conf.template b/docker/nginx/templates/snippets/server-https.conf.template index 1904fdc..4bf7f79 100644 --- a/docker/nginx/templates/snippets/server-https.conf.template +++ b/docker/nginx/templates/snippets/server-https.conf.template @@ -6,8 +6,9 @@ server { } server { - listen 443 ssl http2; - # listen [::]:443 ssl http2; + listen 443 ssl; + # listen [::]:443 ssl; + http2 on; server_name _; root /usr/share/nginx/html; absolute_redirect off; From 9fb123764f11879a4fd2c956df23a45f5a7363c1 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 14 Aug 2026 23:50:40 +0200 Subject: [PATCH 21/29] systemd: latest tag by default, howto using make --- docker-compose.systemd.yml | 2 +- scripts/simdb-dashboard.env.example | 4 +- scripts/simdb-dashboard.service | 77 +++++++++++------------------ 3 files changed, 32 insertions(+), 51 deletions(-) diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml index 385b18f..6a09b69 100644 --- a/docker-compose.systemd.yml +++ b/docker-compose.systemd.yml @@ -12,7 +12,7 @@ # services: dashboard: - image: ${SIMDB_DASHBOARD_IMAGE:-ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049}} + image: ${SIMDB_DASHBOARD_IMAGE:-ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-latest}} logging: driver: journald options: diff --git a/scripts/simdb-dashboard.env.example b/scripts/simdb-dashboard.env.example index e5c3959..3e24244 100644 --- a/scripts/simdb-dashboard.env.example +++ b/scripts/simdb-dashboard.env.example @@ -15,8 +15,8 @@ #DASHBOARD_CONTAINER_NAME=simdb-dashboard-systemd" ## Used in docker-compose.systemd.yml -#SIMDB_DASHBOARD_TAG=service-0.9.0-68-g8819049 -#SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-service-0.9.0-68-g8819049} +#SIMDB_DASHBOARD_TAG=latest +#SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-latest} ## Used in docker-compose*.yml #DASHBOARD_PORT=80 diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service index a85c037..752642e 100644 --- a/scripts/simdb-dashboard.service +++ b/scripts/simdb-dashboard.service @@ -4,49 +4,29 @@ # # ## Quick start (HTTP) # -# See `make systemd-install` and `make systemd-enable`. -# -# 1. Populate the working directory: -# -# sudo mkdir -p /opt/simdb-dashboard -# sudo cp docker-compose.yml /opt/simdb-dashboard/ -# sudo cp docker-compose.systemd.yml /opt/simdb-dashboard/ -# sudo cp -r docker/nginx/templates /opt/simdb-dashboard/ -# -# 2. Create the environment file (only what differs from defaults): -# -# sudo mkdir -p /etc/simdb-dashboard -# sudo tee /etc/simdb-dashboard/simdb-dashboard.env < Date: Fri, 14 Aug 2026 23:51:39 +0200 Subject: [PATCH 22/29] ci: tag and push "latest", create release with tarball asset --- .github/workflows/ci.yml | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85a841e..56f09f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,7 +115,7 @@ jobs: runs-on: ubuntu-latest needs: build permissions: - contents: read + contents: write packages: write steps: - name: Lowercase repo-owner @@ -128,6 +128,12 @@ jobs: name: simdb-dashboard-service path: ${{ runner.temp }} + - name: Download dist artifact + uses: actions/download-artifact@v8 + with: + name: simdb-dashboard-dist + path: ${{ runner.temp }}/dist + - name: Load service image run: docker load -i "${{ runner.temp }}/simdb-dashboard-service.tar" @@ -138,11 +144,29 @@ jobs: username: ${{ steps.repo_owner.outputs.value }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Tag service image for registry + - name: Tag and push service image with version tag run: | docker tag \ simdb-dashboard:service \ - ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:service-${{ needs.build.outputs.version }} + ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:${{ needs.build.outputs.version }} + docker push ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:${{ needs.build.outputs.version }} - - name: Push service image - run: docker push ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:service-${{ needs.build.outputs.version }} + # These steps run on tagged commits only — `:latest` tag and GitHub Release. + - name: Tag and push latest + if: ${{ github.ref_type == 'tag' }} + run: | + docker tag \ + simdb-dashboard:service \ + ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:latest + docker push ghcr.io/${{ steps.repo_owner.outputs.value }}/simdb-dashboard:latest + + - name: Package and upload release tarball + if: ${{ github.ref_type == 'tag' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + tar -czf "${{ runner.temp }}/simdb-dashboard-dist.tar.gz" \ + -C "${{ runner.temp }}/dist" . + gh release create "${{ needs.build.outputs.version }}" \ + "${{ runner.temp }}/simdb-dashboard-dist.tar.gz" \ + --generate-notes From 19a156c9540e2464994cd1ebdff8492a2e0db989 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Fri, 14 Aug 2026 23:55:48 +0200 Subject: [PATCH 23/29] Makefile: use conventional install command --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 7921c54..2f66824 100644 --- a/Makefile +++ b/Makefile @@ -211,34 +211,34 @@ systemd-installdirs: $(DESTDIR)/$(systemd_unitdir) systemd-install: systemd-installdirs - cp -r \ + install -m 644 \ docker-compose.https.yml \ docker-compose.systemd.yml \ docker-compose.yml \ $(DESTDIR)/$(package_optdir) ls docker/nginx/ssl/* 2>/dev/null && \ - cp -r \ + install -m 600 \ docker/nginx/ssl/* \ $(DESTDIR)/$(package_optdir)/docker/nginx/ssl/ || \ echo "WARNING: Could not install missing cert files, see docker/nginx/ssl/*" - cp -r \ + install -m 644 \ docker/nginx/templates/default.conf.template \ $(DESTDIR)/$(package_optdir)/docker/nginx/templates/ - cp -r \ + install -m 644 \ docker/nginx/templates/snippets/location-dashboard.conf.template \ docker/nginx/templates/snippets/location-simdb_proxy.conf.template \ docker/nginx/templates/snippets/runtime-config-template.js \ docker/nginx/templates/snippets/server-http.conf.template \ docker/nginx/templates/snippets/server-https.conf.template \ $(DESTDIR)/$(package_optdir)/docker/nginx/templates/snippets - cp -r \ + install -m 644 \ scripts/simdb-dashboard.env.example \ $(DESTDIR)/$(package_etcdir)/simdb-dashboard.env.example cd $(DESTDIR)/$(package_etcdir) && \ [ ! -f simdb-dashboard.env ] && \ - cp simdb-dashboard.env.example simdb-dashboard.env || \ + install -m 644 simdb-dashboard.env.example simdb-dashboard.env || \ true - cp -r \ + install -m 644 \ scripts/simdb-dashboard.service \ $(DESTDIR)/$(systemd_unitdir)/simdb-dashboard.service From 9bb3e3f00d12c8307376245ac470355404445ac3 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Mon, 17 Aug 2026 05:44:58 +0200 Subject: [PATCH 24/29] fixup! systemd: latest tag by default, howto using make --- docker-compose.systemd.yml | 2 +- scripts/simdb-dashboard.env.example | 2 +- scripts/simdb-dashboard.service | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.systemd.yml b/docker-compose.systemd.yml index 6a09b69..8ccd7d3 100644 --- a/docker-compose.systemd.yml +++ b/docker-compose.systemd.yml @@ -12,7 +12,7 @@ # services: dashboard: - image: ${SIMDB_DASHBOARD_IMAGE:-ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-latest}} + image: ${SIMDB_DASHBOARD_IMAGE:-ghcr.io/iterorganization/simdb-dashboard}:${SIMDB_DASHBOARD_TAG:-latest} logging: driver: journald options: diff --git a/scripts/simdb-dashboard.env.example b/scripts/simdb-dashboard.env.example index 3e24244..9718fbf 100644 --- a/scripts/simdb-dashboard.env.example +++ b/scripts/simdb-dashboard.env.example @@ -16,7 +16,7 @@ ## Used in docker-compose.systemd.yml #SIMDB_DASHBOARD_TAG=latest -#SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-latest} +#SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard ## Used in docker-compose*.yml #DASHBOARD_PORT=80 diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service index 752642e..b5b40d9 100644 --- a/scripts/simdb-dashboard.service +++ b/scripts/simdb-dashboard.service @@ -69,7 +69,7 @@ WorkingDirectory=/opt/simdb-dashboard Environment="COMPOSE_FILE=docker-compose.yml:docker-compose.systemd.yml" #Environment="DASHBOARD_CONTAINER_NAME=simdb-dashboard-systemd" #Environment="SIMDB_DASHBOARD_TAG=latest" -#Environment="SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard:${SIMDB_DASHBOARD_TAG:-latest}" +#Environment="SIMDB_DASHBOARD_IMAGE=ghcr.io/iterorganization/simdb-dashboard" #Environment="DASHBOARD_PORT=80" #Environment="DASHBOARD_HTTPS_PORT=443" #Environment="API_HOST=host.docker.internal" From bef735089653da5e384e614a91ae0728ad80d4e8 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Mon, 17 Aug 2026 06:42:10 +0200 Subject: [PATCH 25/29] nginx: use gzip_static assets with cache-control gzip compressed the index-.js from 5.6 -> 1.7 MB. caching public, immutable makes the browser not GET it again. surprisingly, not using gzip_static also voids caching behavior of browser. --- .../templates/snippets/location-dashboard.conf.template | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docker/nginx/templates/snippets/location-dashboard.conf.template b/docker/nginx/templates/snippets/location-dashboard.conf.template index b2d05c4..dad80e8 100644 --- a/docker/nginx/templates/snippets/location-dashboard.conf.template +++ b/docker/nginx/templates/snippets/location-dashboard.conf.template @@ -1,6 +1,15 @@ # dashboard app expects request url path to start with /dashboard/ location = / { return 302 /dashboard/; } location = /dashboard { return 302 /dashboard/; } + +location /dashboard/assets/ { + # Assets have hashes in filenames (index-.js): OK to cache forever + expires 1y; + add_header Cache-Control "public, immutable"; + # Vite pre-compresses assets (vite-plugin-compression). Serve those. + gzip_static on; +} + location /dashboard/ { try_files $uri $uri/ /dashboard/index.html; } From a8e8275cfae171f826f9d28a2d390c73021c0853 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Mon, 24 Aug 2026 11:08:27 +0200 Subject: [PATCH 26/29] systemd: match description with Dockerfile --- scripts/simdb-dashboard.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/simdb-dashboard.service b/scripts/simdb-dashboard.service index b5b40d9..76a0b1f 100644 --- a/scripts/simdb-dashboard.service +++ b/scripts/simdb-dashboard.service @@ -50,7 +50,7 @@ # scripts/generate-self-signed-certs.sh — helper for test certificates [Unit] -Description=SimDB Dashboard — containerized Vue.js frontend and nginx reverse proxy +Description=SimDB Dashboard — Web frontend for the SimDB simulation management tool Documentation=https://github.com/iterorganization/SimDB-Dashboard Requires=docker.service After=docker.service network-online.target From ab29434a48f9b9180e7c43f1407286b4885d11ea Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Mon, 31 Aug 2026 06:47:13 +0200 Subject: [PATCH 27/29] certs: rootCA-signing; replace ssl->tls, sh->Makefile The rootCA's public key is more easily distributed in test network. TLS is a more appropriate name than SSL (dated). A Makefile can better handle dependency between generated files. --- .dockerignore | 5 +- .gitignore | 11 +- Dockerfile | 2 +- Makefile | 27 ++- docker-compose.https.yml | 6 +- docker/nginx/ssl/.gitkeep | 1 - .../snippets/server-https.conf.template | 6 +- docker/nginx/tls/.gitkeep | 1 + docs/installation.md | 6 +- scripts/certs/Makefile | 172 ++++++++++++++++++ scripts/certs/san.cnf.in | 35 ++++ scripts/generate-self-signed-certs.sh | 84 --------- scripts/simdb-dashboard.service | 2 +- 13 files changed, 253 insertions(+), 105 deletions(-) delete mode 100644 docker/nginx/ssl/.gitkeep create mode 100644 docker/nginx/tls/.gitkeep create mode 100644 scripts/certs/Makefile create mode 100644 scripts/certs/san.cnf.in delete mode 100755 scripts/generate-self-signed-certs.sh diff --git a/.dockerignore b/.dockerignore index 110140f..0120535 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,8 +4,9 @@ dashboard/dist dashboard/node_modules dashboard/.vite -docker/nginx/ssl/*.crt -docker/nginx/ssl/*.key +# Generated TLS assets (*.pem, *.key, san.cnf, ...). +docker/nginx/tls/* +!docker/nginx/tls/.gitkeep docs .env .env.* diff --git a/.gitignore b/.gitignore index 86d755a..10aee8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,13 @@ .idea/ dashboard/dist/ dashboard/node_modules/ -docker/nginx/ssl/*.crt -docker/nginx/ssl/*.key +# Generated TLS assets (*.pem, *.key, san.cnf, ...). +docker/nginx/tls/* +!docker/nginx/tls/.gitkeep +# Generated CA / server certificate build dir (scripts/certs/Makefile). +scripts/certs/*.key +scripts/certs/*.pem +scripts/certs/*.csr +scripts/certs/*.srl +scripts/certs/san.cnf dist/ diff --git a/Dockerfile b/Dockerfile index a0ff879..09f4753 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ LABEL org.opencontainers.image.title="SimDB Dashboard" \ io.simdb.component="dashboard" COPY docker/nginx/templates/ /etc/nginx/templates/ COPY docker/nginx/entrypoint/ /docker-entrypoint.d/ -COPY docker/nginx/ssl/ /etc/nginx/ssl/ +COPY docker/nginx/tls/ /etc/nginx/tls/ RUN chmod +x /docker-entrypoint.d/*.sh # App expects itself at urlpath /dashboard COPY --from=build /app/dist /usr/share/nginx/html/dashboard diff --git a/Makefile b/Makefile index 2f66824..77cdf8e 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ SERVICE_IMAGE := simdb-dashboard:service .PHONY: \ build \ builder \ + certs \ deploy \ dev \ dist \ @@ -76,6 +77,7 @@ help: @echo " make shell Enter shell in the started simdb-dashboard service" @echo "" @echo "HTTPS toggle (set USE_HTTPS=1):" + @echo " make certs Generate + install CA-signed TLS certs into docker/nginx/tls" @echo " USE_HTTPS=1 make service Build HTTPS service stage and tag simdb-dashboard:service-https" @echo " USE_HTTPS=1 make up Start dashboard with docker-compose.https.yml override" @echo " USE_HTTPS=1 make down Stop dashboard started with the HTTPS compose override" @@ -109,7 +111,7 @@ help: @echo " sudo make systemd-stop systemctl stop simdb-dashboard" @echo " sudo make systemd-disable systemctl stop && systemctl disable simdb-dashboard" @echo " sudo make systemd-uninstall Remove files installed by systemd-install" - @echo " USE_HTTPS=1 sudo make systemd-install Include HTTPS compose override and SSL setup" + @echo " USE_HTTPS=1 sudo make systemd-install Include HTTPS compose override and TLS setup" @echo "" @echo "Environment variable examples:" @echo " Start simdb-dashboard at alternative DASHBOARD_PORT, with simdb server at API_PORT:" @@ -117,6 +119,10 @@ help: @echo " Start HTTPS dashboard with alternative HTTP/HTTPS host ports:" @echo " DASHBOARD_PORT=8080 DASHBOARD_HTTPS_PORT=8443 USE_HTTPS=1 make up" +# Generate and install CA-signed TLS certificates (scripts/certs/Makefile). +certs: + $(MAKE) -C scripts/certs install + # Compose targets up: $(DOCKER_COMPOSE) up -d --no-build @@ -183,6 +189,10 @@ simdb-dashboard-service.tar: service @echo "To load and run the image:" @echo " docker load -i simdb-dashboard-service.tar" @echo " docker run --rm -p 8080:80 --add-host host.docker.internal:host-gateway simdb-dashboard:service" + @echo "To load and run the local image via the systemd service:" + @echo " sudo make systemd-install systemd-enable # if not already done" + @echo " docker load -i simdb-dashboard-service.tar # Loaded image: simdb-dashboard:service" + @echo " sudo vim /etc/simdb-dashboard/simdb-dashboard.env # update SIMDB_DASHBOARD_IMAGE=simdb-dashboard SIMDB_DASHBOARD_TAG=service" update-base: $(DOCKER_BUILD) --no-cache --pull --target service -t $(SERVICE_IMAGE) . @@ -206,7 +216,7 @@ systemd-installdirs: mkdir -p \ $(DESTDIR)/$(package_etcdir) \ $(DESTDIR)/$(package_optdir) \ - $(DESTDIR)/$(package_optdir)/docker/nginx/ssl/ \ + $(DESTDIR)/$(package_optdir)/docker/nginx/tls/ \ $(DESTDIR)/$(package_optdir)/docker/nginx/templates/snippets \ $(DESTDIR)/$(systemd_unitdir) @@ -216,11 +226,16 @@ systemd-install: systemd-installdirs docker-compose.systemd.yml \ docker-compose.yml \ $(DESTDIR)/$(package_optdir) - ls docker/nginx/ssl/* 2>/dev/null && \ + ls docker/nginx/tls/*.key 2>/dev/null && \ install -m 600 \ - docker/nginx/ssl/* \ - $(DESTDIR)/$(package_optdir)/docker/nginx/ssl/ || \ - echo "WARNING: Could not install missing cert files, see docker/nginx/ssl/*" + docker/nginx/tls/*.key \ + $(DESTDIR)/$(package_optdir)/docker/nginx/tls/ || \ + echo "WARNING: Could not install missing cert files, see docker/nginx/tls/*" + ls docker/nginx/tls/*.pem 2>/dev/null && \ + install -m 644 \ + docker/nginx/tls/*.pem \ + $(DESTDIR)/$(package_optdir)/docker/nginx/tls/ || \ + echo "WARNING: Could not install missing cert files, see docker/nginx/tls/*" install -m 644 \ docker/nginx/templates/default.conf.template \ $(DESTDIR)/$(package_optdir)/docker/nginx/templates/ diff --git a/docker-compose.https.yml b/docker-compose.https.yml index cdcc093..58047fb 100644 --- a/docker-compose.https.yml +++ b/docker-compose.https.yml @@ -1,6 +1,6 @@ # HTTPS override for docker-compose.yml. # -# Only adds or overrides SSL-specific settings. +# Only adds or overrides TLS-specific settings. # # Enable this override by including it in COMPOSE_FILE, # after the original, e.g. @@ -10,11 +10,11 @@ services: dashboard: environment: - # Use server-https.conf when SSL certificates (cert.crt and cert.key) are provided. + # Use server-https.conf when TLS certificates (server.pem and server.key) are provided. SERVER_CONF: ${SERVER_CONF:-server-https.conf} ports: - "${DASHBOARD_PORT:-80}:80" - "${DASHBOARD_HTTPS_PORT:-443}:443" volumes: - ./docker/nginx/templates:/etc/nginx/templates - - ./docker/nginx/ssl:/etc/nginx/ssl:ro + - ./docker/nginx/tls:/etc/nginx/tls:ro diff --git a/docker/nginx/ssl/.gitkeep b/docker/nginx/ssl/.gitkeep deleted file mode 100644 index 58dc66b..0000000 --- a/docker/nginx/ssl/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# This directory expects cert.crt and cert.key files. diff --git a/docker/nginx/templates/snippets/server-https.conf.template b/docker/nginx/templates/snippets/server-https.conf.template index 4bf7f79..2257902 100644 --- a/docker/nginx/templates/snippets/server-https.conf.template +++ b/docker/nginx/templates/snippets/server-https.conf.template @@ -13,8 +13,8 @@ server { root /usr/share/nginx/html; absolute_redirect off; - ssl_certificate /etc/nginx/ssl/cert.crt; - ssl_certificate_key /etc/nginx/ssl/cert.key; + ssl_certificate /etc/nginx/tls/server.pem; + ssl_certificate_key /etc/nginx/tls/server.key; # Modern TLS baseline for broad compatibility and strong security. ssl_protocols TLSv1.2 TLSv1.3; @@ -23,7 +23,7 @@ server { # Session settings aligned with current hardening guidance. ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; + ssl_session_cache shared:TLS:50m; ssl_session_tickets off; include /etc/nginx/conf.d/snippets/location-dashboard.conf; diff --git a/docker/nginx/tls/.gitkeep b/docker/nginx/tls/.gitkeep new file mode 100644 index 0000000..d26c91b --- /dev/null +++ b/docker/nginx/tls/.gitkeep @@ -0,0 +1 @@ +# This directory expects server.pem and server.key files. diff --git a/docs/installation.md b/docs/installation.md index e9de2b6..519b5cd 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -52,7 +52,9 @@ make down The repository also includes an HTTPS-specific Docker stage and a Compose override in `docker-compose.https.yml`. -1. Provide TLS certificate files at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. +1. Provide TLS certificate files at `docker/nginx/tls/server.pem` and `docker/nginx/tls/server.key`. + + If you don't have such certificates yet, see the Makefile helper in `scripts/certs` to generate them. 2. Build the HTTPS service image: @@ -98,7 +100,7 @@ Notes: - The HTTPS compose override also publishes `DASHBOARD_HTTPS_PORT` (default `443`) and switches `SERVER_CONF` to `server-https.conf`. - `docker-compose.https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`. - Set `USE_HTTPS=1` to switch shared Make targets such as `up`, `down`, `logs-f`, and `shell` to the HTTPS compose chain. -- The `USE_HTTPS=1` flag expects **TLS certificate files** at `docker/nginx/ssl/cert.crt` and `docker/nginx/ssl/cert.key`. If you do not have certificate files yet, a self-signed key pair can be generated with the provided script at `scripts/generate-self-signed-certs.sh` (see the [HTTPS installation workflow](#https-installation-workflow-docker-image--compose-override) section for details). +- The `USE_HTTPS=1` flag expects **TLS certificate files** at `docker/nginx/tls/server.pem` and `docker/nginx/tls/server.key`. If you do not have certificate files yet, a CA-signed key pair can be generated with the provided Makefile at `certs` (see the [HTTPS installation workflow](#https-installation-workflow-docker-image--compose-override) section for details). - Use `PUBLIC_SIMDB_URL` or edit `docker\nginx\templates\snippets\runtime-config-template.js` for adjusting the simdb server: ```sh diff --git a/scripts/certs/Makefile b/scripts/certs/Makefile new file mode 100644 index 0000000..28a55af --- /dev/null +++ b/scripts/certs/Makefile @@ -0,0 +1,172 @@ +# scripts/certs/Makefile +# +# See `make help` for info + +SHELL := /bin/sh + +# ---- configuration (override via environment or command line) ------------- +RSA_BITS ?= 2048 +CERT_DAYS ?= 3650 +CA_DAYS ?= $(CERT_DAYS) +DOMAIN_NAME ?= $(shell hostname -f 2>/dev/null || hostname --fqdn 2>/dev/null || hostname 2>/dev/null || echo localhost) +CERT_CN ?= $(DOMAIN_NAME) +CA_CN ?= $(CERT_CN) Root CA +OPENSSL ?= openssl + +# Ensure make is run from this directory +$(if $(shell test -f san.cnf.in && echo yes),, $(error Must run 'make' from scripts/certs directory)) + +# ---- paths ---------------------------------------------------------------- +# Destination of `make install` (nginx expects server.pem / server.key here). +TLS_DIR := ../../docker/nginx/tls + +# ---- generated file names (all are Make targets) -------------------------- +SAN_CNF := san.cnf +# Source template for san.cnf (checked in, not generated). Consumed via sed +# (see the $(SAN_CNF) rule); only @CERT_CN@ and @DOMAIN_NAME@ are substituted. +SAN_CNF_IN := san.cnf.in +ROOTCA_KEY := rootCA.key +ROOTCA_PEM := rootCA.pem +SERVER_KEY := server.key +SERVER_CSR := server.csr +SERVER_PEM := server.pem + +# Side-effect file written by openssl when signing (-CAcreateserial); it is +# not a declared target. If deleted, it is recreated on the next re-sign +# (with a reset serial), and `make install` simply skips it when absent. +TLS_SERIAL := rootCA.srl + +.DEFAULT_GOAL := help + +.PHONY: \ + help \ + install \ + check \ + all + +# == phony targets ============================================================= +help: + @echo 'Generate a root CA plus a CA-signed server certificate for the SimDB dashboard.' + @echo 'Replaces scripts/generate-ca-signed-certs.sh.' + @echo '' + @echo 'Files are built here; `make install` copies to ../../docker/nginx/tls.' + @echo '' + @echo 'Dependency graph:' + @echo '' + @echo ' san.cnf.in -> san.cnf' + @echo ' rootCA.key -> rootCA.pem' + @echo ' server.key + san.cnf -> server.csr' + @echo ' server.csr + rootCA.key + san.cnf -> server.pem' + @echo '' + @echo 'Generated files are never overwritten if they exist. Remove a file to rebuild' + @echo 'it and its dependents (e.g., rm rootCA.key rebuilds the entire chain).' + @echo '' + @echo 'Targets (default: help):' + @echo ' make Show this help' + @echo ' make all Build all/missing files' + @echo ' make check Check certificate chain and key/cert pairs' + @echo ' make install Build, then copy files to docker/nginx/tls' + @echo '' + @echo 'Environment variables used:' + @echo ' DOMAIN_NAME=$(DOMAIN_NAME)' + @echo ' CERT_DAYS=$(CERT_DAYS) CA_DAYS=$(CA_DAYS)' + @echo ' CERT_CN=$(CERT_CN) CA_CN=$(CA_CN)' + @echo ' RSA_BITS=$(RSA_BITS)' + +check-$(SERVER_PEM): check-%: $(ROOTCA_PEM) % + @$(OPENSSL) verify -CAfile '$(ROOTCA_PEM)' '$(SERVER_PEM)' >/dev/null || { echo "FAIL: server.pem does not chain up to rootCA.pem"; exit 1; } + +check-$(ROOTCA_KEY): check-%: $(ROOTCA_PEM) % + @ca_pub="$$( $(OPENSSL) x509 -in '$(ROOTCA_PEM)' -noout -pubkey | $(OPENSSL) sha256 )"; \ + ca_key_pub="$$( $(OPENSSL) pkey -in '$(ROOTCA_KEY)' -pubout | $(OPENSSL) sha256 )"; \ + [ "$$ca_pub" = "$$ca_key_pub" ] || { echo "FAIL: rootCA.pem does not match rootCA.key (re-run make all)"; exit 1; } + +check-$(SERVER_KEY): check-%: $(SERVER_PEM) % + @sv_pub="$$( $(OPENSSL) x509 -in '$(SERVER_PEM)' -noout -pubkey | $(OPENSSL) sha256 )"; \ + sv_key_pub="$$( $(OPENSSL) pkey -in '$(SERVER_KEY)' -pubout | $(OPENSSL) sha256 )"; \ + [ "$$sv_pub" = "$$sv_key_pub" ] || { echo "FAIL: server.pem does not match server.key (re-run make all)"; exit 1; } + +check: check-$(SERVER_PEM) check-$(ROOTCA_KEY) check-$(SERVER_KEY) + @echo "check: OK (chain and key/cert pairs match)" + +install: all + @mkdir -p '$(TLS_DIR)' + @install -m 600 '$(ROOTCA_KEY)' '$(SERVER_KEY)' '$(TLS_DIR)/' + @install -m 644 '$(ROOTCA_PEM)' '$(SERVER_PEM)' '$(SERVER_CSR)' '$(SAN_CNF)' '$(TLS_DIR)/' + @if [ -f '$(TLS_SERIAL)' ]; then install -m 644 '$(TLS_SERIAL)' '$(TLS_DIR)/'; fi + @echo "Installed TLS files into $(TLS_DIR):" + @echo " rootCA.pem - distribute to clients (trust anchor)" + @echo " rootCA.key - keep secret (only used to sign)" + @echo " server.pem / server.key - used by nginx (server-https.conf)" + @echo " server.csr / san.cnf / rootCA.srl - introspection / serial" + +all: $(ROOTCA_PEM) $(SERVER_PEM) + @echo "All TLS files are up to date." + +# == generated file targets ==================================================== +# OpenSSL config carrying CN, SANs and X.509 v3 extensions, generated from +# $(SAN_CNF_IN) by substituting @CERT_CN@ and @DOMAIN_NAME@. Built when +# missing only; config values and SANs are applied at this point. The +# template is an order-only prerequisite, so editing it (or a checkout / +# `touch Makefile`) never churns the certificates. +$(SAN_CNF): | $(SAN_CNF_IN) + @echo "Writing $@ from $(notdir $(SAN_CNF_IN)) ..." + @sed \ + -e 's|@CERT_CN@|$(CERT_CN)|g' \ + -e 's|@DOMAIN_NAME@|$(DOMAIN_NAME)|g' \ + '$(SAN_CNF_IN)' > '$@' + +# Root CA private key: generated once, never overwritten. +$(ROOTCA_KEY): + @echo "Generating root CA private key ..." + @$(OPENSSL) genrsa -out '$@' '$(RSA_BITS)' 2>/dev/null + @chmod 600 '$@' + +# Root CA certificate, derived from the key. Order-only prerequisite on the +# config: a changed CN/SAN does not rotate the trust anchor, but re-deriving +# the certificate from the same key keeps the same public key (existing +# server certs and client trust stay valid). +$(ROOTCA_PEM): $(ROOTCA_KEY) | $(SAN_CNF) + @echo "Deriving root CA certificate from the key ..." + @$(OPENSSL) req -x509 -new \ + -key '$(ROOTCA_KEY)' \ + -sha256 \ + -days '$(CA_DAYS)' \ + -subj '/CN=$(CA_CN)' \ + -extensions v3_ca \ + -config '$(SAN_CNF)' \ + -out '$@' + @chmod 644 '$@' + +# Server private key: generated once, never overwritten. +$(SERVER_KEY): + @echo "Generating server private key ..." + @$(OPENSSL) genrsa -out '$@' '$(RSA_BITS)' 2>/dev/null + @chmod 600 '$@' + +# Certificate signing request, derived from the server key + config. +$(SERVER_CSR): $(SERVER_KEY) $(SAN_CNF) + @echo "Generating server certificate signing request ..." + @$(OPENSSL) req -new \ + -key '$(SERVER_KEY)' \ + -sha256 \ + -config '$(SAN_CNF)' \ + -out '$@' + +# Server certificate, signed by the root CA. Order-only prerequisite on the +# CA certificate: re-deriving rootCA.pem from the same key does not force a +# re-sign, but a brand-new rootCA.key (new CA) does. +$(SERVER_PEM): $(SERVER_CSR) $(ROOTCA_KEY) $(SAN_CNF) | $(ROOTCA_PEM) + @echo "Signing server certificate with the root CA ..." + @$(OPENSSL) x509 \ + -req \ + -in '$(SERVER_CSR)' \ + -CA '$(ROOTCA_PEM)' \ + -CAkey '$(ROOTCA_KEY)' \ + -CAcreateserial \ + -sha256 \ + -days '$(CERT_DAYS)' \ + -extfile '$(SAN_CNF)' \ + -extensions v3_req \ + -out '$@' + @chmod 644 '$@' diff --git a/scripts/certs/san.cnf.in b/scripts/certs/san.cnf.in new file mode 100644 index 0000000..58131fe --- /dev/null +++ b/scripts/certs/san.cnf.in @@ -0,0 +1,35 @@ +# OpenSSL configuration for the SimDB dashboard TLS build. +# Generated from san.cnf.in by scripts/certs/Makefile: the subject CN / +# primary DNS SAN and the secondary DNS name are substituted at build time; +# the localhost + loopback SANs below are static. +[req] +distinguished_name = dn +req_extensions = req_ext +x509_extensions = v3_ca +prompt = no + +[dn] +CN = @CERT_CN@ + +[req_ext] +subjectAltName = @alt_names + +[v3_ca] +basicConstraints = critical,CA:TRUE +keyUsage = critical,keyCertSign,cRLSign +subjectKeyIdentifier = hash + +[v3_req] +basicConstraints = critical,CA:FALSE +keyUsage = critical,digitalSignature,keyEncipherment +extendedKeyUsage = serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +subjectAltName = @alt_names + +[alt_names] +DNS.1 = @CERT_CN@ +DNS.2 = @DOMAIN_NAME@ +DNS.3 = localhost +IP.1 = 127.0.0.1 +IP.2 = 0:0:0:0:0:0:0:1 diff --git a/scripts/generate-self-signed-certs.sh b/scripts/generate-self-signed-certs.sh deleted file mode 100755 index 7984f3a..0000000 --- a/scripts/generate-self-signed-certs.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env sh -set -eu - -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) -REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) -# Directory where generated TLS assets are to be stored. -SSL_DIR="$REPO_ROOT/docker/nginx/ssl" -CERT_FILE="$SSL_DIR/cert.crt" -KEY_FILE="$SSL_DIR/cert.key" -# Temporary OpenSSL config file used for SAN and extensions. -CNF_FILE="$SSL_DIR/san.cnf" - -# Certificate validity period in days. -CERT_DAYS="${CERT_DAYS:-3650}" - -# Primary DNS name for SAN/CN; auto-detected with localhost fallback. -DOMAIN_NAME="${DOMAIN_NAME:-$(hostname -f 2>/dev/null || hostname --fqdn 2>/dev/null || hostname 2>/dev/null || echo localhost)}" -# Certificate common name; defaults to DOMAIN_NAME. -CERT_CN=${CERT_CN:-$DOMAIN_NAME} - -if ! command -v openssl >/dev/null 2>&1; then - echo "Error: openssl is required but was not found in PATH." >&2 - exit 1 -fi - -mkdir -p "$SSL_DIR" - -cat > "$CNF_FILE" < Date: Mon, 31 Aug 2026 10:19:09 +0200 Subject: [PATCH 28/29] nginx: add FYI comment, remove double log output --- docker/nginx/templates/default.conf.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/nginx/templates/default.conf.template b/docker/nginx/templates/default.conf.template index 4d97cf7..9a456ca 100644 --- a/docker/nginx/templates/default.conf.template +++ b/docker/nginx/templates/default.conf.template @@ -2,10 +2,10 @@ # are envsubst-processed into /etc/nginx/conf.d at container startup. # See: https://github.com/docker-library/docs/tree/master/nginx -# Log to container stdout/stderr so logs are visible via `docker compose logs`. -# Change `info` to `debug` for maximum verbosity (requires nginx debug build). -error_log /dev/stderr info; -access_log /dev/stdout; +# FYI: the official nginx image already has /var/log/nginx/access.log symlinked to /dev/stdout, +# and the default /etc/nginx/nginx.conf reads: +# +# access_log /var/log/nginx/access.log main; # Hostnames inside upstream blocks are resolved at config-load time using the # system resolver, which does read /etc/hosts. From 0ad14d7b90be278c068fcb4e2f8d5205ab752a85 Mon Sep 17 00:00:00 2001 From: Louwrens van Dellen Date: Mon, 31 Aug 2026 11:51:10 +0200 Subject: [PATCH 29/29] nginx: bugfix simdb backend at prefix /scenarios/api --- .../templates/snippets/location-simdb_proxy.conf.template | 3 +++ docs/installation.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docker/nginx/templates/snippets/location-simdb_proxy.conf.template b/docker/nginx/templates/snippets/location-simdb_proxy.conf.template index 794399f..aaf9e11 100644 --- a/docker/nginx/templates/snippets/location-simdb_proxy.conf.template +++ b/docker/nginx/templates/snippets/location-simdb_proxy.conf.template @@ -11,5 +11,8 @@ location ~ "^/(?:scenarios/api|scenarios|api|swaggerui).*$" { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + # inform simdb backend to add this prefix in their responses + proxy_set_header X-Forwarded-Prefix /scenarios/api; + proxy_pass http://simdb_backend; } diff --git a/docs/installation.md b/docs/installation.md index 519b5cd..957751b 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -95,7 +95,7 @@ make down service up Notes: -- Requests under `/scenarios/api/` are proxied by nginx to a simdb server expected at `API_HOST:API_PORT` (defaults to `host.docker.internal:5000`). +- Requests under `/scenarios/api/` are proxied by nginx to a simdb server expected at `API_HOST:API_PORT` (defaults to `host.docker.internal:5000`). nginx sends `X-Forwarded-Prefix: /scenarios/api`; SimDB should trust respect header (`ProxyFix(..., x_prefix=1)`) so absolute URLs it returns retain the public prefix. - You can start multiple dashboards if you change the host port with `DASHBOARD_PORT`. - The HTTPS compose override also publishes `DASHBOARD_HTTPS_PORT` (default `443`) and switches `SERVER_CONF` to `server-https.conf`. - `docker-compose.https.yml` reuses the base `docker-compose.yml`; environment variables from the base file are inherited, and override entries only add new variables or replace matching keys such as `SERVER_CONF`.