麒麟V10部署Tengine与铜锁国密双栈验证
记录时间:2026-11-05
环境:Kylin V10 SP3 (Halberd) / aarch64 / Tengine 3.2.0-rc5 / Tongsuo 8.5.0(OpenSSL 3.5.4 基线)/ gcc 7.3.0
一、背景
这次要做的是给 nginx 侧加国密能力:同一个入口既服务国密浏览器(TLCP 双证书),也保留 RSA 给国际浏览器,满足国密改造与等保要求。先在测试环境把整套流程跑通,再谈生产切换。
测试环境:
| 节点 | IP | 系统 | 角色 |
|---|---|---|---|
| localhost | 192.168.120.79 | Kylin V10 SP3 / aarch64 | 测试机,离线(ISO 挂载 /mnt,本地源 kylin10-local-repo) |
生产侧现状一并记下,后面切生产以此为准:
| 项 | 106.109 现网值 |
|---|---|
| nginx 版本 | gzeport/1.28.1,gcc 4.8.5 编译 |
| TLS 库 | OpenSSL 1.1.1w,静态编译,无国密套件 |
| 会话保持 | nginx-sticky-module-1.2.6,upstream 里 7 处裸 sticky; |
| 编译选项 | enable-tls1_3 enable-weak-ssl-ciphers,弱套件开着,等保要关 |
二、选型过程
2.1 Angie 一开始被我错误排除了
Tongsuo 官方 README 建议用 Angie 替代 nginx。我翻了它的第三方模块清单,整页没有 sticky,就以为 Angie 做不了会话保持,先把它排除了。后来阿俊指出官方文档里有 sticky 指令,我回查后确认,Angie 的 sticky 是内置的,在 upstream 上下文,有 cookie、route、learn 三种模式,只有编译时加 --without-http_upstream_sticky_module 才会去掉;国密也是内置的(ssl_ntls on; 加双参数 ssl_certificate,可以和 RSA 证书并存),维护节奏比 Tengine 好(1.12.4,2026-10 发布)。
所以这一轮改用 Angie 重新验证。下面 Tengine 的编译和配置过程留着作对照,Angie 的验证记录另起一篇。
2.2 再排除原生 nginx 打国密补丁
查 Tongsuo 组织下的 nginx-ntls 仓库,GitHub 返回 404,官方已经不再维护这个补丁。原生 nginx 这条路没法长期跟,否决。
2.3 最后定 Tengine
Tengine 自带 modules/ngx_tongsuo_ntls(国密双证书)和 modules/ngx_http_upstream_session_sticky_module(提供 session_sticky),两个约束一次解决。这里我确认过一个容易漏的细节:Tengine 3.2.0 默认编译的是新版 sticky,参数规格是 NGX_CONF_2MORE,至少两个参数,现有的裸 sticky; 会直接报 invalid number of arguments。旧模块 session_sticky 不在默认编译范围,必须显式 --add-module。
nginx 是 sticky,Tengine 是 session_sticky,指令名不一样。现网 nginx 用的是第三方 nginx-sticky-module-1.2.6 的 sticky 指令;切到 Tengine 时 upstream 里的 sticky; 必须全部改成 session_sticky;(106.109 有 7 处),同时编译参数必须带 --add-module=modules/ngx_http_upstream_session_sticky_module。两件事漏一件都会在 tengine -t 阶段报错:模块没编进去报 unknown directive "session_sticky",继续用裸 sticky; 会命中内置新版 sticky 报 invalid number of arguments in "sticky"。两边默认 cookie 名都是 route,改名后现有会话不受影响。
三、编译
3.1 获取源码包
# Tengine 3.2.0-rc5 源码
curl -L -o tengine-3.2.0-rc5.tar.gz \
https://github.com/alibaba/tengine/archive/refs/tags/3.2.0-rc5.tar.gz
curl -L -O https://github.com/alibaba/tengine/releases/download/3.2.0-rc5/SHA256SUMS
# Tongsuo 8.5.0 源码
curl -L -o Tongsuo-8.5.0.tar.gz \
https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/8.5.0.tar.gz
curl -L -O https://github.com/Tongsuo-Project/Tongsuo/releases/download/8.5.0/Tongsuo-8.5.0.tar.gz.asc
cd /usr/local/src
tar xf tengine-3.2.0-rc5.tar.gz && tar xf Tongsuo-8.5.0.tar.gz
ls -d tengine-3.2.0-rc5 Tongsuo-8.5.0
ls Tongsuo-8.5.0/test/certs/sm2/ | head # 第四章要用这里的测试双证书
yum install -y gcc gcc-c++ make perl pcre-devel zlib-devel tar
3.2 先补 Text::Template
Tongsuo 的 Configure 第 2109 行是 use Text::Template 1.46;,这是硬依赖,Kylin 本地源没有这个包。我从 CPAN 下了 Text-Template-1.61.tar.gz 离线安装:
#下载地址; https://cpan.metacpan.org/modules/by-module/Text/Text-Template-1.61.tar.gz
cd /usr/local/src
tar xf Text-Template-1.61.tar.gz && cd Text-Template-1.61
perl Makefile.PL && make && make install
perl -MText::Template -e 'print $Text::Template::VERSION, "\n"' # 输出 1.61
Makefile.PL 提示的
Test::More::UTF8、Test::Warnings只是测试依赖,不影响使用。
3.3 编 Tongsuo
cd /usr/local/src/Tongsuo-8.5.0
./config enable-ntls --prefix=/opt/tongsuo no-shared
make -j"$(nproc)" && make install
/opt/tongsuo/bin/openssl version
版本输出是 Tongsuo 8.5.0 加 OpenSSL 3.5.4。我原本以为 8.x 都是 OpenSSL 1.1.1 基线,实际 8.5.0 已经换成 3.5.4 基线,后面评估生产工具链要把这条算上(生产是 gcc 4.8.5)。
3.4 编 Tengine
useradd -r -s /sbin/nologin gzapps
cd /usr/local/src/tengine-3.2.0-rc5
./configure \
--prefix=/AppHome/nginx --user=gzapps --group=gzapps \
--with-http_ssl_module --with-http_stub_status_module --with-pcre \
--with-http_gzip_static_module --with-http_v2_module \
--with-http_sub_module --with-http_realip_module \
--with-stream --with-stream_ssl_module --with-stream_realip_module \
--add-module=modules/ngx_tongsuo_ntls \
--add-module=modules/ngx_http_upstream_session_sticky_module \
--with-openssl=/usr/local/src/Tongsuo-8.5.0 \
--with-openssl-opt="--api=1.1.1 enable-ntls"
make -j"$(nproc)"
我去掉了 Tengine README 里的
--strict-warnings。OpenSSL 3.5.4 在 gcc 7.3 下开-Werror很容易被新告警打断,不带它编译成功率更高。
安装直接装到 /AppHome/nginx,靠的是编译时定好的 --prefix:
make install
Tengine 的安装目标把二进制装成
tengine,不是nginx,路径是/AppHome/nginx/sbin/tengine。--prefix必须保持这个值,配置里的pid logs/...是相对编译期 prefix 解析的,前缀不对服务起不来。
装完我在 sbin/ 里补了个软链,让原来的 sbin/nginx 路径继续可用,启动脚本、systemd 单元和运维习惯都不用改:
cd /AppHome/nginx/sbin
ln -s tengine nginx
./nginx -V
软链指向的是文件名
tengine,升级时make install覆盖的也是tengine,软链一直有效。反过来,如果哪天有别的东西往sbin/写真实的nginx文件,软链会被顶掉,需要重建。
-V 复核到 Tengine/3.2.0、nginx/1.31.5、built with OpenSSL 3.5.4,两个 add-module 都在。
四、证书
浏览器验收的证书必须由 CA 签发,源码树里那套自签测试证书过不了 360 国密浏览器。这一轮只做到协议握手,证书按下面准备。
4.1 RSA 自签证书
mkdir -p /AppHome/nginx/cert
openssl req -x509 -newkey rsa:2048 -nodes -days 30 -subj "/CN=sm2-test.local" \
-keyout /AppHome/nginx/cert/rsa.key -out /AppHome/nginx/cert/rsa.crt
4.2 国密测试证书
用 Tongsuo 源码树 test/certs/sm2/ 的现成双证书(根 CA、中间 CA、服务端签名与加密证书,另有客户端证书),有效期到 2123 年:
cd /usr/local/src/Tongsuo-8.5.0/test/certs/sm2
cp server_sign.crt server_sign.key server_enc.crt server_enc.key chain-ca.crt /AppHome/nginx/cert/
cat server_sign.crt chain-ca.crt > /AppHome/nginx/cert/sign.fullchain.crt
cat server_enc.crt chain-ca.crt > /AppHome/nginx/cert/enc.fullchain.crt
这套是三级链,必须把
chain-ca.crt拼上,否则国密客户端报链不完整。它只够跑协议:公钥算法 OID 直接写成 SM2(1.2.156.10197.1.301),360 国密浏览器要求id-ecPublicKey加 SM2 曲线参数,否则握手到 ServerKeyExchange 后回alert 50。生产证书按《麒麟V10部署Angie与铜锁国密双栈验证》4.2 在 gmcert.org 签发。
4.3 换成 gmcert 证书时的路径对应
Tengine 是四条独立指令,对应关系:
ssl_sign_certificate /opt/gmcert/sign/sign.crt;
ssl_sign_certificate_key /opt/gmcert/sign/sign.key;
ssl_enc_certificate /opt/gmcert/enc/enc.crt;
ssl_enc_certificate_key /opt/gmcert/enc/enc.key;
gmcert 证书根直签、下载即含根,不需要拼 fullchain。sign.fullchain.crt 那套拼链只对源码树测试证书有意义。
五、配置
/AppHome/nginx/conf/sm2-test.conf 的核心内容:
worker_processes 1;
pid logs/tengine-sm2-test.pid;
error_log logs/error.log info;
events { worker_connections 256; }
http {
access_log off;
server {
listen 18443 ssl;
server_name sm2-test.local;
enable_ntls on;
ssl_sign_certificate /AppHome/nginx/cert/sign.fullchain.crt;
ssl_sign_certificate_key /AppHome/nginx/cert/server_sign.key;
ssl_enc_certificate /AppHome/nginx/cert/enc.fullchain.crt;
ssl_enc_certificate_key /AppHome/nginx/cert/server_enc.key;
ssl_certificate /AppHome/nginx/cert/rsa.crt;
ssl_certificate_key /AppHome/nginx/cert/rsa.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers "ECC-SM2-SM4-CBC-SM3:ECC-SM2-SM4-GCM-SM3:ECDHE-SM2-SM4-CBC-SM3:ECDHE-SM2-SM4-GCM-SM3:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
location / { return 200 "proto=$ssl_protocol cipher=$ssl_cipher\n"; }
}
}
各指令的作用:
| 指令 | 值 | 说明 |
|---|---|---|
enable_ntls |
on |
开启国密双证书能力,由 ngx_tongsuo_ntls 提供 |
ssl_sign_certificate / _key |
sign.fullchain.crt / server_sign.key |
国密签名证书与私钥 |
ssl_enc_certificate / _key |
enc.fullchain.crt / server_enc.key |
国密加密证书与私钥 |
ssl_certificate / _key |
rsa.crt / rsa.key |
国际 RSA 证书,与国密证书同 server 并存 |
ssl_ciphers |
国密四个变体加两个 RSA 套件 | ECC 与 ECDHE 两类国密套件都要列,部分国密客户端只支持 ECC 静态套件 |
ssl_protocols |
TLSv1.2 TLSv1.3 |
只约束国际 TLS;国密走 NTLSv1.1,不在这里控制 |
六、握手验证
四条路径逐条跑过,360 浏览器单独说:
| 客户端 | 协议 | 套件 | 结果 |
|---|---|---|---|
| Tongsuo ECC 静态 | NTLSv1.1 | ECC-SM2-SM4-GCM-SM3 |
通过 |
| Tongsuo ECDHE(带客户端双证书) | NTLSv1.1 | ECDHE-SM2-SM4-GCM-SM3 |
通过 |
| Chrome / Edge | TLSv1.3 | TLS_AES_128_GCM_SHA256 |
通过(自签证书,只验证了协议路径) |
| Tongsuo TLS1.2 | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384 |
通过 |
| 360 国密浏览器 | — | — | 未做,自签证书过不了,原因见八 |
# 国密 ECC,客户端不需要证书
printf 'GET / HTTP/1.0\r\n\r\n' | /opt/tongsuo/bin/openssl s_client \
-connect 127.0.0.1:18443 -cipher ECC-SM2-SM4-GCM-SM3 \
-enable_ntls -ntls -verifyCAfile /AppHome/nginx/cert/chain-ca.crt
# 国密 ECDHE,必须带客户端双证书
printf 'GET / HTTP/1.0\r\n\r\n' | /opt/tongsuo/bin/openssl s_client \
-connect 127.0.0.1:18443 -cipher ECDHE-SM2-SM4-GCM-SM3 \
-enable_ntls -ntls -verifyCAfile /AppHome/nginx/cert/chain-ca.crt \
-sign_cert /usr/local/src/Tongsuo-8.5.0/test/certs/sm2/client_sign.crt \
-sign_key /usr/local/src/Tongsuo-8.5.0/test/certs/sm2/client_sign.key \
-enc_cert /usr/local/src/Tongsuo-8.5.0/test/certs/sm2/client_enc.crt \
-enc_key /usr/local/src/Tongsuo-8.5.0/test/certs/sm2/client_enc.key
# 国际 TLS1.2 精确套件
printf 'GET / HTTP/1.0\r\n\r\n' | /opt/tongsuo/bin/openssl s_client \
-connect 127.0.0.1:18443 -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384
国密握手成不成看 Protocol: NTLSv1.1,套件名里有没有 SM2 不作数。浏览器里看到 proto=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 也是正常的,Chrome 不支持国密,会自动走 RSA 路径。
360 这条路径卡在证书编码,Tengine 的国密实现本身没问题:换成 CA 签发的标准编码证书后,同一套双栈写法在 Angie 那一轮已经能正常握手(NTLSv1.1 / ECC-SM2-SM4-GCM-SM3)。


七、session_sticky 验证
握手验证完之后,我在 sm2-test.conf 的 http {} 里追加了一段,验证 Tengine 的会话保持能不能替掉现网的 nginx-sticky-module-1.2.6:
upstream demo_backend {
server 127.0.0.1:19001;
server 127.0.0.1:19002;
session_sticky;
}
server {
listen 18444;
server_name sticky-test.local;
location / { proxy_pass http://demo_backend; }
}
起两个能区分的后端,用 timeout 兜底,15 分钟后自动退出,避免留下残留进程:
mkdir -p /tmp/b1 /tmp/b2
echo BACKEND-19001 > /tmp/b1/index.html
echo BACKEND-19002 > /tmp/b2/index.html
(cd /tmp/b1 && timeout 900 python3 -m http.server 19001 --bind 127.0.0.1 &)
(cd /tmp/b2 && timeout 900 python3 -m http.server 19002 --bind 127.0.0.1 &)
curl -s http://127.0.0.1:19001/ && curl -s http://127.0.0.1:19002/
确认两个后端都返回各自的标记,再加载配置:
/AppHome/nginx/sbin/tengine -t -c /AppHome/nginx/conf/sm2-test.conf
/AppHome/nginx/sbin/tengine -s reload -c /AppHome/nginx/conf/sm2-test.conf
curl -sk -c /tmp/cookie.jar -o - http://127.0.0.1:18444/
for i in 1 2 3; do curl -sk -b /tmp/cookie.jar http://127.0.0.1:18444/; done
判据:
- 首次响应的
Set-Cookie名字是route(session_sticky的默认 cookie 名,和现网 nginx-sticky 一致); - 后面三次带 cookie 的请求都落在同一个 BACKEND,不出现两个标记交替。
这一步的前提是编译时挂了
--add-module=modules/ngx_http_upstream_session_sticky_module。Tengine 3.2.0 默认编译的是新版sticky,它要求至少两个参数,裸sticky;会报 invalid number of arguments,所以会话保持用的是旧模块的session_sticky;。切换生产时,106.109 的 7 处sticky;要逐处改成session_sticky;。
八、问题与回滚
- 360 国密浏览器没验收:源码树测试证书的公钥 OID 直接写成 SM2,浏览器要
id-ecPublicKey加 SM2 曲线参数,握手到 ServerKeyExchange 后回alert 50。换 CA 签发的证书后,Angie 轮已通过。 Text::Template缺失:Tongsuo 的 Configure 里use Text::Template 1.46;是硬依赖,这台机器的本地源没有这个包,CPAN 离线装 1.61 解决。--strict-warnings让编译容易中断:去掉后正常编完。- 安装完找不到
sbin/nginx:Tengine 装的是sbin/tengine。 - 指定了
ECDHE-RSA-AES256-GCM-SHA384却协商成 TLS1.3:-cipher只对 TLS1.2 及以下生效,要显式加-tls1_2。 - 麒麟系统自带的 openssl 有国密套件(
ECC-SM4-CBC-SM3等),但头文件和libssl.so都没有 ntls 导出符号,想直接用系统库给 nginx 挂双证书走不通,只能编 Tongsuo。 - 测试机是 aarch64,生产 109 是 x86_64,测试产物不能直接搬生产,生产要按同一配方在本机重编。
- Tongsuo 8.5.0 是 OpenSSL 3.5.4 基线,生产 gcc 4.8.5 能不能编过还要实测;编不过就回退 8.4.0(1.1.1 基线)或升级工具链。
回滚:删掉 sbin/nginx 软链、停掉测试实例,就回到安装前。生产切换时保留原二进制,出问题换回去再 -t 和 reload。