From 975f6e74ad2a14e50459005d1e9cfbf7c33c2846 Mon Sep 17 00:00:00 2001 From: david_bai Date: Thu, 9 Oct 2025 21:46:03 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20clarify=20LAN=20TLS=20(self-signed)=20u?= =?UTF-8?q?sage=20=E2=80=94=20import=20CA=20in=20browser,=20correct=20HTTP?= =?UTF-8?q?S=20endpoints=20(8443),=20CORS=20guidance;=20fix=20deploy=20hin?= =?UTF-8?q?ts=20to=20only=20show=20reachable=20Nginx=20URLs.=20Also:=20tru?= =?UTF-8?q?st=20CA=20in=20frontend=20container=20and=20align=20HTTPS=20por?= =?UTF-8?q?t=20mapping.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++ README.zh-CN.md | 8 ++++++ deploy.sh | 27 ++++++++++++++++-- docker-compose.yml | 5 +++- docker/scripts/generate-config.sh | 46 +++++++++++++++++++++++-------- docs/DEPLOYMENT_docker.md | 29 +++++++++++++++++++ docs/DEPLOYMENT_docker.zh-CN.md | 29 +++++++++++++++++++ 7 files changed, 136 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 895d88c..457aa66 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,14 @@ bash ./deploy.sh --mode full --domain your-domain.com --with-nginx --with-turn - See [Docker Deployment Guide](./docs/DEPLOYMENT_docker.md) (Modes Overview, LAN TLS limitations, Let’s Encrypt auto-issue/renew) +Heads-up (LAN TLS, self-signed) +- Import the CA certificate into your browser (or system trust store) on first use: `docker/ssl/ca-cert.pem`. Otherwise the browser shows “certificate not valid/untrusted”. +- Access endpoints (by default): + - Nginx: `http://localhost` + - HTTPS: `https://localhost:8443`, `https://:8443` + - Frontend dev ports (optional): `http://localhost:3002`, `http://:3002` + - With CA trusted, using same-origin HTTPS (8443) avoids CORS; common dev origins (`localhost`, `:3002`) are allowed by default. + ## 🚀 Quick Start (Full-Stack Local Development) Before you begin, ensure your development environment has [Node.js](https://nodejs.org/) (v18+), [npm](https://www.npmjs.com/), and a running [Redis](https://redis.io/) instance installed. diff --git a/README.zh-CN.md b/README.zh-CN.md index 6339ff2..c81ee20 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -62,6 +62,14 @@ bash ./deploy.sh --mode full --domain your-domain.com --with-nginx --with-turn - 完整说明见: docs/DEPLOYMENT_docker.zh-CN.md(模式一览、LAN TLS、自签限制、Let’s Encrypt 自动签发/续期) +提示(lan-tls 自签 HTTPS) +- 首次访问需导入 CA 证书:`docker/ssl/ca-cert.pem` 到浏览器(或系统信任),否则浏览器会提示“证书无效/不受信任”。 +- 访问方式(默认): + - Nginx: `http://localhost` + - HTTPS: `https://localhost:8443`、`https://<你的局域网IP>:8443` + - 前端开发口(可选): `http://localhost:3002`、`http://<你的局域网IP>:3002` + - 若导入了 CA,浏览器与 API 走同源 HTTPS(8443)可避免 CORS;我们已默认放开常见开发来源(`localhost`、`:3002` 等)。 + **部署优势**: - ✅ 部署时间: 60 分钟 → 5 分钟 diff --git a/deploy.sh b/deploy.sh index 2b647bf..506afc7 100644 --- a/deploy.sh +++ b/deploy.sh @@ -545,6 +545,7 @@ show_deployment_info() { local public_ip="" local frontend_port="" local backend_port="" + local https_port="" local deployment_mode="" local network_mode="" local domain_name="" @@ -555,6 +556,7 @@ show_deployment_info() { public_ip=$(grep "PUBLIC_IP=" .env | cut -d'=' -f2) frontend_port=$(grep "FRONTEND_PORT=" .env | cut -d'=' -f2) backend_port=$(grep "BACKEND_PORT=" .env | cut -d'=' -f2) + https_port=$(grep "HTTPS_PORT=" .env | cut -d'=' -f2) deployment_mode=$(grep "DEPLOYMENT_MODE=" .env | cut -d'=' -f2) network_mode=$(grep "NETWORK_MODE=" .env | cut -d'=' -f2) domain_name=$(grep "DOMAIN_NAME=" .env | cut -d'=' -f2) @@ -604,13 +606,32 @@ show_deployment_info() { echo -e "${BLUE}🔀 Nginx Proxy:${NC}" if [[ -n "$domain_name" ]]; then echo " HTTP: http://$domain_name" - [[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://$domain_name" + if [[ -f "docker/ssl/server-cert.pem" ]]; then + echo " HTTPS: https://$domain_name" + fi elif [[ -n "$public_ip" ]]; then echo " HTTP: http://$public_ip" - [[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://$public_ip" + if [[ -f "docker/ssl/server-cert.pem" ]]; then + # In non-domain cases, show HTTPS with explicit port (e.g., lan-tls uses 8443) + if [[ -n "$https_port" && "$https_port" != "443" ]]; then + echo " HTTPS: https://$public_ip:$https_port" + else + echo " HTTPS: https://$public_ip" + fi + fi else echo " HTTP: http://localhost" - [[ -f "docker/ssl/server-cert.pem" ]] && echo " HTTPS: https://localhost" + if [[ -f "docker/ssl/server-cert.pem" ]]; then + # Show correct HTTPS endpoint based on configured port + if [[ -n "$https_port" && "$https_port" != "443" ]]; then + echo " HTTPS: https://localhost:$https_port" + if [[ -n "$local_ip" && "$local_ip" != "127.0.0.1" ]]; then + echo " HTTPS (LAN): https://$local_ip:$https_port" + fi + else + echo " HTTPS: https://localhost" + fi + fi fi fi diff --git a/docker-compose.yml b/docker-compose.yml index 4bc9527..103dc15 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -69,6 +69,7 @@ services: - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://localhost:3001} - PORT=3002 - HOSTNAME=0.0.0.0 + - NODE_EXTRA_CA_CERTS=/opt/privydrop/ssl/ca-cert.pem ports: - "${FRONTEND_PORT:-3002}:3002" depends_on: @@ -76,6 +77,8 @@ services: condition: service_healthy networks: - privydrop-network + volumes: + - ./docker/ssl:/opt/privydrop/ssl:ro healthcheck: test: ["CMD", "node", "health-check.js"] interval: 30s @@ -90,7 +93,7 @@ services: restart: unless-stopped ports: - "${HTTP_PORT:-80}:80" - - "${HTTPS_PORT:-443}:443" + - "${HTTPS_PORT:-443}:${DOCKER_HTTPS_CONTAINER_PORT:-443}" volumes: - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro diff --git a/docker/scripts/generate-config.sh b/docker/scripts/generate-config.sh index 4146077..3a9e071 100755 --- a/docker/scripts/generate-config.sh +++ b/docker/scripts/generate-config.sh @@ -186,20 +186,23 @@ generate_env_file() { case "$DEPLOYMENT_MODE" in lan-http) - cors_origin="http://${LOCAL_IP}:3002,http://localhost:3002" + # Allow both dev ports and nginx origins to avoid CORS when --with-nginx is used + cors_origin="http://${LOCAL_IP}:3002,http://localhost:3002,http://${LOCAL_IP},http://localhost" api_url="http://${LOCAL_IP}:3001" ;; lan-tls) if [[ "$WEB_HTTPS_ENABLED" == "true" ]]; then HTTPS_LISTEN_PORT="8443" - cors_origin="https://${LOCAL_IP}:${HTTPS_LISTEN_PORT}" + # Allow common dev origins to avoid CORS when accessing via http://localhost and :3002 + # Keep API URL on HTTPS to go through nginx TLS + cors_origin="https://${LOCAL_IP}:${HTTPS_LISTEN_PORT},https://localhost:${HTTPS_LISTEN_PORT},http://${LOCAL_IP},http://${LOCAL_IP}:3002,http://localhost,http://localhost:3002" api_url="https://${LOCAL_IP}:${HTTPS_LISTEN_PORT}" ssl_mode="self-signed" fi ;; public) local effective_public_host="${PUBLIC_IP:-$LOCAL_IP}" - cors_origin="http://${effective_public_host}:3002,http://localhost:3002" + cors_origin="http://${effective_public_host}:3002,http://localhost:3002,http://${effective_public_host},http://localhost" api_url="http://${effective_public_host}:3001" turn_enabled="true" ;; @@ -250,6 +253,16 @@ generate_env_file() { TURN_MIN_PORT="$turn_min_port_value" TURN_MAX_PORT="$turn_max_port_value" + # Decide container HTTPS port for Docker mapping + # - full (with SNI 443): container listens on 443 (stream), website on 8443 internal + # - lan-tls (self-signed, explicitly enabled): container listens on 8443 (no stream) + local docker_https_container_port="443" + if [[ "$DEPLOYMENT_MODE" == "lan-tls" && "$WEB_HTTPS_ENABLED" == "true" ]]; then + docker_https_container_port="8443" + else + docker_https_container_port="443" + fi + cat > "$env_file" << EOF # PrivyDrop Docker configuration # Generated at: $(date) @@ -272,6 +285,7 @@ FRONTEND_PORT=3002 BACKEND_PORT=3001 HTTP_PORT=80 HTTPS_PORT=${HTTPS_LISTEN_PORT:-443} +DOCKER_HTTPS_CONTAINER_PORT=${docker_https_container_port} # ============================================================================= # Redis config @@ -516,26 +530,34 @@ generate_ssl_certificates() { -out docker/ssl/server.csr \ -subj "/C=CN/ST=Local/L=Local/O=PrivyDrop/CN=${LOCAL_IP}" 2>/dev/null - # Create extensions config + # Create extensions config (with v3_req section for -extensions v3_req) cat > docker/ssl/server.ext << EOF +[v3_req] +subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE -keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment -subjectAltName = @alt_names +keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment +extendedKeyUsage=serverAuth +subjectAltName=@alt_names [alt_names] -DNS.1 = localhost -DNS.2 = *.local -DNS.3 = ${DOMAIN_NAME:-privydrop.local} -IP.1 = ${LOCAL_IP} -IP.2 = 127.0.0.1 +DNS.1=localhost +DNS.2=*.local +DNS.3=${DOMAIN_NAME:-privydrop.local} +IP.1=${LOCAL_IP} +IP.2=127.0.0.1 EOF # Sign server certificate openssl x509 -req -days 365 -in docker/ssl/server.csr \ -CA docker/ssl/ca-cert.pem -CAkey docker/ssl/ca-key.pem \ -out docker/ssl/server-cert.pem -CAcreateserial \ - -extensions v3_req -extfile docker/ssl/server.ext 2>/dev/null + -extensions v3_req -extfile docker/ssl/server.ext + # Validate certificate generation + if [[ ! -f docker/ssl/server-cert.pem ]]; then + log_error "Failed to generate docker/ssl/server-cert.pem (self-signed). Check OpenSSL output above." + exit 1 + fi # Clean temporary files rm -f docker/ssl/server.csr docker/ssl/server.ext docker/ssl/ca-cert.srl diff --git a/docs/DEPLOYMENT_docker.md b/docs/DEPLOYMENT_docker.md index f75cddc..46e67e7 100644 --- a/docs/DEPLOYMENT_docker.md +++ b/docs/DEPLOYMENT_docker.md @@ -437,6 +437,35 @@ bash ./deploy.sh --mode lan-tls --enable-web-https --with-nginx - For development or managed devices only (internal CA trusted fleet-wide); HSTS disabled; `turns:443` not guaranteed. For restricted networks (443-only), use full (domain + trusted cert + SNI 443). +Usage (strongly recommended) + +1) Import the self-signed CA (required) +- Location: `docker/ssl/ca-cert.pem` +- Browser import: + - Chrome/Edge: Settings → Privacy & Security → Security → Manage certificates → “Trusted Root Certification Authorities” → Import `ca-cert.pem` + - macOS: Keychain Access → System → Certificates → Import `ca-cert.pem` → set to “Always Trust” + - Linux (system-wide): + - `sudo cp docker/ssl/ca-cert.pem /usr/local/share/ca-certificates/privydrop-ca.crt` + - `sudo update-ca-certificates` +- Without trusting the CA, browser HTTPS will show untrusted cert warnings and API requests will fail. + +2) Access endpoints (default ports and paths) +- Nginx reverse proxy: `http://localhost` +- HTTPS (Web): `https://localhost:8443`, `https://:8443` +- Frontend direct (optional): `http://localhost:3002`, `http://:3002` +- Note: In lan-tls, 443 is not open; HTTPS uses 8443. + +3) CORS +- For convenience, common dev origins are allowed by default: `https://:8443`, `https://localhost:8443`, `http://localhost`, `http://`, `http://localhost:3002`, `http://:3002`. +- To minimize allowed origins, edit `CORS_ORIGIN` in `.env` and then `docker compose restart backend`. + +4) Health checks +- `curl -kfsS https://localhost:8443/api/health` → 200 +- `bash ./test-health-apis.sh` → all tests should pass (frontend container trusts the self-signed CA). + +5) Deployment hints +- The script prints only reachable Nginx endpoints; in lan-tls it will show `https://localhost:8443` (and `https://:8443` if available). + ### Public Domain Deployment (HTTPS + Nginx) — Quick Test 1) Point your domain A record to the server IP (optional: also `turn.` to the same IP) diff --git a/docs/DEPLOYMENT_docker.zh-CN.md b/docs/DEPLOYMENT_docker.zh-CN.md index bae63ff..da0d1c9 100644 --- a/docs/DEPLOYMENT_docker.zh-CN.md +++ b/docs/DEPLOYMENT_docker.zh-CN.md @@ -431,6 +431,35 @@ bash ./deploy.sh --mode lan-tls --enable-web-https --with-nginx - 仅用于开发或受管终端(全员导入内部 CA);禁用 HSTS;不保证 `turns:443`;受限网络(仅 443 出口)应使用 full(域名 + 受信证书 + SNI 443)。 +使用说明(强烈建议逐条完成) + +1) 导入自签 CA 证书(必做) +- 证书位置:`docker/ssl/ca-cert.pem` +- 浏览器导入: + - Chrome/Edge:设置 → 隐私与安全 → 安全 → 管理证书 → “受信任的根证书颁发机构” → 导入 `ca-cert.pem` + - macOS:钥匙串访问 → System → 证书 → 导入 `ca-cert.pem` → 设置“始终信任” + - Linux(系统层): + - `sudo cp docker/ssl/ca-cert.pem /usr/local/share/ca-certificates/privydrop-ca.crt` + - `sudo update-ca-certificates` +- 未导入时,浏览器访问 HTTPS 会提示“证书无效/不受信任”,API 请求也会失败。 + +2) 访问方式(默认端口与路径) +- Nginx 反代:`http://localhost` +- HTTPS(Web):`https://localhost:8443`、`https://<局域网IP>:8443` +- 前端直连(可选):`http://localhost:3002`、`http://<局域网IP>:3002` +- 说明:lan-tls 下未开启 443;HTTPS 统一走 8443。 + +3) 跨域(CORS)说明 +- 为方便开发与调试,脚本已默认放开常见来源:`https://<局域网IP>:8443`、`https://localhost:8443`、`http://localhost`、`http://<局域网IP>`、`http://localhost:3002`、`http://<局域网IP>:3002`。 +- 若仍需最小化来源,请在 `.env` 中精准收敛 `CORS_ORIGIN`,并 `docker compose restart backend`。 + +4) 健康检查 +- `curl -kfsS https://localhost:8443/api/health` → 200 +- `bash ./test-health-apis.sh` → 所有测试应通过(前端 detailed 健康已在容器内信任自签 CA)。 + +5) 部署提示 +- 脚本会只显示可访问的 Nginx 入口;lan-tls 下将显示明确的 `https://localhost:8443`(如存在局域网 IP 也将显示 `https://:8443`)。 + ### 公网域名部署(HTTPS + Nginx)快速测试 1) 将域名 A 记录解析至服务器 IP(可选:`turn.` 指向相同 IP)