Skip to content

TLS (HTTPS) 部署指南

本文档描述了 Classroom Manager 生产环境部署中 HTTPS 的实现方式,使用 Nginx 作为 TLS 终端和反向代理,运行在 Docker Compose 技术栈中。

架构概览

在生产环境中,一个专用的 Nginx 容器负责:

  • 在 443 端口(HTTPS)终止 TLS,并提供证书和私钥服务。
  • 将构建好的前端作为静态文件从 /usr/share/nginx/html 提供服务,并通过 try_files 支持 SPA 路由。
  • 将后端请求(Django/Gunicorn)反向代理到内部上游 backend:8000
  • 将常用应用路径转发到后端:/api//media//admin//static/

从 Compose 的角度来看:

  • Nginx 服务发布主机端口 80443
  • Nginx 以只读卷挂载生产配置和证书目录。
  • Nginx 依赖后端容器并加入同一 Docker 网络以进行服务发现。

文件和路径

Nginx 配置

Nginx 从以下位置加载生产虚拟主机配置:

  • 主机路径:./nginx/nginx.conf
  • 容器路径:/etc/nginx/conf.d/default.conf(只读)

证书目录

证书期望位于:

  • 主机路径:./nginx/certs/
  • 容器路径:/etc/nginx/certs/(只读)

并在 Nginx 中引用为:

  • /etc/nginx/certs/fullchain.pem(证书链)
  • /etc/nginx/certs/privkey.pem(私钥)

前提条件

  • 你必须拥有有效的 TLS 证书和私钥对,位于以下路径:

    • ./nginx/certs/fullchain.pem
    • ./nginx/certs/privkey.pem
  • 确保主机上的文件权限合适(私钥应受限访问):

    • chmod 600 ./nginx/certs/privkey.pem
    • chmod 644 ./nginx/certs/fullchain.pem
  • 更新 Nginx 配置中的 server_name 以匹配你的真实域名。

证书获取

方案 A:使用受信任的 CA(推荐)

从受信任的 CA 获取证书(例如通过 ACME/Let's Encrypt),在主机(或专用的证书管理容器)上获取,然后将生成的文件复制/软链接到 ./nginx/certs/

  • 将完整证书链放置为 ./nginx/certs/fullchain.pem
  • 将私钥放置为 ./nginx/certs/privkey.pem

续期后,重新加载 Nginx 以使其加载新文件:

bash
docker compose -f docker-compose.prod.yml exec nginx nginx -t
docker compose -f docker-compose.prod.yml exec nginx nginx -s reload

方案 B:自签名证书(仅用于开发/内部测试)

WARNING

自签名证书会导致浏览器警告,不应在公共生产环境中使用,但可用于快速功能验证。

bash
mkdir -p ./nginx/certs
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout ./nginx/certs/privkey.pem \
  -out ./nginx/certs/fullchain.pem \
  -subj "/CN=localhost"

Nginx TLS 配置(当前行为)

TLS 监听

服务器块在 443 端口启用 SSL 监听。

证书

Nginx 从挂载的证书目录加载证书链和密钥。

协议和会话设置

配置启用了 TLS 1.2 和 TLS 1.3,使用 SSL 会话缓存,设置会话超时,并禁用会话票据。

路由行为

  • //usr/share/nginx/html 提供 SPA 前端服务,对于客户端路由回退到 /index.html
  • /api//media//admin/ 被反向代理到上游,并包含标准转发头(包括 X-Forwarded-Proto)。
  • /static/ 同样被代理到后端。

加固和生产环境建议

添加 HTTP(端口 80)到 HTTPS 的重定向

如果暴露了 80 端口,添加一个单独的服务器块将所有 HTTP 流量重定向到 HTTPS:

nginx
server {
   listen 80;
   server_name example.com;
   return 301 https://$host$request_uri;
}

使用真实域名并考虑 HSTS

server_name localhost; 替换为你的实际域名。确认 HTTPS 稳定后,考虑添加 HSTS:

nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

DANGER

只有在你确定 HTTPS 在你的域名上正确工作时才启用 HSTS。

添加现代安全头(推荐)

典型的基线配置:

nginx
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header Referrer-Policy strict-origin-when-cross-origin always;

验证反向代理后面的 Django 设置

由于 Nginx 转发了 X-Forwarded-Proto,确保 Django 配置为信任该头,以便它能正确检测安全请求并生成 HTTPS URL。常见设置包括:

  • SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
  • CSRF_TRUSTED_ORIGINS = ["https://example.com"]
  • SESSION_COOKIE_SECURE = True
  • CSRF_COOKIE_SECURE = True

运维检查清单

上线前

  • 确认证书文件存在:

    • ./nginx/certs/fullchain.pem
    • ./nginx/certs/privkey.pem
  • 确认 Nginx 正常加载:

bash
docker compose -f docker-compose.prod.yml exec nginx nginx -t
  • 确认 HTTPS 可达性:
bash
curl -vkI https://YOUR_DOMAIN/
curl -vkI https://YOUR_DOMAIN/api/

上线后

  • 启用自动证书续期(如果使用 ACME)。
  • 续期后重新加载 Nginx。
  • 定期从外部网络验证 TLS 和响应头。