OpenEuler 24.03 用 Ansible 一键完成 SSH 互信 —— 从踩坑到最终方案
用 Ansible 一键完成 SSH 互信 —— 从踩坑到最终方案
适用读者:需要在多台 Linux 服务器之间快速建立免密登录(SSH 互信)的运维 / 开发人员。
环境:控制节点 + 若干 CentOS 7/8、Rocky、Ubuntu 混合目标机;Ansible 2.9/2.10+。
1. 背景
在批量运维场景里,先手动逐台 ssh-copy-id
太低效。最理想的做法是 一条 Playbook 就把公钥下发到所有服务器。乍一看只需一个 authorized_key
模块,但在真实环境会碰到:
-
SELinux 依赖:目标机启用 SELinux,却没安装
python{,3}-libselinux
,导致模块 abort。 -
解释器发现 Warning:
Platform linux … discovered Python interpreter at /usr/bin/python3 …
。 -
包名差异:CentOS 7 与 8+、Debian/Ubuntu 的 SELinux 绑定包名不同。
-
密钥缺失:控制节点可能还没生成
id_rsa.pub
。 -
密码安全:不想把
ansible_ssh_pass
写死在 Playbook。
本文记录一次“从红到全绿”的完整踩坑与修正过程。
2. 问题与排查
序号 | 现象 | 根因 | 解决思路 |
---|---|---|---|
1 | Aborting, target uses selinux but python bindings (libselinux-python) aren't installed! | 目标机启用 SELinux,但缺少 Python 绑定库 | 先用 raw 命令安装正确的库,再跑正常模块 |
2 | No package libselinux-python available | CentOS 8+ / Rocky 没有该包,正确包名是 python3-libselinux | 根据包管理器自动分支安装 |
3 | bash: 行 X: 语法错误:未预期的文件结束符 | raw 多行脚本缩进 / 换行被拼坏 | 用 YAML 折行字符串 > ,每段结尾加 ; |
4 | Platform linux … discovered Python interpreter … | 解释器自动探测,仅 Warning | 可在 inventory 指定 ansible_python_interpreter=/usr/bin/python3 ;也可忽略 |
5 | 没有公钥导致 lookup('file', '~/.ssh/id_rsa.pub') 报错 | 控制节点无密钥 | openssh_keypair 预生成,幂等且只运行一次 |
6 | Playbook 定义了两个 vars: 块 | 前后覆盖,易混淆 | 合并为一个 vars |
3. 关键技术点
3.1 用 raw
绕过 Ansible 模块依赖
-
为什么:
yum
/dnf
/apt
模块自身也要 importselinux
,装库前就会失败。 -
做法:
raw
仅走 SSH,不依赖远端 Python 环境,先把依赖装好。
raw: >if command -v dnf >/dev/null; then dnf -y install python3-libselinux;elif command -v yum >/dev/null; then yum -y install libselinux-python3 || yum -y install libselinux-python;elif command -v apt-get >/dev/null; then apt-get update -qq && apt-get install -y python3-selinux;fi
3.2 控制节点自动生成密钥
-
只在 localhost 执行,且
run_once: true
。 -
openssh_keypair
幂等:已有密钥就跳过。
- openssh_keypair:path: "{{ local_key_path }}"type: rsasize: 4096state: presentwhen: not key_stat.stat.existsdelegate_to: localhostrun_once: true
3.3 交互输入密码 / 后续免密
-
Playbook 加
vars_prompt
;首次执行带-k
。 -
公钥下发后,再运行无需输入密码。
4. 最终 Playbook(ssh_trust.yml)
---
- name: 自动生成 SSH 密钥并配置免密登录hosts: allgather_facts: novars_prompt:- name: ansible_ssh_passprompt: "请输入目标主机 SSH 密码"private: yesvars:ansible_user: rootlocal_key_path: "{{ lookup('env', 'HOME') + '/.ssh/id_rsa' }}"pre_tasks:- name: 检查控制节点是否已有公钥ansible.builtin.stat:path: "{{ local_key_path }}.pub"register: key_statdelegate_to: localhostrun_once: true- name: 如果没有就生成密钥对(控制节点)ansible.builtin.openssh_keypair:path: "{{ local_key_path }}"type: rsasize: 4096state: presentwhen: not key_stat.stat.existsdelegate_to: localhostrun_once: truetasks:- name: 安装 SELinux Python 依赖(raw)raw: >if command -v dnf >/dev/null; then dnf -y install python3-libselinux;elif command -v yum >/dev/null; then yum -y install libselinux-python3 || yum -y install libselinux-python;elif command -v apt-get >/dev/null; then apt-get update -qq && apt-get install -y python3-selinux;fibecome: true- name: 确保远端 .ssh 目录存在ansible.builtin.file:path: "/root/.ssh"state: directorymode: '0700'owner: rootgroup: rootbecome: true- name: 部署公钥到目标主机ansible.posix.authorized_key:user: rootstate: presentkey: "{{ lookup('file', local_key_path + '.pub') }}"
5. 使用方法
# hosts.ini 里列出所有目标 IP
ansible-playbook -i hosts.ini ssh_trust.yml -k
-
首次:输入密码,下发公钥。
-
再次运行:已免密,无需
-k
。 -
若想彻底消除解释器 Warning,在 inventory 加:
ansible_python_interpreter=/usr/bin/python3
6. 收获与最佳实践
-
SELinux 依赖先装再用,必要时用
raw
。 -
包名与发行版强相关,写脚本时用分支判断。
-
密钥生成与部署保持幂等,
run_once
+authorized_key
。 -
敏感信息交互输入或用 Vault,避免明文密码。
-
读报错要抓关键字,一次定位核心问题。
至此,一份可跨发行版的“一键互信” Playbook 就完成了。希望本文能帮你少走弯路,快速把绿色 ok=… failed=0
留在终端。祝运维愉快!