国密证书一键部署: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`在控制机上做证书有效期检查与配置语法预验证