一、背景与需求
在拥有数十甚至上百台服务器的生产环境中,手动更新每台服务器上的 SSL/TLS 证书不仅耗时费力,而且极易出错。证书过期可能导致服务中断、安全风险等严重问题。Ansible 作为一款强大的自动化运维工具,能够通过编写 Playbook 实现证书的批量、一致、可靠更新。
本文将手把手教你如何编写一个 Ansible Playbook,实现以下目标:
- 将新证书文件分发到目标服务器组。
- 在目标服务器上备份旧证书。
- 替换 Nginx/Apache 等服务的证书文件。
- 重新加载或重启相关服务使新证书生效。
- 验证新证书是否部署成功。
二、环境准备
1. 控制机要求
- 已安装 Ansible(建议版本 2.9+)。
- 配置好 SSH 密钥对,能够免密登录所有目标服务器。
- 准备好新的证书文件(通常包含
cert.pem(证书)和key.pem(私钥))。
2. 目录结构
建议按如下结构组织你的 Ansible 项目:
bash
ansible-cert-update/
├── inventory.ini # 主机清单文件
├── playbook.yml # 主 Playbook
├── roles/
│ └── update_cert/ # 证书更新角色
│ ├── tasks/
│ │ └── main.yml # 任务定义
│ ├── files/ # 存放证书文件
│ │ ├── new_cert.pem
│ │ └── new_key.pem
│ └── templates/ # 配置文件模板(可选)
└── vars/ # 变量文件(可选)
└── main.yml
三、编写 Ansible Playbook
1. 主机清单 (inventory.ini)
定义需要更新证书的服务器组。
ini
[web_servers]
web01 ansible_host=192.168.1.101 ansible_user=root
web02 ansible_host=192.168.1.102 ansible_user=root
web03 ansible_host=192.168.1.103 ansible_user=root
[web_servers:vars]
定义证书在目标服务器上的路径
cert_dest_path=/etc/nginx/ssl/
service_name=nginx
2. 主 Playbook (playbook.yml)
yaml
---
- name: 批量更新服务器 SSL/TLS 证书
hosts: web_servers
become: yes
gather_facts: yes
vars_files:
- vars/main.yml # 可集中定义变量
tasks:
- name: 确保目标证书目录存在
file:
path: "{{ cert_dest_path }}"
state: directory
mode: '0755'
- name: 备份旧证书文件(如果存在)
copy:
remote_src: yes
src: "{{ cert_dest_path }}{{ item }}"
dest: "{{ cert_dest_path }}{{ item }}.bak.{{ ansible_date_time.date }}"
mode: '0644'
loop:
- cert.pem
- key.pem
when: item is file # 简化逻辑,实际可用stat模块判断
ignore_errors: yes # 如果旧证书不存在,忽略错误继续
name: 分发新证书文件到目标服务器
copy:
src: "files/{{ item.src }}"
dest: "{{ cert_dest_path }}{{ item.dest }}"
mode: '0644'
loop:
{ src: 'new_cert.pem', dest: 'cert.pem' }
{ src: 'new_key.pem', dest: 'key.pem' }
name: 验证证书文件权限
file:
path: "{{ cert_dest_path }}{{ item }}"
mode: '0644'
owner: root
group: root
loop:
cert.pem
key.pem
name: 重新加载服务配置(适用于 Nginx)
systemd:
name: "{{ service_name }}"
state: reloaded
when: service_name == "nginx"
name: 重启服务(适用于 Apache 或其他需要重启的服务)
systemd:
name: "{{ service_name }}"
state: restarted
when: service_name != "nginx"
name: 验证新证书是否生效
shell: |
echo | openssl s_client -connect localhost:443 -servername {{ inventory_hostname }} 2>/dev/null | openssl x509 -noout -dates
register: cert_check
changed_when: false
failed_when: false
name: 输出证书有效期信息
debug:
msg: "{{ inventory_hostname }} 证书有效期: {{ cert_check.stdout_lines | last }}"</code></pre>
变量文件 (vars/main.yml) - 可选
集中管理变量,便于维护
cert_dest_path: /etc/nginx/ssl/
service_name: nginx
backup_suffix: "bak.{{ ansible_date_time.date }}"
四、执行与验证
测试 Playbook 语法
ansible-playbook playbook.yml --syntax-check
试运行(Dry Run)
使用 --check 模式模拟执行,不会真正修改服务器。
ansible-playbook playbook.yml --check
正式执行
ansible-playbook playbook.yml
手动验证
Playbook 执行后,可以通过以下命令手动检查:
检查证书有效期
openssl x509 -in /etc/nginx/ssl/cert.pem -noout -dates
检查服务状态
systemctl status nginx
使用 curl 测试 HTTPS 访问
curl -I https://your-domain.com
五、进阶优化与注意事项
安全性增强
私钥权限:确保私钥文件(key.pem)权限为 600,仅 root 可读。
使用 Ansible Vault:如果证书文件包含敏感信息,可使用 ansible-vault 加密。
备份策略:除了按日期备份,建议将旧证书归档到指定备份目录。
错误处理与回滚
在关键步骤(如分发文件、重启服务)后添加错误判断。
可以编写一个"回滚" Playbook,在证书更新失败时自动恢复备份。
支持多服务与多路径
通过变量和条件判断,使 Playbook 能适配 Nginx、Apache、HAProxy 等多种服务,以及不同的证书存放路径。
集成到 CI/CD 流水线
将证书更新 Playbook 作为流水线的一个阶段,在证书即将过期时自动触发更新。
六、总结
通过本文的步骤,你可以快速构建一个可靠、可重复的服务器证书批量更新方案。Ansible 的幂等性保证了操作的安全性,而清晰的步骤和验证机制则让整个流程可控、可审计。
核心要点回顾:
- 规划先行:明确证书路径、服务类型、备份策略。
- 逐步测试:充分利用 --check 和单台测试,避免批量误操作。
- 验证到位:更新后务必检查证书有效期和服务状态。
- 持续优化:根据实际环境调整 Playbook,加入错误处理和告警。
现在,你可以将上面的代码复制到你的项目中,根据实际情况修改变量和主机信息,开始你的自动化证书更新之旅!