麒麟V10部署Tengine与铜锁国密双栈验证

麒麟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)。

image-20261105153441275

image-20261105153452583

七、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。

九、参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇