Ansible SSH免密登录实战:从密码配置到公钥部署的自动化运维指南

Ansible SSH免密登录实战:从密码配置到公钥部署的自动化运维指南 1. 项目概述从手动运维到自动化密钥管理的跨越在运维和开发工作中你是否厌倦了每次登录服务器都要手动输入密码或者在使用Ansible等自动化工具时被“destination host unreachable”或“permission denied”这类错误反复折磨今天要聊的就是解决这些痛点的核心技能之一在Ansible的host清单中配置密码并最终实现通过SSH公钥免密登录。这不仅仅是敲几个命令而是理解自动化运维中身份认证的底层逻辑是从“手动操作员”向“自动化架构师”转变的关键一步。很多人刚开始接触Ansible时会直接在ansible.cfg或命令行里写死密码或者更糟在剧本里明文存储。这不仅是安全隐患更是运维的噩梦——一旦密码变更所有脚本都得跟着改。而SSH公钥认证则是解决这个问题的标准答案。它就像给你的服务器配了一把独一无二的“物理钥匙”私钥而目标主机上只存放对应的“锁芯”公钥。你只需要保管好私钥就能畅通无阻地打开所有装了你公钥的“锁”。这个过程就是实现高效、安全自动化操作的基础。接下来我会带你从最基础的密码配置开始一步步走到完全免密的公钥部署并分享其中每一步的“坑”与“技巧”。2. 核心思路与方案选型为何要告别明文密码在深入操作之前我们必须搞清楚为什么要大费周章地配置公钥免密。直接使用密码不是更简单吗这里涉及到几个核心考量安全性、可靠性和自动化效率。安全性是首要驱动力。在/etc/ansible/hosts文件或剧本变量中明文存储root密码无异于将大门钥匙挂在门口。一旦配置文件泄露整个基础设施将门户大开。而SSH密钥对公钥和私钥采用非对称加密私钥本地严格保管公钥即使公开也无法反向推导出私钥从根本上杜绝了密码泄露的风险。可靠性关乎自动化流程的稳定性。你是否遇到过执行ansible all -m ping时因为某台主机SSH服务偶尔卡顿导致整个任务超时失败或者在使用--ask-pass交互式输入密码时无法集成到CI/CD流水线中明文密码认证依赖于网络稳定性和SSH服务端的密码认证功能任何波动都可能造成“failed to connect to any host resolved for dns name”或认证失败。公钥认证是SSH连接中更稳定、更快速的认证方式它避免了密码协商过程连接建立速度更快对网络抖动的容忍度也更高。自动化效率是终极目标。我们使用Ansible的初衷是“一键部署”、“批量管理”。如果每个任务都需要人工干预输入密码或者为处理密码而编写复杂的异常逻辑那就本末倒置了。公钥免密是实现真正无人值守自动化的基石。它让Ansible可以像在本地执行命令一样在成百上千台远程主机上静默、可靠地运行。那么方案如何选型通常有两种路径临时过渡方案在Ansible主机清单中配置SSH密码用于初始环境搭建和公钥分发。这是“先有鸡还是先有蛋”问题的解决方案——你需要先有权限登录才能上传公钥。最终目标方案在所有被管理主机上部署Ansible控制机的SSH公钥实现完全免密认证。这是生产环境的标准实践。我们的操作将清晰地分为这两个阶段第一阶段是为第二阶段铺路。这里有一个关键心得永远不要在生产环境的自动化脚本中硬编码任何密码即使是临时方案也应使用Ansible Vault等加密工具进行管理或在执行后立即清理痕迹。3. 第一阶段实操在Ansible Host清单中配置SSH密码这个阶段的目标是获得一个初始的、可用的认证方式以便我们能够登录目标主机并执行后续的公钥部署操作。我们主要操作的是Ansible的清单文件。3.1 理解Ansible清单文件Ansible的清单Inventory定义了你要管理的主机集合。默认位置是/etc/ansible/hosts但你也可以使用-i参数指定自定义文件。清单不仅仅是IP列表它支持分组、变量定义其中就包括连接认证变量。一个基础的清单文件可能长这样[webservers] 192.168.1.101 192.168.1.102 [dbservers] 192.168.1.201要为这些主机配置SSH密码我们需要添加连接变量。这里有几种做法各有优劣。3.2 配置密码的三种方法及避坑指南方法一主机变量直接写在IP后这是最直接的方式适合主机数量少、密码相同的情况。[webservers] 192.168.1.101 ansible_ssh_userroot ansible_ssh_passYourPassword123 192.168.1.102 ansible_ssh_userroot ansible_ssh_passYourPassword123注意ansible_ssh_pass是较新版本Ansible中用于SSH密码的参数。在老版本如Ansible 2.5之前中可能使用ansible_password。务必根据你的Ansible版本确认。使用单引号包裹密码可以防止shell解析其中的特殊字符。方法二组变量写在[group_name:vars]部分当一组主机使用相同的认证信息时用组变量更简洁便于维护。[webservers] 192.168.1.101 192.168.1.102 [webservers:vars] ansible_ssh_userroot ansible_ssh_passYourPassword123 ansible_ssh_port22 # 默认是22如果修改过则需指定方法三使用加密的变量文件推荐用于临时需求明文密码终究是隐患即使是临时使用。对于稍正式的临时操作可以使用Ansible Vault加密变量文件。 首先创建一个变量文件如host_vars/passwords.yml--- ansible_ssh_user: root ansible_ssh_pass: YourPassword123然后用ansible-vault加密它ansible-vault encrypt host_vars/passwords.yml系统会提示你输入一个加密密码。之后这个passwords.yml文件就变成了密文。在运行Playbook时你需要通过--ask-vault-pass参数提供解密密码或者将解密密码写入文件并通过--vault-password-file指定。实操心得与常见问题权限问题确保清单文件如/etc/ansible/hosts的权限设置合理如644避免被无关用户读取。SSH服务配置目标主机的/etc/ssh/sshd_config必须允许密码认证。检查PasswordAuthentication yes这一行是否被注释或设为no。修改后需重启SSH服务systemctl restart sshd。连接测试配置好后不要急于运行复杂剧本。先用ansible命令进行连接测试ansible webservers -m ping -k这里的-k参数是--ask-pass的缩写它会提示你输入SSH密码。注意如果你已经在清单中配置了ansible_ssh_pass则不应使用-k否则会冲突。直接使用ansible webservers -m ping即可。“Host key verification failed”错误首次连接一台新主机时SSH会询问是否将主机密钥加入已知列表。在自动化中这会中断流程。解决方法是在ansible.cfg中配置[defaults] host_key_checking False请注意这降低了安全性容易遭受中间人攻击仅建议在可控的、信任的内部网络环境中使用。生产环境更佳实践是预先通过安全渠道将主机指纹host key分发到Ansible控制机的~/.ssh/known_hosts文件中。4. 第二阶段核心部署SSH公钥实现免密登录通过第一阶段的密码认证我们已经获得了目标主机的访问权。现在我们的核心任务是将Ansible控制机或指定用户的SSH公钥部署到所有目标主机上对应的用户目录下。这是实现永久免密登录的关键。4.1 SSH密钥对的工作原理与生成简单来说SSH密钥对包含一把私钥和一把公钥。私钥必须像保护密码一样严格保密存放在Ansible控制机上如~/.ssh/id_rsa。它是你身份的证明。公钥可以公开分发需要被放置到目标主机的相应用户的~/.ssh/authorized_keys文件中。它像一把锁只有对应的私钥才能打开。生成密钥对如果还没有的话ssh-keygen -t rsa -b 4096 -C ansible-control-host -f ~/.ssh/ansible_id_rsa-t rsa指定密钥类型为RSA。Ed25519也是目前推荐的选择-t ed25519它更安全且更快。-b 4096指定密钥长度为4096位安全性更高。-C添加一个注释通常用邮箱或标识这里我们标记为“ansible-control-host”。-f指定生成密钥文件的路径和名称。这里我们生成一个专用于Ansible的密钥与个人密钥隔离便于管理。执行命令后你会得到两个文件~/.ssh/ansible_id_rsa私钥和~/.ssh/ansible_id_rsa.pub公钥。务必确保私钥文件权限为600(chmod 600 ~/.ssh/ansible_id_rsa)。4.2 使用Ansible模块批量部署公钥有了密钥和初始密码认证我们就可以使用Ansible的authorized_key模块来批量部署公钥。这是最优雅、最标准的方式。创建一个简单的Playbook例如deploy_ssh_key.yml--- - name: Deploy SSH public key for passwordless login hosts: all # 针对清单中的所有主机 gather_facts: no # 初始部署可能不需要收集facts加快速度 tasks: - name: Ensure .ssh directory exists ansible.builtin.file: path: ~/.ssh state: directory mode: 0700 - name: Deploy public key ansible.builtin.authorized_key: user: root # 指定要部署到的目标用户这里以root为例 state: present key: {{ lookup(file, ~/.ssh/ansible_id_rsa.pub) }}运行这个Playbookansible-playbook -i /path/to/your/inventory deploy_ssh_key.yml --ask-pass # 或者如果你在清单中配置了密码则直接 ansible-playbook deploy_ssh_key.yml这个Playbook做了两件事确保目标用户的~/.ssh目录存在且权限为700只有所有者可读、写、执行。将Ansible控制机上~/.ssh/ansible_id_rsa.pub文件的内容添加到目标主机对应用户的~/.ssh/authorized_keys文件中。state: present确保是添加而非覆盖。高级技巧与参数解析指定特定密钥如果你生成了多个密钥对可以使用key参数指定公钥内容或者使用path参数指定远程主机上已有公钥文件的路径。排他性部署使用exclusive: yes参数这会使得authorized_keys文件中只保留本次指定的公钥删除其他所有密钥。请谨慎使用除非你确定要移除其他所有授权。管理多个密钥你可以将公钥内容存放在一个变量文件中然后循环部署多个密钥。为不同用户部署通过user参数指定不同用户可以为root、deploy、admin等不同账户部署相同的管理密钥。4.3 验证免密登录与清理密码配置公钥部署完成后必须进行验证。最直接的测试方式是使用ssh命令ssh -i ~/.ssh/ansible_id_rsa root192.168.1.101如果不需要输入密码就能直接登录说明公钥部署成功。接下来在Ansible中测试。此时你需要告诉Ansible使用我们新生成的私钥。有两种方式方式一在清单或组变量中指定私钥路径在/etc/ansible/hosts或组变量中移除ansible_ssh_pass添加[webservers:vars] ansible_ssh_userroot ansible_ssh_private_key_file~/.ssh/ansible_id_rsa # ansible_ssh_passYourPassword123 # 注释掉或删除这一行方式二在ansible.cfg中全局指定如果所有主机使用同一密钥在ansible.cfg的[defaults]部分添加[defaults] private_key_file ~/.ssh/ansible_id_rsa现在运行一个不需要特权sudo的命令来测试ansible webservers -m ping如果返回成功的pong恭喜你免密登录配置成功最后一步安全加固与清理禁用目标主机的SSH密码认证为了提高安全性建议在目标主机的/etc/ssh/sshd_config中将PasswordAuthentication设置为no然后重启sshd服务。务必在确认公钥登录100%工作正常后再进行此操作你可以用Ansible批量完成- name: Disable SSH password authentication hosts: all tasks: - name: Update sshd_config ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: ^#?PasswordAuthentication line: PasswordAuthentication no state: present notify: restart sshd handlers: - name: restart sshd ansible.builtin.service: name: sshd state: restarted从Ansible清单中移除明文密码这是至关重要的一步。删除或注释掉所有ansible_ssh_pass变量行。如果你使用了加密的变量文件可以保留但不再使用或者直接删除加密文件。妥善保管私钥私钥文件ansible_id_rsa是你的“万能钥匙”。建议将其存放在安全的位置并设置严格的文件权限600。可以考虑使用ssh-agent来管理私钥避免将私钥文件到处拷贝。5. 深度优化与生产环境实践基础的免密登录搭建完成后在生产环境中我们还需要考虑更多因素以实现安全、高效、可维护的自动化运维体系。5.1 多环境、多角色密钥管理策略在复杂环境中你可能需要管理开发、测试、生产等多个环境或者为数据库管理员、应用部署员、监控系统等不同角色配置不同的密钥。推荐策略基于Ansible Vault的密钥管理为不同环境/角色生成独立密钥对例如id_rsa_prod,id_rsa_devops。使用group_vars和host_vars在清单中定义[prod],[dev]等分组以及[db_servers],[web_servers]等角色分组。在组变量中定义密钥路径group_vars/prod.yml:--- ansible_ssh_private_key_file: ~/.ssh/keys/id_rsa_prod ansible_ssh_user: prod_admingroup_vars/dev.yml:--- ansible_ssh_private_key_file: ~/.ssh/keys/id_rsa_dev ansible_ssh_user: dev_user加密敏感变量如果私钥路径或用户名本身敏感可以将这些group_vars文件用ansible-vault加密。目录结构示例inventory/ ├── production/ │ ├── hosts │ └── group_vars/ │ └── all.yml (加密) ├── development/ │ ├── hosts │ └── group_vars/ │ └── all.yml └── keys/ ├── id_rsa_prod ├── id_rsa_prod.pub ├── id_rsa_dev └── id_rsa_dev.pub5.2 使用SSH-Agent进行会话级密钥管理频繁在Playbook或配置中指定私钥路径并不方便且私钥密码如果设置了需要反复输入。ssh-agent是一个守护进程它可以托管你的解密的私钥在SSH会话期间提供认证服务。基本使用流程启动ssh-agent并添加私钥eval $(ssh-agent -s) # 启动agent并设置环境变量 ssh-add ~/.ssh/ansible_id_rsa # 添加私钥会提示输入私钥密码如果有现在运行Ansible命令或SSH登录时将自动使用agent中的密钥无需再指定-i或配置private_key_file。你可以将ssh-agent和ssh-add命令添加到你的shell启动脚本如~/.bashrc或~/.zshrc中实现登录后自动加载。注意事项ssh-agent带来的便利也伴随着风险。任何能够访问你当前会话的程序都可能使用已加载的密钥。在不安全的共享环境中需慎用。使用完毕后可以用ssh-add -D删除所有已加载的密钥。5.3 应对复杂网络与跳板机场景在实际企业网络中Ansible控制机可能无法直接访问所有目标主机如处于不同VPC、有防火墙隔离。这时就需要通过跳板机Bastion Host或使用SSH ProxyJump/ProxyCommand。Ansible配置SSH通过跳板机连接在ansible.cfg或清单变量中配置[ssh_connection] ssh_args -o ProxyJumpbastion_userbastion_host_ip:22 -o ControlMasterauto -o ControlPersist30m control_path ~/.ssh/ansible-%%r%%h:%%p或者在主机变量中更精细地控制[target_hosts] internal_host_1 ansible_host10.0.1.10 ansible_ssh_common_args-o ProxyJumpbastion_user54.32.1.1关键点跳板机本身也需要配置免密登录到目标主机或者Ansible控制机需要通过跳板机转发其认证信息。这通常需要在跳板机上配置ForwardAgent yes并在Ansible控制机上使用ssh-agent。这是一个更高级的话题涉及~/.ssh/config的详细配置。6. 故障排查与经验实录即使按照步骤操作你也可能会遇到各种问题。下面是我在多年实践中总结的一些常见“坑”及其解决方案。6.1 公钥部署后仍提示输入密码这是最常见的问题。请按以下顺序排查检查目标主机authorized_keys文件权限文件权限必须是600或644。文件所有者必须是目标用户。.ssh目录权限必须是700。 权限错误是导致SSH公钥认证失败的元凶之一。可以使用Ansible的stat模块远程检查ansible problem_host -m shell -a ls -la ~/.ssh/; cat ~/.ssh/authorized_keys检查公钥内容是否正确确保部署到authorized_keys中的公钥内容完整是一行且没有多余空格或换行。可以使用ssh-keygen -l -f ~/.ssh/authorized_keys检查密钥指纹是否与控制机上的公钥一致。检查目标主机SSH服务配置确保/etc/ssh/sshd_config中包含且未注释PubkeyAuthentication yes。检查AuthorizedKeysFile的路径设置默认是.ssh/authorized_keys .ssh/authorized_keys2。检查是否有AllowUsers或DenyUsers限制。修改配置后需重启sshdsystemctl restart sshd。检查SELinux或AppArmor在某些严格的安全策略下SELinux可能会阻止非标准目录或权限的.ssh目录访问。可以暂时将SELinux设置为宽容模式测试setenforce 0。如果问题解决则需要为.ssh目录设置正确的SELinux上下文restorecon -Rv ~/.ssh/。6.2 Ansible连接超时或“Host key verification failed”超时问题检查网络连通性ansible target_host -m ping前先用ping或nc -zv host port测试。在清单中调整ansible_ssh_timeout和ansible_ssh_connect_timeout变量适当增加超时时间。Host key验证失败如前所述在ansible.cfg中设置host_key_checking False可以绕过但有安全风险。更好的做法是预先收集所有主机密钥并部署到Ansible控制机的known_hosts文件中。可以使用ssh-keyscan命令批量收集ssh-keyscan -H 192.168.1.101 192.168.1.102 ~/.ssh/known_hosts或者使用Ansible的known_hosts模块来管理。6.3 权限提升sudo与公钥认证的结合很多时候我们使用非root用户如deploy通过公钥登录然后需要sudo权限执行任务。这需要在目标主机上配置sudo并在Ansible中正确设置。目标主机sudo配置确保Ansible使用的用户在/etc/sudoers文件中配置了无需密码执行特定或全部命令的权限。例如deploy ALL(ALL) NOPASSWD: ALL注意NOPASSWD存在安全风险应根据最小权限原则细化命令列表。Ansible配置在Playbook或清单变量中设置ansible_become: yes # 等价于旧版的 ansible_sudo ansible_become_user: root # 要切换到的用户默认为root ansible_become_method: sudo # 提升权限的方法默认为sudo # ansible_become_pass: sudo_password # 如果需要sudo密码但我们已经配置了NOPASSWD所以不需要6.4 关于“记住密码”和密钥密码的权衡在生成SSH密钥时ssh-keygen会询问你是否为私钥设置一个密码短语passphrase。这是一个重要的安全特性。设置密码短语即使私钥文件被盗没有密码短语也无法使用。但每次使用私钥时都需要输入除非使用ssh-agent。这为自动化带来了不便。不设置密码短语私钥即拿即用非常方便自动化。但一旦私钥泄露攻击者就能直接访问所有系统。生产环境建议为用于自动化运维的专用密钥设置强密码短语。在受控的、安全的Ansible控制机上使用ssh-agent在会话开始时一次性输入密码短语之后在整个会话期内自动管理。这样既保证了密钥使用的便利性又在私钥文件泄露时提供了保护。定期轮换更新密钥对就像定期更换密码一样。从在Ansible清单中配置密码开始到最终实现基于公钥的免密登录这个过程是构建自动化运维信任基石的标准路径。它不仅仅是执行一系列命令更是对安全性、可靠性和可维护性的一次系统化实践。记住自动化运维的第一原则是“信任但要验证”而SSH公钥基础设施正是实现这一原则的完美工具。当你不再需要为登录密码而分心时才能真正专注于编写那些让服务器“翩翩起舞”的精彩Playbook。