123

I would like to use ansible to manage a group of existing servers. I have created an ansible_hosts file, and tested successfully (with the -K option) with commands that only target a single host

ansible -i ansible_hosts host1 --sudo -K # + commands ...

My problem now is that the user passwords on each host are different, but I can't find a way of handling this in Ansible.

Using -K, I am only prompted for a single sudo password up-front, which then seems to be tried for all subsequent hosts without prompting:

host1 | ...
host2 | FAILED => Incorrect sudo password
host3 | FAILED => Incorrect sudo password
host4 | FAILED => Incorrect sudo password
host5 | FAILED => Incorrect sudo password

Research so far:

  • a StackOverflow question with one incorrect answer ("use -K") and one response by the author saying "Found out I needed passwordless sudo"

  • the Ansible docs, which say "Use of passwordless sudo makes things easier to automate, but it’s not required." (emphasis mine)

  • this security StackExchange question which takes it as read that NOPASSWD is required

  • article "Scalable and Understandable Provisioning..." which says:

    "running sudo may require typing a password, which is a sure way of blocking Ansible forever. A simple fix is to run visudo on the target host, and make sure that the user Ansible will use to login does not have to type a password"

  • article "Basic Ansible Playbooks", which says

    "Ansible could log into the target server as root and avoid the need for sudo, or let the ansible user have sudo without a password, but the thought of doing either makes my spleen threaten to leap up my gullet and block my windpipe, so I don’t"

    My thoughts exactly, but then how to extend beyond a single server?

  • ansible issue #1227, "Ansible should ask for sudo password for all users in a playbook", which was closed a year ago by mpdehaan with the comment "Haven't seen much demand for this, I think most people are sudoing from only one user account or using keys most of the time."

So... how are people using Ansible in situations like these? Setting NOPASSWD in /etc/sudoers, reusing password across hosts or enabling root SSH login all seem rather drastic reductions in security.

9
  • 2
    Is there a reason you're not using SSH keys?
    – Trondh
    Dec 18, 2013 at 21:53
  • 27
    I am already using SSH keys; they don't affect sudo (which should still require a password).
    – supervacuo
    Dec 19, 2013 at 9:04
  • 1
    This may not be exactly what you're looking for, but on Ubuntu boxes I still use keys, i.e. putting my public key in /root/authorized_keys to get in directly as root. Obvious drawback is allowing root logins over ssh... I also disallow password logins over ssh and run fail2ban for added security.
    – senorsmile
    Dec 23, 2013 at 20:53
  • 31
    @senorsmile thanks for the response! Would you mind making it into an answer instead so I can downvote you for not reading the question?
    – supervacuo
    Dec 29, 2013 at 16:27
  • 4
    i.e. the ansible_ssh_password setting only applies to the SSH password (the clue is in the name...). & I'm already using key-based SSH login.
    – supervacuo
    Dec 29, 2013 at 16:28

8 Answers 8

65

You've certainly done your research...

From all of my experience with ansible what you're looking to accomplish, isn't supported. As you mentioned, ansible states that it does not require passwordless sudo, and you are correct, it does not. But I have yet to see any method of using multiple sudo passwords within ansible, without of course running multiple configs.

So, I can't offer the exact solution you are looking for, but you did ask...

"So... how are people using Ansible in situations like these? Setting NOPASSWD in /etc/sudoers, reusing password across hosts or enabling root SSH login all seem rather drastic reductions in security."

I can give you one view on that. My use case is 1k nodes in multiple data centers supporting a global SaaS firm in which I have to design/implement some insanely tight security controls due to the nature of our business. Security is always balancing act, more usability less security, this process is no different if you are running 10 servers or 1,000 or 100,000.

You are absolutely correct not to use root logins either via password or ssh keys. In fact, root login should be disabled entirely if the servers have a network cable plugged into them.

Lets talk about password reuse, in a large enterprise, is it reasonable to ask sysadmins to have different passwords on each node? for a couple nodes, perhaps, but my admins/engineers would mutiny if they had to have different passwords on 1000 nodes. Implementing that would be near impossible as well, each user would have to store there own passwords somewhere, hopefully a keypass, not a spreadsheet. And every time you put a password in a location where it can be pulled out in plain text, you have greatly decreased your security. I would much rather them know, by heart, one or two really strong passwords than have to consult a keypass file every time they needed to log into or invoke sudo on a machine.

So password resuse and standardization is something that is completely acceptable and standard even in a secure environment. Otherwise ldap, keystone, and other directory services wouldn't need to exist.

When we move to automated users, ssh keys work great to get you in, but you still need to get through sudo. Your choices are a standardized password for the automated user (which is acceptable in many cases) or to enable NOPASSWD as you've pointed out. Most automated users only execute a few commands, so it's quite possible and certainly desirable to enable NOPASSWD, but only for pre-approved commands. I'd suggest using your configuration management (ansible in this case) to manage your sudoers file so that you can easily update the password-less commands list.

Now, there are some steps you can take once you start scaling to further isolate risk. While we have 1000 or so nodes, not all of them are 'production' servers, some are test environments, etc. Not all admins can access production servers, those than can though use their same SSO user/pass|key as they would elsewhere. But automated users are a bit more secure, for instance an automated tool that non-production admins can access has a user & credentials that cannot be used in production. If you want to launch ansible on all nodes, you'd have to do it in two batches, once for non-production and once for production.

We also use puppet though, since it's an enforcing configuration management tool, so most changes to all environments would get pushed out through it.

Obviously, if that feature request you cited gets reopened/completed, what you're looking to do would be entirely supported. Even then though, security is a process of risk assessment and compromise. If you only have a few nodes that you can remember the passwords for without resorting to a post-it note, separate passwords would be slightly more secure. But for most of us, it's not a feasible option.

3
  • Thanks, @Zeb -- I had imagined that users with dozens-to-thousands of servers would use NOPASSWD for sanity reasons anyway (probably supported by tighter firewall rules etc.), but it's good to read your use-case and thoughts on the threat model.
    – supervacuo
    Dec 31, 2013 at 12:39
  • 17
    One comment, though, on your suggestion about restricting to "pre-approved" sudo commands (which did occur to me when I discovered that NOPASSWD is pretty much required). Unfortunately, this seems completely unsupported as well -- ansible makes no promises about calling e.g. chown or mkdir binaries directly, and needs to be able to sudo /bin/sh for most modules to work.
    – supervacuo
    Dec 31, 2013 at 13:00
  • I seem to recall one of my engineers complaining about that a while back, that's annoying.
    – Zeb
    Jan 1, 2014 at 1:59
44

From Ansible 1.5 on, it is possible to use an encrypted vault for host_vars and other variables. This does at least enable you to store a per-host (or per-group) ansible_sudo_pass variable securely. Unfortunately, --ask-vault-pass will only prompt for a single vault password per ansible invocation, so you are still constrained to a single vault password for all the hosts that you’ll use together.

Nevertheless, for some uses this may be an improvement over having a single sudo password across multiple hosts, as an attacker without access to your encrypted host_vars would still need a separate sudo password for every machine (or machine group) that he or she attacks.

5
  • 3
    The ansible_sudo_pass option seems new, too — and seems to do what I was asking for. A single vault password is ideal for my purposes, as well. Thanks!
    – supervacuo
    May 11, 2014 at 13:49
  • Is there a way of avoiding encrypting all host_vars using this method? (a potential limitation noted by Alex Dupuy)
    – supervacuo
    Sep 24, 2015 at 1:41
  • 1
    @supervacuo - To avoid encrypting all host vars, just use a host var directory containing main.yml (unencrypted) and secret.yml (encrypted) - see last part of this doc section where it talks about the "raleigh" group - same technique works for host vars and group vars. I use this a lot, the only variation is that if a secret is only needed for some playbooks it can be useful to store it in a different tree entirely and include via a path that includes the host or var name.
    – RichVel
    Sep 14, 2016 at 6:01
  • 1
    If i've encrypted the 'ansible_ssh_pass' value in the vault, so i also need to duplicate the 'ansible_sudo_pass' value? Is there a way for the second sudo_pass value to refences the first 'ssh_pass' value? Aug 30, 2018 at 16:18
  • Good coverage on vaulted password use can be found here: stackoverflow.com/a/37300030/5025060
    – CODE-REaD
    Apr 3, 2019 at 18:48
26

With Ansible 1.5, it is possible to set the ansible_sudo_pass variable using lookup('password', …):

ansible_sudo_pass: "{{ lookup('password', 'passwords/' + inventory_hostname) }}"

I find this more convenient than using files in host_vars/ for several reasons:

  • I actually use with_password: "passwords/{{ inventory_hostname}} encrypt=sha256_crypt" to provision the passwords for the deploy remote user (which is then needed for sudo), so they are already present in the files (although doing these plaintext lookups loses the salt value stored in the file when the hashed value is generated).

  • This keeps just the passwords in the file (no ansible_sudo_pass: known plaintext) for some epsilon increase in cryptographic security. More significantly, it means that you aren't encrypting all the other host-specific variables, so they can be read without the vault password.

  • Putting the passwords in a separate directory makes it easier to keep the files out of source control, or to use a tool like git-crypt to store them in encrypted form (you can use this with earlier Ansible that lacks the vault feature). I use git-crypt and since I only check out the repository in decrypted form on encrypted filesystems, I don't bother with the vault and thus don't need to type a vault password. (Using both would of course be more secure.)

You can also use the lookup function with ansible_ssh_pass; this may even be possible with earlier versions of Ansible that don't have ansible_sudo_pass.

1
  • 2
    I finally got around to trying this (thanks!), but can't see how it would work without git-crypt; as far as I can see. It seems like Ansible doesn't yet have support for using lookup on an encrypted vault. The password module docs say that there is some as-yet-undocumented support for encrypted storage, but I have yet to find details. Any clues?
    – supervacuo
    Sep 14, 2015 at 17:44
23

Using pass is a simple method to provide ansible with sudo passwords. pass stores one password per file which makes it easy to share passwords via git or other methods. It’s also secure (using GnuPG) and if you’re using gpg-agent, it lets you use ansible without entering a password on each usage.

To provide the password stored as servers/foo for server foo to ansible, use it in an inventory file like this:

[servers]
foo ansible_sudo=True \
    ansible_sudo_pass="{{ lookup('pipe', 'pass servers/foo') }}"

Given that you unlocked the key for gpg-agent earlier, this will run ansible without the need to enter any password.


Update: Ansible has this functionality built-in nowadays, through the passwordstore plugin. The equivalent to the above would be:

[servers]
foo ansible_sudo=True \
    ansible_sudo_pass="{{ lookup('passwordstore', 'servers/foo') }}"

It’s even possible to create a password if it doesn’t exist yet, which can be extremely useful.

0
3

This is a quite old thread, nonetheless:

  • We use two different authentication systems in-house, management of machines is done from local workstations in my team.
  • I wrote a vars_plugin for Ansible (a rather complete implementation may be found at https://gist.github.com/mfriedenhagen/e488235d732b7becda81) which differentiates several authentication systems:
  • The name of the authentication system is group specific.
  • The used login/sudo user is group and admin specific.
  • So I pull the user from the admin's environment and the corresponding password via the python-keyring library from a password safe (we use Mac OS X's keychain, but from the documentation kwallet Gnome and _win_crypto are supported as well).
  • I set permissions on the passwords in Mac OS X's keychain to notify me about the fact, that the command line program security requests access to a password for every authentication system used by the hosts, so I run "ansible -s all -m ping" and get two prompts (one for each authentication system) where I press the space bar and ansible picks up the passwords.
3
  • This looks very neat, thank you — I like the idea of not having to store passwords in two places. At the moment I'm stuck using KeepassX (because the GNOME keyring doesn't seem very portable) but perhaps I can make python-keepass work?
    – supervacuo
    Apr 2, 2015 at 11:37
  • As far as I understand, python-keyring is an abstraction for this, so your colleagues may use other OSes. Or do you store your keepassx db on an USB stick and have to administer from workstations with different OSes? Apr 3, 2015 at 12:29
  • understood; I meant that I already have to use KeepassX to have access to passwords on non-GNOME systems, so storing them in gnome-keyring would mean keeping duplicate records. Still way nicer than using NOPASSWD, though…
    – supervacuo
    Apr 7, 2015 at 15:46
1

Anyone ending up here late: c.f. https://stackoverflow.com/a/37002802

Per host, in your inventory hosts file (inventory/<inventoryname>/hosts)
[server]
10.0.0.0 ansible_sudo_pass=foobar

Per group, in your inventory groups file (inventory/<inventoryname>/groups)
[server:vars]
ansible_sudo_pass=foobar

Per group, in group vars (group_vars/<groupname>/ansible.yml) and encrypted (ansible-vault create group_vars/<groupname>/ansible.yml)
ansible_sudo_pass: "foobar"
0

One possible way to do this is using environmental variables.

e.g.

pass1=foo pass2=bar ansible-playbook -i production servers.xml

Then in the plays, you can lookup the sudo password using:

lookup('env', 'pass1') 
lookup('env', 'pass2') 
0

This is perfectly possible. You can define your inventory to use variables for things like passwords:

INVENTORY:

[023_mqtt]
023-softbank-mqtt01 ansible_host=IP.IP.IP.IP ansible_ssh_private_key_file=/path/to/my_key ansible_ssh_user=my_user ansible_become=yes ansible_become_method=sudo ansible_become_pass='{{ my_user_pass }}'

You need a group_vars/023_mqtt.yaml file where your inventory is, if you are using a jump server(if you need it):

ansible_ssh_common_args: '-o ProxyCommand="ssh -i /path/to/your/jump_key -W %h:%p -q [email protected]"'

Finally, you define a vault to store your passwords. Preferably to create it inside the same folder as your inventory file:

ansible-vault create passwds.yaml
[enter a vault pass]

Add the passwords:

my_user : 'my_user's sudo password'

To run this, pass the vault as extra variables:

 ansible -a "docker ps" -i /path/to/hosts hosts_group --ask-vault-pass --extra-vars '@passwd.yaml'

I have this defined for multiple hosts, each with their own SSH key, SSH passphrase and SSH SUDO password and it works fine. For full article, check out my git:

https://github.com/flow0787/ansible/blob/master/README.md

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .