ansible批量修改服务器密码
Ansible批量修改服务器密码:从Shell到Python的进化之路
📌 需求背景
服务器密码定期轮换是运维的基本要求。之前我写过一套基于Python socket的客户端主动上报密码脚本,这次尝试用Ansible主控端直接修改的方式。网上大多数方案需要编写复杂的playbook或手动传递变量,本文提供两种更轻量的实现:Shell版本和Python版本,让你可以交互式选择目标主机并自动生成安全密码。
🏗️ 一、方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Python socket客户端上报 | 无需主控端,被动接收 | 每台机器都要部署客户端 | 大规模、跨网络环境 |
| Ansible Shell版本 | 无需额外模块,依赖少 | 交互式输入,需手动选择 | 少量主机,临时修改 |
| Ansible Python版本 | 代码更优雅,扩展性强 | 需要安装passlib库 | 需要更多功能定制 |
🐚 二、Shell版本:快速轻量
2.1 完整代码
#!/bin/bash # ============================================ # Ansible批量修改root密码脚本 (Shell版) # 功能:读取hosts文件、选择主机、生成密码、加密、修改 # 依赖:ansible, python3, passlib # ============================================ # 获取所有定义的主机(过滤空行、注释行、分组行) allhost=`egrep -v '^$|^#|^\[' /etc/ansible/hosts | awk -F ' ' '{print $1}'` now=`date +'%Y-%m-%d %H:%M:%S'` # 显示所有主机 echo -e "\033[32m可用的主机列表:\033[0m" for ip in $allhost do echo $ip done # 选择目标主机 echo -e "\033[33;5m-----------------------\033[0m" read -p "请输入以上其中一台主机: " host # 验证输入是否在列表中 if ! echo "$allhost" | grep -q "^$host$"; then echo -e "\033[31m错误:输入的主机不在列表中!\033[0m" exit 1 fi # 生成15位随机密码(字母+数字) passwd=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15` # 保存密码到文件(审计用) echo "$now $host $passwd" >> ~/script/passwd.txt echo -e "\033[32m主机: $host 密码: $passwd\033[0m" # 使用Python生成SHA512加密密码(Ansible user模块需要加密后的密码) newpass=`/usr/bin/python3 -c "from passlib.hash import sha512_crypt; print(sha512_crypt.using(rounds=5000).hash('$passwd'))"` # 执行Ansible修改密码 ansible $host -m user -a "name=root password='$newpass' update_password=always" if [ $? -eq 0 ]; then echo -e "\033[32m密码修改成功!\033[0m" else echo -e "\033[31m密码修改失败,请检查Ansible连接\033[0m" fi
2.2 运行效果

🐍 三、Python版本:优雅扩展
3.1 完整代码
#!/usr/bin/env python3 # -*- coding:utf-8 -*- # ============================================ # Ansible批量修改root密码脚本 (Python版) # 功能:列出主机索引、选择、生成密码、加密、修改 # 依赖:ansible, passlib # ============================================ import os import random import string from passlib.hash import sha512_crypt def get_hosts(): """从ansible hosts文件读取所有主机""" # 过滤空行、注释行和分组行 cmd = "grep -vE '^$|^#|^\[' /etc/ansible/hosts | awk '{print $1}'" with os.popen(cmd) as f: hosts = [line.strip() for line in f if line.strip()] return hosts def randpass(length=15): """生成指定长度的随机密码(字母+数字)""" chars = string.ascii_letters + string.digits return ''.join(random.choice(chars) for _ in range(length)) def display_hosts(hosts): """带索引显示主机列表""" print("\n\033[32m可用的主机列表:\033[0m") for idx, host in enumerate(hosts): print(f"{idx}: {host}") print("\033[33m-----------------------\033[0m") def main(): # 获取主机列表 hosts = get_hosts() if not hosts: print("\033[31m错误:没有找到有效主机配置!\033[0m") return # 显示主机 display_hosts(hosts) # 选择主机 try: choice = int(input('请选择主机编号: ')) if choice < 0 or choice >= len(hosts): print("\033[31m错误:编号超出范围!\033[0m") return except ValueError: print("\033[31m错误:请输入有效的数字!\033[0m") return # 获取选中的主机 target_host = hosts[choice].strip() # 生成密码 plain_password = randpass() encrypted_password = sha512_crypt.using(rounds=5000).hash(plain_password) # 显示结果 print(f"\n\033[32m您选择的主机: {target_host}") print(f"新密码: {plain_password}\033[0m\n") # 保存密码到文件(审计用) with open(os.path.expanduser('~/script/passwd.txt'), 'a') as f: f.write(f"{target_host} {plain_password}\n") # 执行Ansible修改密码 ansible_cmd = f"ansible {target_host} -m user -a 'name=root password={encrypted_password} update_password=always'" print(f"执行命令: {ansible_cmd}") result = os.system(ansible_cmd) if result == 0: print("\033[32m✅ 密码修改成功!\033[0m") else: print("\033[31m❌ 密码修改失败,请检查Ansible连接\033[0m") if __name__ == '__main__': main()
3.2 运行效果

🔍 四、关键代码解析
4.1 从hosts文件提取主机
# Shell版本:使用egrep过滤空行、注释行和分组行 egrep -v '^$|^#|^\[' /etc/ansible/hosts | awk '{print $1}' # Python版本:使用grep和列表推导 cmd = "grep -vE '^$|^#|^\[' /etc/ansible/hosts | awk '{print $1}'" hosts = [line.strip() for line in os.popen(cmd) if line.strip()]
4.2 随机密码生成
# Shell版本:从/dev/urandom读取随机字符 passwd=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15` # Python版本:使用random和string模块 def randpass(length=15): chars = string.ascii_letters + string.digits return ''.join(random.choice(chars) for _ in range(length))
4.3 密码加密(SHA512)
# 为什么需要加密? Ansible的user模块修改密码时,需要传入加密后的密码字符串,而不是明文。 Linux系统使用/etc/shadow存储加密密码,通常使用SHA512算法($6$开头)。 # 使用passlib库(推荐) from passlib.hash import sha512_crypt encrypted = sha512_crypt.using(rounds=5000).hash('明文密码') # 或者使用crypt库(Python内置) import crypt encrypted = crypt.crypt('明文密码', crypt.mksalt(crypt.METHOD_SHA512))
⚠️ 五、安全注意事项
🔴 1. 密码存储安全
脚本将明文密码保存到~/script/passwd.txt,请确保该文件权限严格:
chmod 600 ~/script/passwd.txt # 或考虑使用加密存储/密码管理工具
🔴 2. Ansible连接安全
确保Ansible使用ssh密钥认证,而不是密码认证(否则陷入死循环)。
🔴 3. 审计日志
所有密码修改操作都应记录日志,包括时间、操作人、目标主机。可以在脚本中添加:
echo "$(date '+%Y-%m-%d %H:%M:%S') $USER 修改 $host 密码" >> /var/log/password_change.log
🔴 4. 密码复杂度
如果系统有密码复杂度要求(如必须包含特殊字符),修改randpass函数:
chars = string.ascii_letters + string.digits + "!@#$%^&*()"
📝 六、扩展与改进
6.1 批量修改多台主机
# 修改Python版本,支持多选(输入1,3,5或1-5)
choices = input('请选择主机编号(多个用逗号分隔): ')
# 解析选择并循环执行
6.2 使用ansible-playbook的替代方案
# 编写playbook change_pass.yml - hosts: "{{ target }}" tasks: - name: Change root password user: name: root password: "{{ new_password | password_hash('sha512') }}" update_password: always # 执行 ansible-playbook -e "target=web01 new_password={{ 生成的密码 }}" change_pass.yml
6.3 集成密码管理工具
可以将生成的密码自动上传到Vault或KeePass等密码管理工具,避免明文文件存储。
📊 七、总结
本文提供了两种基于Ansible的批量密码修改方案:
- ✅ Shell版本:简单轻量,依赖少,适合快速使用
- ✅ Python版本:代码优雅,交互友好,易于扩展
相比之前用Python socket客户端上报的方式,Ansible主控端修改省去了客户端部署的麻烦,特别适合已经用Ansible管理的环境。关键是安全处理密码存储和加密,避免明文传输和存储风险。
—— 从被动上报到主动管理,运维工具在进化,我们的技能也要跟上。

浙公网安备 33010602011771号