国密证书一键部署:Ansible剧本批量部署国密SSL证书
一、国密SSL证书部署的核心特点
在编写Ansible剧本之前,需要理解国密SSL证书部署的几个关键特性:
1. 双证书机制:国密SSL通信采用“双证书”体系,即**签名证书(用于身份认证和数字签名)和加密证书(用于密钥交换和数据加密)分开,这与RSA的单证书模式完全不同。
2. 服务器需支持国密算法:官方版Nginx结合OpenSSL不支持国密算法,需要重新编译并指定国密SM2模块(如wotrus_ssl),或使用Tengine + Tongsuo(铜锁)组合。
3. 双栈部署兼容性:为确保与各类浏览器的兼容性,通常采用**SM2/RSA双证书方案——同时在服务器上配置国际通用的RSA证书和国密标准的SM2证书。
重要提醒:私钥必须妥善保管
部署过程中必须明确区分私钥与证书文件。私钥(扩展名多为`.key`)属于敏感信息,绝不可上传至Git仓库,必须使用Ansible Vault加密。这是确保国密证书安全部署的红线底线。二、目录结构与剧本总览
text
ansible-gmssl-deploy/
├── inventory.ini # 主机清单
├── ansible.cfg # Ansible配置
├── group_vars/
│ └── all.yml # 全局变量(含域名、证书路径等)
├── playbooks/
│ └── deploy_gmssl.yml # 主部署剧本
├── templates/
│ ├── nginx_gmssl.conf.j2 # Nginx国密配置模板(支持双证书)
│ └── tongsuo_nginx.conf.j2 # Tengine+Tongsuo配置模板
├── files/
│ ├── sm2_server.crt # SM2签名证书
│ ├── sm2_server.key # SM2签名私钥
│ ├── sm2_encrypt.crt # SM2加密证书
│ ├── sm2_encrypt.key # SM2加密私钥
│ └── rsa_server.crt # RSA证书(兼容双栈)
├── roles/
│ ├── gmssl-nginx/ # Nginx国密部署角色
│ │ ├── tasks/
│ │ ├── handlers/
│ │ └── vars/
│ └── gmssl-tengine/ # Tengine+Tongsuo部署角色
└── vault.yml # 私钥等敏感信息(Vault加密)
三、核心剧本详解
以下提供了两种主流国密部署方案的完整剧本:**方案一针对Nginx(需手动编译安装国密版)**,**方案二针对Tengine + Tongsuo(官方已适配国密)**。请根据实际服务器环境选择其中一种。
3.1 方案一:Nginx国密版部署剧本
yaml
name: 批量部署国密SSL证书到Nginx服务器
hosts: gmssl_servers
become: yes
gather_facts: yes
vars:
certificate_path: "/etc/nginx/ssl"
gmssl_nginx_version: "1.24.0"
gmssl_module_source: "/path/to/wotrus_ssl_module" # 国密模块路径
# 双证书文件变量
sm2_server_crt: "sm2_server.crt"
sm2_server_key: "sm2_server.key"
sm2_encrypt_crt: "sm2_encrypt.crt"
sm2_encrypt_key: "sm2_encrypt.key"
# 以下RSA证书变量用于兼容双栈部署
rsa_server_crt: "rsa_server.crt"
rsa_server_key: "rsa_server.key"
tasks:
# 任务1:安装编译依赖
- name: 安装编译所需软件包(CentOS/RHEL)
yum:
name:
- gcc
- gcc-c++
- make
- openssl-devel
- pcre-devel
- zlib-devel
state: present
when: ansible_os_family == 'RedHat'
name: 安装编译所需软件包(Ubuntu/Debian)
apt:
name:
- build-essential
- libssl-dev
- libpcre3-dev
- zlib1g-dev
state: present
update_cache: yes
when: ansible_os_family == 'Debian'
# 任务2:下载并编译国密版Nginx
- name: 下载Nginx源码
get_url:
url: "http://nginx.org/download/nginx-{{ gmssl_nginx_version }}.tar.gz"
dest: "/usr/local/src/nginx-{{ gmssl_nginx_version }}.tar.gz"
name: 解压Nginx源码
unarchive:
src: "/usr/local/src/nginx-{{ gmssl_nginx_version }}.tar.gz"
dest: "/usr/local/src/"
remote_src: yes
name: 编译Nginx(含国密模块)
shell: |
cd /usr/local/src/nginx-{{ gmssl_nginx_version }}
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--add-module={{ gmssl_module_path }}
make && make install
args:
creates: /usr/local/nginx/sbin/nginx
# 任务3:创建证书存放目录并上传证书文件
- name: 创建国密证书目录
file:
path: "{{ certificate_path }}/gmssl"
state: directory
mode: '0755'
name: 创建RSA证书目录(双栈兼容)
file:
path: "{{ certificate_path }}/rsa"
state: directory
mode: '0755'
name: 复制SM2签名证书
copy:
src: "files/{{ sm2_server_crt }}"
dest: "{{ certificate_path }}/gmssl/server.crt"
mode: '0644'
name: 复制SM2签名私钥
copy:
src: "files/{{ sm2_server_key }}"
dest: "{{ certificate_path }}/gmssl/server.key"
mode: '0600' # 私钥权限严格限制
name: 复制SM2加密证书
copy:
src: "files/{{ sm2_encrypt_crt }}"
dest: "{{ certificate_path }}/gmssl/encrypt.crt"
mode: '0644'
name: 复制SM2加密私钥
copy:
src: "files/{{ sm2_encrypt_key }}"
dest: "{{ certificate_path }}/gmssl/encrypt.key"
mode: '0600'
name: 复制RSA证书(双栈兼容,可选)
copy:
src: "files/{{ rsa_server_crt }}"
dest: "{{ certificate_path }}/rsa/server.crt"
mode: '0644'
when: rsa_server_crt is defined
name: 复制RSA私钥(双栈兼容,可选)
copy:
src: "files/{{ rsa_server_key }}"
dest: "{{ certificate_path }}/rsa/server.key"
mode: '0600'
when: rsa_server_key is defined
# 任务4:配置Nginx国密SSL
- name: 生成Nginx国密SSL配置
template:
src: nginx_gmssl.conf.j2
dest: /usr/local/nginx/conf/conf.d/{{ domain_name }}.conf
mode: '0644'
notify: 重启Nginx
# 任务5:验证配置
- name: 验证Nginx配置正确性
command: /usr/local/nginx/sbin/nginx -t
register: nginx_test
failed_when: nginx_test.rc != 0 or 'test is successful' not in nginx_test.stdout
# 任务6:启动并启用Nginx
- name: 启动Nginx服务
systemd:
name: nginx
state: started
enabled: yes
daemon_reload: yes
handlers:
- name: 重启Nginx
systemd:
name: nginx
state: restarted
3.2 方案二:Tengine + Tongsuo 部署剧本(推荐)
Tengine已对Tongsuo(原名BabaSSL)完成适配,增加了对NTLS(国密TLS)的支持,可直接采用该方案,无需手动编译Nginx国密模块。
yaml
name: 批量部署国密SSL证书(Tengine + Tongsuo方案)
hosts: gmssl_servers
become: yes
gather_facts: yes
vars:
tongsuo_version: "8.2.1"
tengine_version: "2.4.1"
certificate_path: "/etc/nginx/ssl"
tasks:
# 任务1:安装Tongsuo铜锁密码库
- name: 下载Tongsuo源码
get_url:
url: "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/{{ tongsuo_version }}.tar.gz"
dest: "/usr/local/src/Tongsuo-{{ tongsuo_version }}.tar.gz"
name: 解压并编译Tongsuo
shell: |
cd /usr/local/src
tar -xzf Tongsuo-{{ tongsuo_version }}.tar.gz
cd Tongsuo-{{ tongsuo_version }}
./config --prefix=/usr/local/tongsuo shared
make && make install
echo "/usr/local/tongsuo/lib" > /etc/ld.so.conf.d/tongsuo.conf
ldconfig
args:
creates: /usr/local/tongsuo/lib/libcrypto.so
# 任务2:安装Tengine(已适配国密)
- name: 下载Tengine源码
get_url:
url: "https://tengine.taobao.org/download/tengine-{{ tengine_version }}.tar.gz"
dest: "/usr/local/src/tengine-{{ tengine_version }}.tar.gz"
name: 编译Tengine并链接Tongsuo
shell: |
cd /usr/local/src
tar -xzf tengine-{{ tengine_version }}.tar.gz
cd tengine-{{ tengine_version }}
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-openssl=/usr/local/src/Tongsuo-{{ tongsuo_version }}
make && make install
args:
creates: /usr/local/nginx/sbin/nginx
# 任务3:配置国密证书(同上,省略重复内容)
- name: 创建证书目录并复制证书文件
# ... 参考方案一的任务3
# 任务4:生成Tengine国密SSL配置(支持双证书)
- name: 生成Tengine国密SSL配置
template:
src: tongsuo_nginx.conf.j2
dest: /usr/local/nginx/conf/conf.d/{{ domain_name }}.conf
notify: 重启Tengine
handlers:
- name: 重启Tengine
systemd:
name: nginx
state: restarted
3.3 Nginx国密SSL配置模板(双证书)
创建 `templates/nginx_gmssl.conf.j2` 文件:
nginx
server {
listen 443 ssl;
server_name {{ domain_name }};
# SM2双证书配置:签名证书+加密证书
ssl_certificate {{ certificate_path }}/gmssl/server.crt; # 签名证书
ssl_certificate_key {{ certificate_path }}/gmssl/server.key; # 签名私钥
ssl_certificate {{ certificate_path }}/gmssl/encrypt.crt; # 加密证书
ssl_certificate_key {{ certificate_path }}/gmssl/encrypt.key; # 加密私钥
# 国密加密套件
ssl_ciphers ECC-SM2-WITH-SM4-SM3:ECDHE-SM2-WITH-SM4-SM3:RSA-SM4-CBC-SM3;
ssl_protocols TLSv1.2 TLSv1.3;
# 以下为双栈兼容配置(支持SM2/RSA双证书自动协商)
# 启用SM2/RSA双证书时需配置以下内容
ssl_certificate {{ certificate_path }}/rsa/server.crt; # RSA证书
ssl_certificate_key {{ certificate_path }}/rsa/server.key; # RSA私钥
location / {
root /var/www/html;
index index.html;
}
}
四、主机清单与变量配置
`inventory.ini` 示例:
ini
[gmssl_servers]
web-server-01 ansible_host=192.168.1.10 domain_name=example.com
web-server-02 ansible_host=192.168.1.11 domain_name=api.example.com
web-server-03 ansible_host=192.168.1.12 domain_name=portal.example.com
[gmssl_servers:vars]
ansible_user=root
ansible_ssh_private_key_file=~/.ssh/id_rsa
`group_vars/all.yml` 示例:
yaml
certificate_path: "/etc/nginx/ssl"
gmssl_module_path: "/usr/local/src/wotrus_ssl_module"
# 国密证书文件名称(需事先放置在files目录下)
sm2_server_crt: "sm2_server.crt"
sm2_server_key: "sm2_server.key"
sm2_encrypt_crt: "sm2_encrypt.crt"
sm2_encrypt_key: "sm2_encrypt.key"
# 双栈兼容RSA证书(可选)
rsa_server_crt: "rsa_server.crt"
rsa_server_key: "rsa_server.key"
# 以下为内网IP绑定的配置示例(如果适用)
# 国密证书通常要求绑定域名,内网IP需通过SAN扩展实现,参考步骤1生成CSR时添加subjectAltName = IP:192.168.1.1
五、执行部署
bash
1. 语法检查
ansible-playbook -i inventory.ini playbooks/deploy_gmssl.yml --syntax-check
2. 试运行(dry-run)
ansible-playbook -i inventory.ini playbooks/deploy_gmssl.yml --check
3. 正式执行部署
ansible-playbook -i inventory.ini playbooks/deploy_gmssl.yml -v
4. 仅对特定主机组执行
ansible-playbook -i inventory.ini playbooks/deploy_gmssl.yml --limit gmssl_servers
5. 分批执行(控制风险)
ansible-playbook -i inventory.ini playbooks/deploy_gmssl.yml --serial=5 --max-fail-percentage=2
# 6. 验证部署结果
ansible -i inventory.ini gmssl_servers -m shell -a 'nginx -t && curl -k https://localhost'
六、验证国密证书部署
部署完成后,需进行以下验证:
验证项 | 验证方法 |
Nginx配置语法正确性 `nginx -t`
服务监听状态 `netstat -tlnp | grep 443`
国密算法套件兼容性 使用支持国密算法的浏览器访问(如密信浏览器、红莲花浏览器、360企业浏览器)
RSA双栈兼容性 使用普通浏览器访问,确认RSA证书正常协商
配置文件内容正确性 检查`server.crt`与`encrypt.crt`路径区分是否正确
七、常见问题排查
问题 可能原因 解决方案
Nginx启动失败 未编译国密模块 确认编译时已添加国密模块参数
浏览器无法访问 仅部署了国密证书,但使用了普通浏览器 部署SM2/RSA双证书方案,同时配置RSA证书 证书协商失败 只配置了一个证书 国密双证书需同时配置签名证书和加密证书
内网IP绑定失败 国密证书通常要求绑定域名 在CSR中添加SAN扩展:`subjectAltName = IP:192.168.1.100`
私钥权限过宽 私钥文件权限未严格限制 私钥文件应设为`0600`或`0400`,不可被其他用户读取
八、扩展建议
1. Ansible Vault加密私钥:将`.key`私钥文件内容放入Vault加密的变量文件中,而非直接放在`files/`目录下
2. 结合ACME自动续期:虽然当前Let's Encrypt等国密支持尚不完善,但可参考`ansible-acme-sh`角色结构为后续证书续期预留接口
3. 引入RHEL System Role:对于RHEL环境,可使用`certificate` System Role实现一致的证书管理
4. 前置校验:利用`delegate_to: localhost`在控制机上做证书有效期检查与配置语法预验证