From 918615831314f0ced4f1c049bc611b7a49ec0002 Mon Sep 17 00:00:00 2001 From: David Schroeder Date: Wed, 26 Aug 2026 17:33:22 -0500 Subject: [PATCH] update nginx --- README.md | 23 ++++++++++++++++ compose.http.yaml | 2 +- compose.tls.yaml | 2 +- compose.yaml | 3 ++- config/.gitignore | 4 +++ config/nginx/.gitkeep | 1 + config/nginx/sites/.gitkeep | 1 + deploy/nginx/templates/http.conf.template | 3 +++ deploy/nginx/templates/tls.conf.template | 3 +++ install.sh | 1 + manage.sh | 25 +++++++++++++++--- tests/test-deployment-scripts.sh | 32 +++++++++++++++++++++++ 12 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 config/nginx/.gitkeep create mode 100644 config/nginx/sites/.gitkeep diff --git a/README.md b/README.md index 8272a9f..4bea19b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,29 @@ deployment to Let's Encrypt: ./manage.sh start ``` +## Nginx configuration + +At first `install.sh` or `manage.sh` run, the deployment copies its editable +Nginx templates to `config/nginx/`. Those local files are never overwritten by +an update: + +- `bootstrap.conf.template` serves the Let's Encrypt HTTP-01 challenge. +- `http.conf.template` is used only in `SSL_MODE=none` test mode. +- `tls.conf.template` is the active production HTTPS reverse-proxy template. +- `sites/*.conf` is included by the HTTP and TLS templates for additional + virtual hosts. + +After editing a template or site snippet, recreate Nginx to render templates +and apply the configuration: + +```sh +./manage.sh reload-nginx +``` + +The command selects the active HTTP or TLS configuration and validates Nginx +after recreating it. Keep certificates in `config/ssl` or `config/letsencrypt`; +they remain read-only mounts separate from the editable Nginx configuration. + ## Local checks ```sh diff --git a/compose.http.yaml b/compose.http.yaml index 7701eb1..0d80238 100644 --- a/compose.http.yaml +++ b/compose.http.yaml @@ -11,4 +11,4 @@ services: nginx: volumes: - - ./deploy/nginx/templates/http.conf.template:/etc/nginx/templates/default.conf.template:ro + - ./config/nginx/http.conf.template:/etc/nginx/templates/default.conf.template:ro diff --git a/compose.tls.yaml b/compose.tls.yaml index f0ec23b..0cdcb8f 100644 --- a/compose.tls.yaml +++ b/compose.tls.yaml @@ -3,7 +3,7 @@ services: ports: - "${HTTPS_PORT:-8643}:443" volumes: - - ./deploy/nginx/templates/tls.conf.template:/etc/nginx/templates/default.conf.template:ro + - ./config/nginx/tls.conf.template:/etc/nginx/templates/default.conf.template:ro - ${SSL_CERTIFICATE_DIR:-./config/ssl}:/etc/nginx/ssl:ro # Certbot's live certificates are symlinks into ../../archive. - ./config/letsencrypt/archive:/etc/archive:ro diff --git a/compose.yaml b/compose.yaml index 93d209d..356df8a 100644 --- a/compose.yaml +++ b/compose.yaml @@ -79,7 +79,8 @@ services: ports: - "${HTTP_PORT:-8680}:80" volumes: - - ./deploy/nginx/templates/bootstrap.conf.template:/etc/nginx/templates/default.conf.template:ro + - ./config/nginx/bootstrap.conf.template:/etc/nginx/templates/default.conf.template:ro + - ./config/nginx/sites:/etc/nginx/sites:ro - ./config/certbot-webroot:/var/www/certbot:ro environment: BROKER_DOMAIN: ${BROKER_DOMAIN} diff --git a/config/.gitignore b/config/.gitignore index e7a0e1c..05883f1 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -15,5 +15,9 @@ !ssl/.gitkeep !certbot-webroot/ !certbot-webroot/.gitkeep +!nginx/ +!nginx/.gitkeep +!nginx/sites/ +!nginx/sites/.gitkeep !backups/ !backups/.gitkeep diff --git a/config/nginx/.gitkeep b/config/nginx/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/config/nginx/.gitkeep @@ -0,0 +1 @@ + diff --git a/config/nginx/sites/.gitkeep b/config/nginx/sites/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/config/nginx/sites/.gitkeep @@ -0,0 +1 @@ + diff --git a/deploy/nginx/templates/http.conf.template b/deploy/nginx/templates/http.conf.template index e8a6778..36e63af 100644 --- a/deploy/nginx/templates/http.conf.template +++ b/deploy/nginx/templates/http.conf.template @@ -57,6 +57,9 @@ server { } } +# Local virtual-host snippets are mounted from config/nginx/sites. +include /etc/nginx/sites/*.conf; + server { listen 80; server_name ${GITEA_DOMAIN}; diff --git a/deploy/nginx/templates/tls.conf.template b/deploy/nginx/templates/tls.conf.template index 2c8ad11..9a14f08 100644 --- a/deploy/nginx/templates/tls.conf.template +++ b/deploy/nginx/templates/tls.conf.template @@ -43,6 +43,9 @@ server { } } +# Local virtual-host snippets are mounted from config/nginx/sites. +include /etc/nginx/sites/*.conf; + server { listen 443 ssl default_server; server_name _; diff --git a/install.sh b/install.sh index 9417553..8f3adca 100755 --- a/install.sh +++ b/install.sh @@ -36,6 +36,7 @@ for required_file in \ compose.yaml \ compose.http.yaml \ compose.tls.yaml \ + deploy/nginx/templates/bootstrap.conf.template \ deploy/nginx/templates/http.conf.template \ deploy/nginx/templates/tls.conf.template; do [[ -f "$required_file" ]] || diff --git a/manage.sh b/manage.sh index 6a4bc20..a13dcfb 100755 --- a/manage.sh +++ b/manage.sh @@ -79,12 +79,13 @@ require_regular_file() { require_stack_files() { require_regular_file compose.yaml + require_regular_file deploy/nginx/templates/bootstrap.conf.template + require_regular_file deploy/nginx/templates/http.conf.template + require_regular_file deploy/nginx/templates/tls.conf.template if [[ "$SSL_MODE" == "none" ]]; then require_regular_file compose.http.yaml - require_regular_file deploy/nginx/templates/http.conf.template else require_regular_file compose.tls.yaml - require_regular_file deploy/nginx/templates/tls.conf.template fi } @@ -197,6 +198,7 @@ prepare() { config/gitea/config \ config/letsencrypt \ config/letsencrypt/archive \ + config/nginx/sites \ config/ssl \ config/certbot-webroot \ config/backups @@ -204,6 +206,16 @@ prepare() { if [[ "$(id -u)" == 0 ]]; then chown -R 1000:1000 config/broker config/gitea fi + for template_name in bootstrap http tls; do + if [[ -e "config/nginx/${template_name}.conf.template" ]]; then + [[ -f "config/nginx/${template_name}.conf.template" ]] || + fail "config/nginx/${template_name}.conf.template must be a regular file" + else + install -m 0644 \ + "deploy/nginx/templates/${template_name}.conf.template" \ + "config/nginx/${template_name}.conf.template" + fi + done docker network inspect tapm-edge >/dev/null 2>&1 || docker network create tapm-edge >/dev/null } @@ -380,6 +392,13 @@ renew) docker compose --profile tools run --rm certbot renew --quiet compose_stack exec nginx nginx -s reload ;; +reload-nginx) + require_env + prepare + compose_stack up -d --force-recreate nginx + compose_stack exec nginx nginx -t + printf 'Nginx configuration was recreated and validated.\n' + ;; backup) require_env prepare @@ -420,7 +439,7 @@ broker-logs) compose_stack logs --tail=100 broker ;; *) - printf 'Usage: %s {prepare|bootstrap|configure-broker|rotate-cookie-secret|set-ssl|start|start-direct|create-admin|renew|backup|status|diagnostics}\n' "$0" +printf 'Usage: %s {prepare|bootstrap|configure-broker|rotate-cookie-secret|set-ssl|start|start-direct|create-admin|renew|reload-nginx|backup|status|diagnostics}\n' "$0" exit 2 ;; esac diff --git a/tests/test-deployment-scripts.sh b/tests/test-deployment-scripts.sh index 901e2a1..135dc25 100755 --- a/tests/test-deployment-scripts.sh +++ b/tests/test-deployment-scripts.sh @@ -82,6 +82,16 @@ for template in \ } done done +grep -F -- './config/nginx/bootstrap.conf.template:/etc/nginx/templates/default.conf.template:ro' \ + "${TEST_ROOT}/compose.yaml" >/dev/null || { + printf 'FAIL: Nginx bootstrap template is not deployment-local\n' >&2 + exit 1 +} +grep -F -- './config/nginx/sites:/etc/nginx/sites:ro' \ + "${TEST_ROOT}/compose.yaml" >/dev/null || { + printf 'FAIL: Nginx site snippets are not mounted from configuration\n' >&2 + exit 1 +} printf '%s\n' \ 'broker.test' \ @@ -105,6 +115,21 @@ assert_env 'TAPM_GITEA_URL=http://git.test:8780' assert_env 'TAPM_GITEA_INTERNAL_URL=http://gitea:3000' assert_env 'GITEA_PUBLIC_URL=http://git.test:8780/' assert_env 'TAPM_DEFAULT_DURATION=7d' +for template_name in bootstrap http tls; do + cmp "${TEST_ROOT}/deploy/nginx/templates/${template_name}.conf.template" \ + "${deployment}/config/nginx/${template_name}.conf.template" || { + printf 'FAIL: Nginx %s template was not seeded into config/nginx\n' "$template_name" >&2 + exit 1 + } +done +printf '%s\n' '# local Nginx customization' \ + >"${deployment}/config/nginx/http.conf.template" +"${deployment}/manage.sh" prepare >/dev/null +grep -Fx '# local Nginx customization' \ + "${deployment}/config/nginx/http.conf.template" >/dev/null || { + printf 'FAIL: Nginx configuration was overwritten during prepare\n' >&2 + exit 1 +} grep -F -- '-f compose.yaml -f compose.http.yaml up -d gitea nginx' \ "$MOCK_DOCKER_LOG" >/dev/null || { printf 'FAIL: HTTP bootstrap did not select compose.http.yaml\n' >&2 @@ -135,6 +160,13 @@ assert_env 'GITEA_PUBLIC_URL=https://git.test/' assert_env 'SSL_MODE=none' assert_env 'TAPM_PUBLIC_URL=http://broker.test:8780' +"${deployment}/manage.sh" reload-nginx >/dev/null +grep -F -- '-f compose.yaml -f compose.http.yaml up -d --force-recreate nginx' \ + "$MOCK_DOCKER_LOG" >/dev/null || { + printf 'FAIL: Nginx reload did not use the active HTTP stack\n' >&2 + exit 1 +} + "${deployment}/manage.sh" rotate-cookie-secret >/dev/null rotated_secret="$( sed -n 's/^TAPM_COOKIE_SECRET=//p' "${deployment}/.env"