From 04c1503d4a030c73e8e457ab0161343d3dea1b4f Mon Sep 17 00:00:00 2001 From: Lee <1633292@qq.com> Date: Wed, 19 Aug 2026 19:07:38 +0800 Subject: [PATCH] ops: serve TraceCD and Gitea over shared HTTPS --- deploy/README.md | 20 +++++++++++++---- deploy/compose.yaml | 3 ++- deploy/nginx-sni.conf | 14 ++++++++++++ deploy/nginx-tracecd.conf | 47 ++++++++++++++++++++++++++++++++++++++- deploy/stack.env.example | 2 +- 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 deploy/nginx-sni.conf diff --git a/deploy/README.md b/deploy/README.md index a5dbd50..44bfe80 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -5,20 +5,29 @@ The production stack lives at `/opt/tracecd` on `156.238.122.140` and is sized f ## Services - MySQL 8.0.40: Docker-only `tracecd-net`; no host port is published. -- Gitea 1.22.6: host ports 3000 and 2222 are bound to loopback only until an authenticated public access method is chosen. +- Gitea 1.22.6: HTTPS at `git.1633292.cn`; host ports 3000 and 2222 remain bound to loopback. - Gitea Actions runner: capacity 1, label `centos-env`, Docker socket access. - TraceCD: built by Gitea Actions, bound to `127.0.0.1:8080` and served by host Nginx. -- Nginx: `t.1633292.cn` on port 80. Port 443 remains owned by Xray. +- Nginx: HTTPS for `t.1633292.cn` and `git.1633292.cn`. Its stream module owns public port 443 and routes these two SNI names to local TLS virtual hosts; all other SNI traffic passes unchanged to Xray on `127.0.0.1:7443`. ## Bootstrap -Copy `compose.yaml`, `runner-config.yaml`, `nginx-tracecd.conf`, and `bootstrap-env.sh` to `/opt/tracecd`. Then run: +Copy `compose.yaml`, `runner-config.yaml`, `nginx-tracecd.conf`, `nginx-sni.conf`, and `bootstrap-env.sh` to `/opt/tracecd`. Install `nginx-mod-stream` and `certbot`, issue certificates for both hostnames, move Xray's listener to `127.0.0.1:7443`, and add this top-level block to `/etc/nginx/nginx.conf`: + +```nginx +stream { + include /etc/nginx/stream.d/*.conf; +} +``` + +Then run: ```bash cd /opt/tracecd chmod 700 bootstrap-env.sh ./bootstrap-env.sh install -m 644 nginx-tracecd.conf /etc/nginx/conf.d/tracecd.conf +install -D -m 644 nginx-sni.conf /etc/nginx/stream.d/sni.conf nginx -t && systemctl reload nginx docker compose pull docker compose up -d @@ -33,7 +42,10 @@ cd /opt/tracecd docker compose ps docker compose logs --tail 100 mysql gitea runner docker stats --no-stream -curl -I -H 'Host: t.1633292.cn' http://127.0.0.1/ +curl -I https://t.1633292.cn/login +curl -I https://git.1633292.cn/user/login ``` +Certbot's packaged systemd timer renews the certificates. A successful renewal must be followed by `systemctl reload nginx`; `/etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh` provides that hook on the server. + The workflow in `.gitea/workflows/deploy.yaml` builds on pushes to `dev`, joins the application to `tracecd-net`, loads `/opt/tracecd/app.env`, and replaces only `trace-container`. diff --git a/deploy/compose.yaml b/deploy/compose.yaml index 30e558c..f45d840 100644 --- a/deploy/compose.yaml +++ b/deploy/compose.yaml @@ -48,7 +48,8 @@ services: GITEA__database__PASSWD: ${MYSQL_ROOT_PASSWORD} GITEA__server__DOMAIN: ${GITEA_HOST} GITEA__server__SSH_DOMAIN: ${GITEA_HOST} - GITEA__server__ROOT_URL: http://${GITEA_HOST}:3000/ + GITEA__server__ROOT_URL: https://${GITEA_HOST}/ + GITEA__server__LOCAL_ROOT_URL: http://gitea:3000/ GITEA__server__SSH_PORT: 2222 GITEA__service__DISABLE_REGISTRATION: "true" GITEA__actions__ENABLED: "true" diff --git a/deploy/nginx-sni.conf b/deploy/nginx-sni.conf new file mode 100644 index 0000000..00589aa --- /dev/null +++ b/deploy/nginx-sni.conf @@ -0,0 +1,14 @@ +map $ssl_preread_server_name $sni_backend { + t.1633292.cn 127.0.0.1:8443; + git.1633292.cn 127.0.0.1:8444; + default 127.0.0.1:7443; +} + +server { + listen 443; + listen [::]:443; + proxy_pass $sni_backend; + ssl_preread on; + proxy_connect_timeout 5s; + proxy_timeout 1h; +} diff --git a/deploy/nginx-tracecd.conf b/deploy/nginx-tracecd.conf index 8d1f002..fa71376 100644 --- a/deploy/nginx-tracecd.conf +++ b/deploy/nginx-tracecd.conf @@ -1,8 +1,28 @@ server { listen 80; listen [::]:80; + server_name t.1633292.cn git.1633292.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/lib/letsencrypt; + default_type text/plain; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 127.0.0.1:8443 ssl; server_name t.1633292.cn; + ssl_certificate /etc/letsencrypt/live/t.1633292.cn/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/t.1633292.cn/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_session_cache shared:tracecd_tls:1m; + ssl_session_timeout 10m; + client_max_body_size 10m; location / { @@ -11,7 +31,32 @@ server { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Proto https; + proxy_connect_timeout 10s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } +} + +server { + listen 127.0.0.1:8444 ssl; + server_name git.1633292.cn; + + ssl_certificate /etc/letsencrypt/live/git.1633292.cn/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/git.1633292.cn/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_session_cache shared:gitea_tls:1m; + ssl_session_timeout 10m; + + client_max_body_size 100m; + + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; proxy_connect_timeout 10s; proxy_read_timeout 300s; proxy_send_timeout 300s; diff --git a/deploy/stack.env.example b/deploy/stack.env.example index 5953de7..0b09035 100644 --- a/deploy/stack.env.example +++ b/deploy/stack.env.example @@ -1,4 +1,4 @@ # Copy to stack.env on the server and keep mode 0600. MYSQL_ROOT_PASSWORD=replace-with-a-random-value MYSQL_PASSWORD=replace-with-a-different-random-value -GITEA_HOST=156.238.122.140 +GITEA_HOST=git.1633292.cn