内网 HTTPS 部署实战:私有 CA + Nginx 反向代理全流程复盘

内网 HTTPS 部署实战:私有 CA + Nginx 反向代理全流程复盘

前言

在内网环境中,很多服务只提供 HTTP 访问,存在明文传输、无法使用浏览器安全特性等问题。本文将完整复盘一次内网 HTTPS 改造过程:自建私有 CA 签发证书 → Nginx 容器加载证书 → 反向代理后端服务 → 客户端信任根证书。

全程使用 Docker Compose 部署,配置可复用,适合内网测试环境、企业内部系统、IoT 管理平台等场景。

注:文中出现的 IP、域名均为示例,请替换为你自己的实际地址。

一、为什么不用自签名证书,而要用私有 CA

很多人第一次配 HTTPS 都是直接 openssl req -x509 生成一张自签名证书,但这样会有一个致命问题:每台服务器都要单独导入一次证书,客户端无法统一信任。

正确的做法是:建一个根 CA,用它签出服务器证书。客户端只需要导入一次根 CA 证书,之后这个 CA 签发的所有服务器证书都会被自动信任。

方案 客户端操作 扩展性
直接自签名服务器证书 每台服务器都要导入一次 差
私有 CA 签发 只导入一次根 CA 好

二、整体架构

text

css 复制代码
客户端浏览器 --HTTPS--> Nginx 容器 --HTTP--> 后端服务(app.internal:8000)

Nginx 容器挂载 server.crt / server.key

三、环境信息

项目 示例值
Nginx 宿主机 192.168.1.100
项目目录 ~/nginx-ssl/
配置目录 ~/nginx-ssl/conf.d/
证书目录 ~/nginx-ssl/ssl/
访问域名 myapp.local
访问 IP 192.168.1.100
后端服务 app.internal:8000/

四、目录结构

text

vbscript 复制代码
nginx-ssl/
├── docker-compose.yml
├── conf.d/
│   └── default.conf
└── ssl/
    ├── ca.key
    ├── ca.crt
    ├── ca.srl
    ├── server.key
    ├── server.csr
    ├── server.crt
    └── san.ext

五、核心步骤复盘

1. 创建根 CA

bash

vbnet 复制代码
openssl genrsa -out ca.key 4096
openssl req -x509 -new -key ca.key -days 3650 -out ca.crt -subj "/CN=MyLAN-CA"

ca.key 是信任链的根基,一旦泄露,所有签发的证书全部失效,务必离线保管。

2. 生成服务器私钥和 CSR

bash

vbnet 复制代码
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=myapp.local"

3. 编写 SAN 扩展文件(最容易踩坑的一步)

现代浏览器(尤其 Chrome)已经不再识别 CN 字段,只认 SAN(Subject Alternative Name)。如果 SAN 没写对,即使证书被信任,浏览器依然会报 ERR_CERT_COMMON_NAME_INVALID。

san.ext 内容:

text

ini 复制代码
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = myapp.local
IP.1  = 192.168.1.100

经验总结 :IP.1 必须是你实际访问用的 IP,而不是随便填。如果你既用域名又用 IP 访问,两个都要列上。

4. 用 CA 签发服务器证书

bash

objectivec 复制代码
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -days 825 -sha256 -extfile san.ext -out server.crt

-days 825 是 iOS/macOS 对服务器证书有效期的上限,超过这个天数在这些设备上会直接报错。

5. 验证证书

bash

vbscript 复制代码
openssl x509 -in server.crt -noout -text | grep -A1 "Subject Alternative Name"
openssl verify -CAfile ca.crt server.crt

预期输出:server.crt: OK

六、Nginx 容器化部署

1. docker-compose.yml

yaml

yaml 复制代码
services:
  nginx:
    image: nginx:latest
    container_name: nginx-ssl
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./ssl:/etc/nginx/ssl:ro
      - ./conf.d:/etc/nginx/conf.d:ro
    networks:
      - webnet

networks:
  webnet:
    driver: bridge

2. conf.d/default.conf

HTTP 自动跳转 HTTPS:

nginx

perl 复制代码
server {
    listen 80;
    server_name myapp.local 192.168.1.100;
    return 301 https://$host$request_uri;
}

HTTPS 主服务:

nginx

ini 复制代码
server {
    listen 443 ssl;
    server_name myapp.local 192.168.1.100;

    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://app.internal:8000/;

        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_connect_timeout 30s;
        proxy_read_timeout    60s;
        proxy_send_timeout    60s;
    }
}

3. 关于 proxy_pass 末尾斜杠的坑

配置 访问 /foo 实际请求
proxy_pass http://app.internal:8000/; /foo(原样转发,推荐)
proxy_pass http://app.internal:8000; /foo(行为相同但语义差)

如果后端服务挂在子路径(如 /vna),则要写成:

nginx

bash 复制代码
location /vna/ {
    proxy_pass http://app.internal:8000/vna/;
}

七、启动与验证

bash

bash 复制代码
cd ~/nginx-ssl
docker compose up -d
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload
docker compose logs -f nginx

验证命令:

bash

javascript 复制代码
curl -k https://192.168.1.100/
curl --cacert ~/nginx-ssl/ssl/ca.crt https://192.168.1.100/
curl http://app.internal:8000/

八、客户端信任根 CA

关键点:客户端只需导入 ca.crt(根 CA 证书),不需要 导入 server.crt。

Windows(Chrome / Edge)

  1. 下载 ca.crt 到本地。
  2. 双击,选择"安装证书"。
  3. 存储位置选"本地计算机"(管理员)或"当前用户"。
  4. 选择"将所有的证书都放入下列存储",浏览,选择"受信任的根证书颁发机构"。
  5. 完成导入,彻底重启浏览器。

macOS(Safari / Chrome)

  1. 双击 ca.crt,加入"系统"钥匙串。
  2. 钥匙串访问,找到 MyLAN-CA,右键"显示简介"。
  3. "信任"里"使用此证书时"选"始终信任"。
  4. 输入密码确认。

Firefox(独立证书库)

  1. about:preferences#privacy → 证书 → "查看证书"。
  2. "证书颁发机构" → "导入" → 选 ca.crt。
  3. 勾选"信任由此证书颁发机构来标识网站"。

九、踩坑记录

现象 原因 解决
ERR_CERT_AUTHORITY_INVALID 根 CA 未导入或导入位置错 导入到"受信任的根证书颁发机构"
ERR_CERT_COMMON_NAME_INVALID SAN 缺少访问用的域名/IP 重新生成 san.ext,重签 server.crt
用 IP 访问报错但域名正常 SAN 只写了 DNS 补充 IP.1
502 Bad Gateway 容器访问不到后端 宿主机先 curl 后端确认可达
nginx -t 报证书路径错误 挂载路径不对 确认容器内路径为 /etc/nginx/ssl/server.crt
页面样式/JS 404 后端返回绝对路径资源 为对应前缀补充 location 代理
Firefox 仍报错 Firefox 未导入 CA 单独导入到 Firefox 证书库

十、经验总结

  1. 私有 CA 比单张自签名证书更优雅,客户端只导一次根证书,后续所有服务器证书自动被信任。
  2. SAN 是必须的,CN 已过时。Chrome 从某个版本开始完全忽略 CN,只看 SAN。
  3. ca.key 要离线保管,不要和 Nginx 部署在同一台机器上。
  4. 有效期不要超过 825 天,否则 iOS/macOS 会拒绝。
  5. 证书续期无需重新导入 CA,只需重签服务器证书并 nginx -s reload。
  6. 生产环境建议用 Let's Encrypt,Nginx 配置方式完全一致,仅替换证书文件即可。

十一、速查清单

服务端:

bash

vbnet 复制代码
cd ~/nginx-ssl/ssl
openssl genrsa -out ca.key 4096
openssl req -x509 -new -key ca.key -days 3650 -out ca.crt -subj "/CN=MyLAN-CA"
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=myapp.local"
# 写 san.ext(含 DNS.1 和 IP.1)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -days 825 -sha256 -extfile san.ext -out server.crt

部署:

bash

bash 复制代码
cd ~/nginx-ssl
docker compose up -d
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload

验证:

bash

javascript 复制代码
curl --cacert ~/nginx-ssl/ssl/ca.crt https://192.168.1.100/
openssl verify -CAfile ca.crt server.crt

客户端 :导入 ca.crt 到"受信任的根证书颁发机构",重启浏览器。

结语

本文完整复盘了内网 HTTPS 改造的全流程,从私有 CA 建设到 Nginx 容器化部署,再到客户端信任配置。相比直接使用自签名证书,私有 CA 方案在可维护性和扩展性上都有明显优势。

关键词:私有 CA、Nginx、HTTPS、反向代理、Docker、自签名证书、SAN

相关推荐
微软技术分享9 分钟前
NexShell 1.0|大模型驱动的智能 SSH 运维终端,永久免费,赋能 Linux 服务器管理
运维·服务器·ssh
小白的码BUG之路9 小时前
linux -- ssh两台机器相连
运维·服务器·ssh
缓慢更新10 小时前
表单引擎的存储建模:宽表与文档库的取舍
运维·服务器·网络·数据库·低代码·oracle·适配器模式
续写烂尾11 小时前
贴片 SD NAND、eMMC、TF 卡规格怎么定?硬件工程师选型清单
运维·服务器·网络
迪康妍妍11 小时前
终端管控实操:封堵网盘与IM外发通道的落地思路
运维·网络·安全·架构·安全性测试
sxgzzn11 小时前
如何选择合适的开关柜在线监测工具?
运维·重构·无人机·开关柜局放在线监测·光伏运维管理系统
zxcvb15312 小时前
运维经验分享:监控网页更新内容的几种思路
运维
云飞云共享云桌面13 小时前
设计师共享单台图形服务器,解决 SolidWorks大装配卡顿难题
运维·服务器·3d·自动化·电脑·制造
无足鸟ICT13 小时前
【RHCA+】RPM包命名规则
linux·运维·服务器
迪康Defender14 小时前
终端安全运维:浅谈迪康端点安全一体化管理系统工具箱实用能力
运维·安全·信息可视化·自动化·运维开发·学习方法·远程工作