diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..5a48c96 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,89 @@ +# Local Kanban Nginx + +## Current User-Mode HTTPS Proxy + +This repo includes a user-mode Nginx config that does not require sudo: + +```bash +nginx -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf \ + -p /home/gahow/projects/kanban/.nginx +``` + +It listens on: + +```text +https://gahow-pc.ipads-lab.se.sjtu.edu.cn:8443 +``` + +It uses the self-signed certificate under `deploy/certs/`, so browsers will show a certificate warning unless the certificate is trusted locally. + +Reload it with: + +```bash +nginx -s reload \ + -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf \ + -p /home/gahow/projects/kanban/.nginx +``` + +Stop it with: + +```bash +nginx -s stop \ + -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf \ + -p /home/gahow/projects/kanban/.nginx +``` + +## Production 443 Proxy + +For port 443, install `deploy/nginx.local-kanban.conf` into the system Nginx site directory and use a trusted certificate. The root domain serves the portal page, and individual apps keep their own ports: + +```bash +sudo cp deploy/nginx.local-kanban.conf /etc/nginx/sites-available/local-kanban +sudo ln -sf /etc/nginx/sites-available/local-kanban /etc/nginx/sites-enabled/local-kanban +sudo nginx -t +sudo systemctl reload nginx +``` + +The install script intentionally does not bind port 80 by default, because another local service may already own it. Enable HTTP-to-HTTPS redirect only when port 80 is free: + +```bash +KANBAN_ENABLE_HTTP_REDIRECT=1 ./scripts/install-nginx-system.sh +``` + +The Node app should run only on localhost behind Nginx: + +```bash +KANBAN_HOST=127.0.0.1 \ +KANBAN_PORT=5180 \ +KANBAN_AUTH_SECURE_COOKIE=1 \ +KANBAN_REQUIRE_HTTPS=1 \ +npm start +``` + +Kanban is then normally reached through the user-mode HTTPS proxy on: + +```text +https://gahow-pc.ipads-lab.se.sjtu.edu.cn:8443/ +``` + +## Persistent User Services + +Use the user-level systemd units when the service should stay up without an open shell: + +```bash +./scripts/install-user-systemd.sh +``` + +This installs and starts: + +```text +local-kanban.service +local-kanban-nginx-8443.service +``` + +Check them with: + +```bash +systemctl --user status local-kanban.service +systemctl --user status local-kanban-nginx-8443.service +``` diff --git a/deploy/nginx.local-kanban.conf b/deploy/nginx.local-kanban.conf new file mode 100644 index 0000000..fa3edf8 --- /dev/null +++ b/deploy/nginx.local-kanban.conf @@ -0,0 +1,89 @@ +limit_req_zone $binary_remote_addr zone=kanban_login:10m rate=5r/m; + +server { + listen 443 ssl http2; + server_name gahow-pc.ipads-lab.se.sjtu.edu.cn; + root /var/www/local-kanban-portal; + index index.html; + + ssl_certificate /etc/nginx/certs/gahow-pc.fullchain.pem; + ssl_certificate_key /etc/nginx/certs/gahow-pc.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + + add_header Strict-Transport-Security "max-age=31536000" always; + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer" always; + + client_max_body_size 1m; + + location / { + try_files $uri $uri/ /index.html; + } +} + +server { + listen 3443 ssl http2; + server_name gahow-pc.ipads-lab.se.sjtu.edu.cn; + + ssl_certificate /etc/nginx/certs/gahow-pc.fullchain.pem; + ssl_certificate_key /etc/nginx/certs/gahow-pc.key; + ssl_protocols TLSv1.2 TLSv1.3; + + location / { + proxy_pass http://127.0.0.1:3300; + proxy_http_version 1.1; + proxy_set_header Host $host:3443; + 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; + } +} + +server { + listen 5443 ssl http2; + server_name gahow-pc.ipads-lab.se.sjtu.edu.cn; + + ssl_certificate /etc/nginx/certs/gahow-pc.fullchain.pem; + ssl_certificate_key /etc/nginx/certs/gahow-pc.key; + ssl_protocols TLSv1.2 TLSv1.3; + + location /api/ { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header Host $host:5443; + 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; + } + + location / { + proxy_pass http://127.0.0.1:5174; + proxy_http_version 1.1; + proxy_set_header Host $host:5443; + 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_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} + +server { + listen 8788 ssl http2; + server_name gahow-pc.ipads-lab.se.sjtu.edu.cn; + + ssl_certificate /etc/nginx/certs/gahow-pc.fullchain.pem; + ssl_certificate_key /etc/nginx/certs/gahow-pc.key; + ssl_protocols TLSv1.2 TLSv1.3; + + location / { + proxy_pass http://127.0.0.1:8787; + proxy_http_version 1.1; + proxy_set_header Host $host:8788; + 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; + } +} diff --git a/deploy/nginx.user-8443.conf b/deploy/nginx.user-8443.conf new file mode 100644 index 0000000..c4ae32b --- /dev/null +++ b/deploy/nginx.user-8443.conf @@ -0,0 +1,57 @@ +worker_processes 1; +pid /home/gahow/projects/kanban/.nginx/nginx.pid; + +events { + worker_connections 512; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /home/gahow/projects/kanban/.nginx/logs/access.log; + error_log /home/gahow/projects/kanban/.nginx/logs/error.log warn; + + client_body_temp_path /home/gahow/projects/kanban/.nginx/client_body_temp; + proxy_temp_path /home/gahow/projects/kanban/.nginx/proxy_temp; + fastcgi_temp_path /home/gahow/projects/kanban/.nginx/fastcgi_temp; + uwsgi_temp_path /home/gahow/projects/kanban/.nginx/uwsgi_temp; + scgi_temp_path /home/gahow/projects/kanban/.nginx/scgi_temp; + + limit_req_zone $binary_remote_addr zone=kanban_login_user:10m rate=5r/m; + + server { + listen 8443 ssl; + server_name gahow-pc.ipads-lab.se.sjtu.edu.cn; + + ssl_certificate /home/gahow/projects/kanban/deploy/certs/local-kanban.crt; + ssl_certificate_key /home/gahow/projects/kanban/deploy/certs/local-kanban.key; + ssl_protocols TLSv1.2 TLSv1.3; + + add_header Strict-Transport-Security "max-age=31536000" always; + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer" always; + + client_max_body_size 1m; + + location = /api/auth/login { + limit_req zone=kanban_login_user burst=5 nodelay; + proxy_pass http://127.0.0.1:5180; + 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; + } + + location / { + proxy_pass http://127.0.0.1:5180; + 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; + } + } +} diff --git a/deploy/systemd/local-kanban-nginx-8443.service b/deploy/systemd/local-kanban-nginx-8443.service new file mode 100644 index 0000000..6590a60 --- /dev/null +++ b/deploy/systemd/local-kanban-nginx-8443.service @@ -0,0 +1,18 @@ +[Unit] +Description=Local Kanban user-mode HTTPS proxy on 8443 +After=network-online.target local-kanban.service +Requires=local-kanban.service + +[Service] +Type=forking +WorkingDirectory=/home/gahow/projects/kanban +PIDFile=/home/gahow/projects/kanban/.nginx/nginx.pid +ExecStartPre=/usr/bin/mkdir -p /home/gahow/projects/kanban/.nginx/logs /home/gahow/projects/kanban/.nginx/client_body_temp /home/gahow/projects/kanban/.nginx/proxy_temp /home/gahow/projects/kanban/.nginx/fastcgi_temp /home/gahow/projects/kanban/.nginx/uwsgi_temp /home/gahow/projects/kanban/.nginx/scgi_temp +ExecStart=/usr/sbin/nginx -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf -p /home/gahow/projects/kanban/.nginx +ExecReload=/usr/sbin/nginx -s reload -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf -p /home/gahow/projects/kanban/.nginx +ExecStop=/usr/sbin/nginx -s quit -c /home/gahow/projects/kanban/deploy/nginx.user-8443.conf -p /home/gahow/projects/kanban/.nginx +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=default.target diff --git a/deploy/systemd/local-kanban.service b/deploy/systemd/local-kanban.service new file mode 100644 index 0000000..8e725e7 --- /dev/null +++ b/deploy/systemd/local-kanban.service @@ -0,0 +1,14 @@ +[Unit] +Description=Local Kanban Node app +After=network-online.target + +[Service] +Type=simple +WorkingDirectory=/home/gahow/projects/kanban +ExecStart=/home/gahow/projects/kanban/scripts/start-kanban.sh +Restart=always +RestartSec=3 +Environment=NODE_ENV=production + +[Install] +WantedBy=default.target diff --git a/scripts/install-nginx-system.sh b/scripts/install-nginx-system.sh new file mode 100755 index 0000000..7512e64 --- /dev/null +++ b/scripts/install-nginx-system.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +DOMAIN="${KANBAN_DOMAIN:-gahow-pc.ipads-lab.se.sjtu.edu.cn}" +UPSTREAM_PORT="${KANBAN_PORT:-5180}" +ENABLE_HTTP_REDIRECT="${KANBAN_ENABLE_HTTP_REDIRECT:-0}" +GITEA_PORT="${KANBAN_GITEA_PORT:-3443}" +STOCK_FRONTEND_PORT="${KANBAN_STOCK_FRONTEND_PORT:-5443}" +STOCK_API_PORT="${KANBAN_STOCK_API_PORT:-8788}" + +if [[ "${EUID}" -ne 0 ]]; then + exec sudo -E bash "$0" "$@" +fi + +mkdir -p /etc/nginx/certs +mkdir -p /var/www/local-kanban-portal +cp "${ROOT_DIR}/deploy/certs/local-kanban.crt" /etc/nginx/certs/gahow-pc.fullchain.pem +cp "${ROOT_DIR}/deploy/certs/local-kanban.key" /etc/nginx/certs/gahow-pc.key +cp "${ROOT_DIR}/portal/index.html" /var/www/local-kanban-portal/index.html +cp "${ROOT_DIR}/portal/portal.css" /var/www/local-kanban-portal/portal.css +rm -f /var/www/local-kanban-portal/portal.js +chmod 600 /etc/nginx/certs/gahow-pc.key + +{ +cat </etc/nginx/sites-available/local-kanban + +ln -sf /etc/nginx/sites-available/local-kanban /etc/nginx/sites-enabled/local-kanban +rm -f /etc/nginx/sites-enabled/default +nginx -t +systemctl reload nginx || systemctl restart nginx + +echo "Nginx portal is configured for https://${DOMAIN}/" +echo "Kanban remains available on its own port, for example https://${DOMAIN}:8443/" +echo "Gitea HTTPS is configured for https://${DOMAIN}:${GITEA_PORT}/" +echo "Stock Agent HTTPS is configured for https://${DOMAIN}:${STOCK_FRONTEND_PORT}/" +echo "Stock Agent API HTTPS is configured for https://${DOMAIN}:${STOCK_API_PORT}/" +if [[ "${ENABLE_HTTP_REDIRECT}" != "1" ]]; then + echo "Port 80 redirect is disabled. Set KANBAN_ENABLE_HTTP_REDIRECT=1 only if port 80 is free." +fi diff --git a/scripts/install-user-systemd.sh b/scripts/install-user-systemd.sh new file mode 100755 index 0000000..50a1fa2 --- /dev/null +++ b/scripts/install-user-systemd.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +USER_UNIT_DIR="${HOME}/.config/systemd/user" + +mkdir -p "${USER_UNIT_DIR}" +install -m 0644 "${ROOT_DIR}/deploy/systemd/local-kanban.service" "${USER_UNIT_DIR}/local-kanban.service" +install -m 0644 "${ROOT_DIR}/deploy/systemd/local-kanban-nginx-8443.service" "${USER_UNIT_DIR}/local-kanban-nginx-8443.service" + +systemctl --user daemon-reload +systemctl --user enable --now local-kanban.service +systemctl --user enable --now local-kanban-nginx-8443.service + +echo "Local Kanban is managed by user systemd:" +echo " systemctl --user status local-kanban.service" +echo " systemctl --user status local-kanban-nginx-8443.service" diff --git a/scripts/start-kanban.sh b/scripts/start-kanban.sh new file mode 100755 index 0000000..e1aa1bc --- /dev/null +++ b/scripts/start-kanban.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "${ROOT_DIR}" + +export KANBAN_HOST="${KANBAN_HOST:-127.0.0.1}" +export KANBAN_PORT="${KANBAN_PORT:-5180}" +export KANBAN_AUTH_SECURE_COOKIE="${KANBAN_AUTH_SECURE_COOKIE:-1}" +export KANBAN_REQUIRE_HTTPS="${KANBAN_REQUIRE_HTTPS:-1}" + +exec npm start diff --git a/scripts/start-user-nginx-8443.sh b/scripts/start-user-nginx-8443.sh new file mode 100755 index 0000000..8bc6a47 --- /dev/null +++ b/scripts/start-user-nginx-8443.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +mkdir -p \ + "${ROOT_DIR}/.nginx/logs" \ + "${ROOT_DIR}/.nginx/client_body_temp" \ + "${ROOT_DIR}/.nginx/proxy_temp" \ + "${ROOT_DIR}/.nginx/fastcgi_temp" \ + "${ROOT_DIR}/.nginx/uwsgi_temp" \ + "${ROOT_DIR}/.nginx/scgi_temp" + +if [[ -f "${ROOT_DIR}/.nginx/nginx.pid" ]] && kill -0 "$(cat "${ROOT_DIR}/.nginx/nginx.pid")" 2>/dev/null; then + nginx -s reload -c "${ROOT_DIR}/deploy/nginx.user-8443.conf" -p "${ROOT_DIR}/.nginx" +else + nginx -c "${ROOT_DIR}/deploy/nginx.user-8443.conf" -p "${ROOT_DIR}/.nginx" +fi + +echo "User-mode Nginx is running at https://gahow-pc.ipads-lab.se.sjtu.edu.cn:8443/"