如何在两步验证情况下使用Ansible?

14

我已经使用DuoSecurity为ssh启用了双重身份验证(使用此playbook:https://github.com/CoffeeAndCode/ansible-duo)。

现在如何使用Ansible来管理服务器。由于这个原因,SSH调用在收集信息时失败。我希望运行playbook的人在运行之前输入双重身份验证代码。

禁用部署用户的两步验证是一种可能的解决方案,但这会创建一个安全问题,我想要避免。


1
在我能想到的几乎所有情况下,将堡垒主机锁定在您的网络内并使用双因素身份验证会是更好的设计选择,并允许Ansible在该网络内无需进行身份验证即可进行ssh。正如下面(技术上正确!)的答案所说,任何涉及如此大量的繁重设置的解决方案都会在规模上带来如此之多的痛苦,以至于它将消除使用Ansible的优势。 - nikobelia
3个回答

9

这是一种非常规的方法,但你可以通过一个启用了两步验证的SSH连接来隧道传输一个没有启用两步验证的Ansible SSH连接。

概述

我们将设置两个用户:ansible将是Ansible使用的用户。它应该以Ansible支持的方式进行身份验证(即不包括2步验证)。此用户将受到限制,只能从127.0.0.1连接,因此无法从机器外部访问。

第二个用户ansible_tunnel将向外界开放,但需要经过两步验证进行身份验证,并且只允许将SSH连接隧道传输到本地机器。

您必须能够为某些用户配置双重身份验证(而不是全部用户)。

有关SSH隧道的一些信息

在目标机器上:

  1. Create two users: ansible and ansible_tunnel
  2. Put your public key in ~/.ssh/authorized_keys of both users
  3. Set the shell of ansible_tunnel to /bin/false, or lock the user - it will be used for tunneling exclusively, not running commands
  4. Add the following to /etc/ssh/sshd_config:

    AllowTcpForwarding no
    
    AllowUsers ansible@127.0.0.1 ansible_tunnel
    
    Match User ansible_tunnel
      AllowTcpForwarding yes
      PermitOpen 127.0.0.1:22
      ForceCommand echo 'This account can only be used for tunneling SSH sessions'
    
  5. Setup 2-factor authentication only for ansible_tunnel
  6. Restart sshd

在运行Ansible的机器上:

  1. Before running Ansible, run the following (on the Ansible machine, not the target):

    ssh -N -L 8022:127.0.0.1:22 ansible_tunnel@<host>
    

    You will be authenticated using two factors.

  2. Once the tunnel is up (check with netstat), run Ansible with ansible_ssh_user=ansible, ansible_ssh_port=8022 and ansible_ssh_host=localhost.

总结

  • 只有ansible_tunnel可以从外部连接,并且需要使用两个因素进行身份验证
  • 一旦建立隧道,连接本地机器上的端口8022就相当于连接远程机器上的sshd
  • 我们仅允许ansible通过本地主机连接SSH,因此只允许通过隧道连接

扩展

对于多台服务器,这种方法不太适用,因为需要为每台机器单独打开一个隧道,这需要手动操作。但是,如果您已经为服务器选择了双因素身份验证,那么您已经愿意为连接到每台服务器执行一些手动操作,而这种解决方案只会增加一些脚本封装的开销。

[编辑以添加]

额外奖励

为了方便起见,我们可能想直接登录维护帐户以执行一些手动工作,而不必经过设置隧道的过程。在这种情况下,我们可以配置SSH要求进行双因素身份验证,同时保持通过隧道连接而无需进行双因素身份验证的能力:

# All users must authenticate using two factors
AuthenticationMethods publickey,keyboard-interactive

# Allow both maintenance user and tunnel user with no restrictions
AllowUsers ansible ansible_tunnel

# The maintenance user is allowed to authenticate using a single factor only
# when connecting from a local address - it should be impossible to connect to
# this user using a single factor from the outside (the only way to do that is
# having an existing access to the machine, or use the two-factor tunnel)
Match User ansible Address 127.0.0.1
  AuthenticationMethods publickey

很棒的答案,这似乎是比我在ansible邮件列表或github问题中找到的任何解决方案都更好的解决方案,虽然有点像黑客技巧。将ansible_tunnel用户设置为仅限隧道的独立用户的好处是什么?看起来你可以允许每个用户仅使用来自127.0.0.1的公钥身份验证,然后只涉及一个用户,实际维护命令作为你自己运行。 - danny
我认为你可以设置一个单一用户,允许来自外部的2fac-only,但仅允许来自127.0.0.1的pubkey-only,并将该用户用于隧道和ansible(即将该用户隧道到自身,哈哈)。我只是喜欢保持角色分离 - 以防我为隧道用户错误配置SSH,它仍然不被允许运行命令。 - duvduv

8

我可以使用ssh和ansible的ControlMaster功能以及2FA。

我的本地ssh客户端配置了一个ControlPath套接字以进行多路连接复用。Ansible被配置为使用相同的套接字。

本地ssh客户端

此配置可为所有连接启用多路复用。我个人将此配置存储在`~/.ssh/config`中:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/master-%r@%h:%p.socket
    ControlPersist 1m

当连接建立时,一个套接字将出现在$HOME/.ssh目录中。该套接字会在断开连接后持续一分钟。

配置ansible

Ansible被配置为重复使用本地套接字。

在您的ansible配置文件(例如~/.ansible.cfg)中添加以下内容:

[ssh_connection]

control_path=~/.ssh/master-%%r@%%h:%%p.socket

请注意变量替换时需要使用双 % 符号。

用法

  1. 使用ssh普通命令连接到您的服务器(ssh user@server),然后进行2FA身份验证;
  2. 像往常一样启动您的ansible命令。

步骤2必须在ControlPersist配置中执行,或者在另一个终端中启动ansible命令时在终端中保持ssh连接。

您也可以使用ssh -O exit user@server强制关闭不需要的连接。

请注意,如果您打开第三个终端并运行ssh user@server,您将不会被要求提供凭据:在步骤1中建立的连接将被重复使用。

缺点

在网络条件较差的情况下

有时当您失去连接时,套接字会持久存在。每次进一步的连接都会挂起。您必须手动断开此连接,使用ssh -O exit user@server。这是此方法已知的唯一缺点。

参考资料:


3

使用Bastion主机的解决方案

即使使用ssh Bastion主机,我也花了很长时间才使它正常工作。如果这对其他人有帮助,以下是我的解决方案。它使用ControlMaster ssh配置选项,因为ansible使用普通的ssh,所以可以配置为使用相同的ssh功能并重新使用与Bastion主机的连接,无论它打开多少个连接到远程主机。我已经在一般情况下推荐使用这些控制选项(假设你有很多主机的性能原因),但在2FA到Bastion主机的情况下没有提到过。

通过这种方法,您不需要进行任何sshd配置更改,因此您需要将AuthenticationMethods publickey,keyboard-interactive设置为位于Bastion服务器上的唯一身份验证方法设置,并且仅对您要代理访问的所有其他服务器使用publickey。由于Bastion主机是唯一从互联网接受外部连接的主机,因此它是唯一需要2FA的主机,而内部主机依靠代理转发进行公钥身份验证,但不使用2FA。

在客户端上,我在我从中运行ansible的顶级目录(因此是ansible.cfg的同级目录)中创建了一个用于我的ansible环境的新ssh配置文件,名为ssh.config。它包含:

Host bastion-persistent-connection
  HostName <bastion host>
  ForwardAgent yes
  IdentityFile ~/.ssh/my-key
  ControlMaster auto
  ControlPath ~/.ssh/ansible-%r@%h:%p
  ControlPersist 10m

Host 10.0.*.*
  ProxyCommand ssh -W %h:%p bastion-persistent-connection -F ./ssh.config
  IdentityFile ~/.ssh/my-key

然后在ansible.cfg文件中,我有以下内容:

[ssh_connection]
ssh_args = -F ./ssh.config

一些需要注意的事项:
  • 在本例中,我的私有子网为10.0.0.0/16,与上面的主机通配符选项相对应。跳板代理所有连接到该子网上的服务器的ssh连接。

  • 这有点脆弱,因为由于ProxyCommand传递了此配置文件的本地路径,所以我只能在此目录中运行ssh或ansible命令。不幸的是,我认为SSH没有映射到当前使用的配置文件的变量,因此我无法自动将相同的配置文件传递给ProxyCommand。根据您的环境,最好使用绝对路径。

唯一的问题是它使得运行ansible更加复杂。不幸的是,据我所知,ansible完全不支持2FA。因此,如果您没有与跳板之间的现有ssh连接,则ansible将为每个私有服务器打印一个Verification code:,但它实际上并未监听输入,因此无论您做什么都将无法建立连接。

所以我首先运行:ssh -F ssh.config bastion-persistent-connection

这会在~/.ssh/ansible-*中创建套接字文件,本地的ssh代理将在可配置时间后关闭并删除该套接字(我设置为10m)。

一旦套接字打开,我就可以像平常一样运行ansible命令,例如:ansible all -m ping,它们会成功执行。


小调整:我最终使用了 ControlPath ~/.ansible/cp/ansible-ssh-%h-%p-%r,这对应于 Ansible 的默认 control_path。http://docs.ansible.com/ansible/intro_configuration.html#control-path - Aidan Feldman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接