用户使用  Ansible  批量更新服务器  SSL证书是一种高效、可重复的自动化运维方式。下面将从准备证书、设计  Playbook、安全处理私钥、服务重启等方面详细说明,并提供一个完整的示例。

1.  准备工作

证书文件:将新的证书文件(`.crt`)、私钥文件(`.key`)以及中间证书(如果有)存放在  Ansible  控制节点上(例如  `files/ssl/`  目录下)。

主机清单:明确需要更新证书的目标主机分组,例如  `webservers`。

服务信息:知道目标主机上负责  HTTPS  的服务(Nginx、Apache、HAProxy  等)及其证书路径和重启命令。

2.  Playbook  设计思路

一个典型的更新  SSL  证书的  Playbook  包含以下步骤:

1.  确保证书目录存在:如果目标路径不存在则创建。

2.  分发证书和私钥:使用  `copy`  或  `template`  模块将文件推送到指定位置。

3.  设置正确的权限:私钥文件权限应设为  `0600`,证书文件通常为  `0644`。

4.  通知服务重启:当证书文件发生变化时,触发  handler  来重新加载或重启服务。

5.  (可选)验证证书:可使用  `shell`  或  `command`  模块检查证书是否生效。

3.  安全处理私钥

私钥是敏感信息,不要明文提交到版本库。

使用  **Ansible  Vault**  加密私钥文件或整个变量文件。

也可以在  Playbook  中通过  `ansible-vault`  加密后的文件直接分发,或者将私钥内容定义为变量,通过  `copy`  的  `content`  参数写入。

4.  示例  Playbook

以下示例假设目标主机为  Nginx,证书路径为  `/etc/nginx/ssl/`,证书文件名为  `example.com.crt`,私钥为  `example.com.key`。

yaml

name:  批量更新  SSL  证书

    hosts:  webservers

    become:  yes

    vars:

        cert_src_dir:  "files/ssl/"

        cert_dest_dir:  "/etc/nginx/ssl/"

        cert_name:  "example.com"

        service_name:  "nginx"

    tasks:

        -  name:  创建证书存放目录

            file:

                path:  "{{  cert_dest_dir  }}"

                state:  directory

                owner:  root

                group:  root

                mode:  0755

          name:  分发证书文件

            copy:

                src:  "{{  cert_src_dir  }}/{{  cert_name  }}.crt"

                dest:  "{{  cert_dest_dir  }}/{{  cert_name  }}.crt"

                owner:  root

                group:  root

                mode:  0644

            notify:  reload  nginx

          name:  分发私钥文件(使用  Vault  加密)

            copy:

                src:  "{{  cert_src_dir  }}/{{  cert_name  }}.key.vault"

                dest:  "{{  cert_dest_dir  }}/{{  cert_name  }}.key"

                owner:  root

                group:  root

                mode:  0600

            notify:  reload  nginx

          name:  分发中间证书(如果有)

            copy:

                src:  "{{  cert_src_dir  }}/ca-bundle.crt"

                dest:  "{{  cert_dest_dir  }}/ca-bundle.crt"

                owner:  root

                group:  root

                mode:  0644

            notify:  reload  nginx

    handlers:

        -  name:  reload  nginx

            systemd:

                name:  "{{  service_name  }}"

                state:  reloaded

            #  对于非  systemd  系统,可使用  service  模块

说明:

  私钥文件被命名为  `.key.vault`,实际是一个用  `ansible-vault`  加密的普通文件。Playbook  执行时需提供  Vault  密码(`--ask-vault-pass`)。

  使用  `notify`  机制,仅当文件真正发生变化时才触发服务重载,避免不必要的重启。

  如果证书路径或服务名在不同主机上不同,可以使用  `group_vars`  或  `host_vars`  来定义变量。

5.  批量处理多个站点/不同证书

如果一台服务器上运行多个  HTTPS  站点,每个站点使用不同证书,可以通过循环或变量数组来处理。

示例:使用  `with_items`  循环分发多个证书。

yaml

vars:

    sites:

        -  domain:  example.com

            crt_file:  example.com.crt

            key_file:  example.com.key

        -  domain:  test.com

            crt_file:  test.com.crt

            key_file:  test.com.key

tasks:

      name:  分发证书文件

        copy:

            src:  "{{  cert_src_dir  }}/{{  item.crt_file  }}"

            dest:  "{{  cert_dest_dir  }}/{{  item.crt_file  }}"

            mode:  0644

        loop:  "{{  sites  }}"

        notify:  reload  nginx

      name:  分发私钥文件

        copy:

            src:  "{{  cert_src_dir  }}/{{  item.key_file  }}.vault"

            dest:  "{{  cert_dest_dir  }}/{{  item.key_file  }}"

            mode:  0600

        loop:  "{{  sites  }}"

        notify:  reload  nginx

6.  更高级的考虑

6.1  使用  template  生成配置

如果证书路径需要写入配置文件(如  Nginx  的  `server`  块),可以使用  `template`  模块动态生成配置文件,并通知服务重载。

6.2  检测证书是否真的需要更新

可以在分发前检查目标主机上现有证书的  `md5sum`  或  `stat`  属性,只有当不同时才替换,减少不必要的服务重载。但通常  `copy`  模块本身已具备幂等性(通过文件校验)。

6.3  滚动更新(避免服务中断)

如果服务集群支持负载均衡,可以分批更新,确保服务不中断。使用  `serial`  关键字控制并发:

yaml

  name:  更新  SSL  证书

    hosts:  webservers

    serial:  1      #  每次只更新一台

6.4  验证证书

更新后可以增加一个验证任务,例如使用  `openssl`  检查证书有效期:

yaml

-  name:  验证证书有效期

    shell:  "openssl  x509  -enddate  -noout  -in  {{  cert_dest_dir  }}/{{  cert_name  }}.crt"

    register:  cert_enddate

    changed_when:  false

-  name:  显示证书过期时间

    debug:

        msg:  "{{  cert_enddate.stdout  }}"

6.5  使用  acme.sh  或  Let's  Encrypt  自动获取

如果证书来自  Let's  Encrypt,可以结合  Ansible  调用  `acme_certificate`  模块自动申请和更新证书,但前提是控制节点或目标主机上需要配置好  ACME  环境。

7.  安全性补充

使用  `ansible-vault`  加密所有包含私钥的文件或变量。

确保  Playbook  中不包含明文密码或私钥。

为私钥设置严格的权限(`0600`),仅  root  可读。

考虑在目标主机上设置  SELinux  上下文,如果使用  SELinux,可能需要  `seboolean`  或  `sefcontext`  调整。

8.  执行与测试

测试模式:先使用  `--check`  模式查看变更,避免误操作。

逐步执行:可以先用  `--limit`  限制在一台主机上执行,验证无误后再全量更新。

备份:在分发新证书前,使用  `backup:  yes`  备份旧证书:

yaml

copy:

    src:  ...

    dest:  ...

    backup:  yes

通过  Ansible  Playbook  批量更新服务器SSL证书,可以确保一致性、可追溯性和自动化程度。关键点包括:妥善保管私钥、使用  handler  触发服务重载、利用幂等性避免不必要的重启,以及结合滚动更新策略减少影响。根据实际环境调整证书路径、服务名称和中间证书配置即可。