我的新服务器实例配置为通过 ssh 使用密码以 root 身份登录。我希望我的 Ansible playbook 将其重新配置为使用密钥,并在第一次运行时禁用使用密码的 root 登录,因此我需要类似这样的操作:
- 尝试使用密钥登录
如果无法使用密钥登录:
- 使用密码登录
- 将密钥添加到 authorized_keys
- 禁用 root 用户密码登录
- 可选择使用密钥重新连接
做其他任务
我怎样才能做到这一点?
编辑:需要说明的是,我不是在问如何添加密钥或禁用 root,这只是为了了解上下文。我是在问如果无法使用密钥进行身份验证,如何回退到密码。设置--ask-pass
或ansible_ssh_pass
后,Ansible 甚至不会尝试使用公钥身份验证
答案1
您可以尝试PreferredAuthentications
将选项设置为publickey,password
。默认按此顺序包含这些以及其他选项,因此 ansible 可能正在设置它。通过-o
或客户端添加它ssh_config
可能会阻止这种情况。
您可能能够使用包装器脚本。例如,使用此脚本key_or_password.sh
和pass.sh
提供密码的脚本,运行bash key_or_password.sh root@host
将尝试使用公钥,然后进行非交互式密码登录。
export DISPLAY=dummy:0
export SSH_ASKPASS=$PWD/pass.sh
exec setsid ssh -v -o 'PreferredAuthentications publickey,password' "$@"
日志指示哪种方法成功,例如
debug1: Authentication succeeded (publickey).
答案2
如果剧本的“首次运行”有所不同,我会执行以下操作ansible_user
(例如,如果您只有一个root
用户,并且您要使用 SSH 密钥设置一个新用户):
ansible_pass
像使用密码登录一样存储密码(记得使用 Vault),这应该是您在剧本“首次运行”时使用的用户的密码。ansible_user
当您在服务器上正确设置用户后,将其设置为您希望在第一次运行后使用的用户的用户名。- 将变量设置
ansible_user_first_run
为您将用于剧本“首次运行”的用户,例如root
。 - 使用本地命令尝试使用正确的 SSH 密钥连接到服务器,使用
ignore_errors
和changed_when: False
- 如果失败,则更新
ansible_user
为ansible_user_first_run
代码如下:
---
- name: Check if connection is possible
command: ssh -o User={{ ansible_user }} -o ConnectTimeout=10 -o PreferredAuthentications=publickey -o PubkeyAuthentication=yes {{ inventory_hostname }} echo "Worked"
register: result
connection: local
ignore_errors: yes
changed_when: False
- name: If no connection, change user_name
connection: local
set_fact:
ansible_user: "{{ ansible_user_first_run }}"
when: result|failed
注意:值得设置transport = ssh
,因为 paramiko 在某些配置下可能会意外无法登录服务器(例如,当服务器设置为不接受密码时,您首先尝试使用密钥然后输入密码...很奇怪!)此外 ssh 传输速度更快,所以无论如何都是值得的。
进一步注意:如果您使用此方法,则需要gather_facts: false
在剧本定义文件中指定,以便在进入测试密码阶段之前不会自动运行设置/事实收集任务。如果您需要任何 ansible 事实,则需要setup
在访问通常在 等位置可用的任何数据之前明确调用您的角色ansible_devices
。执行此操作的一个好方法是setup
使用一个when
子句进行调用,该子句检查您在角色中调用它之前使用的事实是否为空。
答案3
--ask-pass
运行 ansible-playbook 时即可使用。
对于您要求的其他任务,可以通过各种方式实现,例如复制模块。
禁用 root 登录也可以通过模板sshd_conf
或在 conf 文件中插入行来实现。
答案4
我最近实现了一个变体回答给出的是汤姆·布尔 它适用于多台主机,按顺序检查访问权限并在需要时提示输入凭据,然后运行任务以完成设置(例如,添加授权密钥或为 Ansible 设置用户):
playbook.yaml
:
# Checking access to hosts must run sequentially
- hosts: all
gather_facts: no
serial: 1
tasks:
- include_tasks: check-access.yaml
# Subsequent tasks can run in parallel as usual
- hosts: all
tasks:
- include_tasks: setup-tasks.yaml
check-access.yaml
:
- name: check ansible user exists
command: ssh -o User={{ ansible_user }} -o ConnectTimeout=10 -o PreferredAuthentications=publickey -o PubkeyAuthentication=yes {{ ansible_host }} echo "Worked"
register: remote
connection: local
ignore_errors: yes
become: false
changed_when: False
- name: collect credentials
block:
- name: collect username
pause:
prompt: "Username for {{ inventory_hostname }} ({{ ansible_host }})"
echo: yes
register: username_result
- name: collect password
pause:
prompt: "Password for {{ username_result.user_input }}"
echo: no
register: password_result
- name: update ansible credentials
set_fact:
ansible_user: "{{ username_result.user_input }}"
ansible_password: "{{ password_result.user_input }}"
ansible_become_password: "{{ password_result.user_input }}"
when: remote.failed
setup-tasks.yaml
playbook.yaml
然后将在所有主机上运行,并且可以包含所需的任何任务以确保初始访问检查在下次运行时成功。
虽然将check-access.yaml
任务保存在单独的文件中确实使主剧本更易于阅读,但可以将它们纳入主剧本 - 但是我还没有尝试过,因此不能确定它们是否会按正确的顺序运行。
请注意,这些任务不能被置于一个角色中,因为prompt
只在任务中起作用