如果密钥被拒绝,如何让 Ansible 使用密码?

如果密钥被拒绝,如何让 Ansible 使用密码?

我的新服务器实例配置为通过 ssh 使用密码以 root 身份登录。我希望我的 Ansible playbook 将其重新配置为使用密钥,并在第一次运行时禁用使用密码的 root 登录,因此我需要类似这样的操作:

  • 尝试使用密钥登录
  • 如果无法使用密钥登录:

    • 使用密码登录
    • 将密钥添加到 authorized_keys
    • 禁用 root 用户密码登录
    • 可选择使用密钥重新连接
  • 做其他任务

我怎样才能做到这一点?

编辑:需要说明的是,我不是在问如何添加密钥或禁用 root,这只是为了了解上下文。我是在问如果无法使用密钥进行身份验证,如何回退到密码。设置--ask-passansible_ssh_pass后,Ansible 甚至不会尝试使用公钥身份验证

答案1

您可以尝试PreferredAuthentications将选项设置为publickey,password。默认按此顺序包含这些以及其他选项,因此 ansible 可能正在设置它。通过-o或客户端添加它ssh_config可能会阻止这种情况。

您可能能够使用包装器脚本。例如,使用此脚本key_or_password.shpass.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_errorschanged_when: False
  • 如果失败,则更新ansible_useransible_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.yamlplaybook.yaml然后将在所有主机上运行,​​并且可以包含所需的任何任务以确保初始访问检查在下次运行时成功。

虽然将check-access.yaml任务保存在单独的文件中确实使主剧本更易于阅读,但可以将它们纳入主剧本 - 但是我还没有尝试过,因此不能确定它们是否会按正确的顺序运行。

请注意,这些任务不能被置于一个角色中,因为prompt只在任务中起作用

相关内容