Ansible SSH authentication with password and private key - ssh

For security reasons and compliance, we're required to set up 2FA on our hosts. We implement it by forcing authentication with passwords AND a public key with the AuthenticationMethods setting in sshd_config. The private key is required to have a password as well.
So in order to run playbooks on these hosts, we need to be able to enter the login password and the password of the private key. I've used the -k flag together with the ansible_ssh_private_key_file option in the hosts file (or with the --private-key flag). It asks for the SSH login password but then it just hangs and never asks me for the private key passphrase. When I set the -vvvv flat I see that the key is passed correctly, but the SSH login password isn't passed with the command:
<10.1.2.2> SSH: EXEC sshpass -d10 ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o Port=22022 -o 'IdentityFile="/home/me/.ssh/id_ed25519"' -o 'User="me"' -o ConnectTimeout=10 -o ControlPath=/home/me/.ansible/cp/db574551ae 10.1.2.2 '/bin/sh -c '"'"'echo ~me && sleep 0'"'"''
How can I make Ansible work with both passwords and public keys?

As stated in the Ansible Documentation:
Ansible does not expose a channel to allow communication between the user and the
ssh process to accept a password manually to decrypt an ssh key when using the ssh
connection plugin (which is the default). The use of ssh-agent is highly recommended.
This is why you don't get prompted to type in your private key password. As said in the comments, setup a ssh agent, when you'll be prompted for it:
$ ssh-agent bash
$ ssh-add ~/.ssh/id_rsa
Then, after playbook execution, clear out identities so to be asked for passwords the next time:
# Deletes all identities from the agent:
ssh-add -D
# or, instead of adding identities, removes identities (selectively) from the agent:
ssh-add -d <file>
You may pack key addition, playbook execution and cleaning into one wrapper shell script.

Related

FIDO2 for SSH login on Linux Server

To establish an SSH connection between my PC (Linux) and server (Linux) I have to enter the password of the user.
ssh USER#<IP-Address>
Now I want to replace the password with FIDO2. For this I have executed the following commands on my PC and followed the instructions.
ssh-keygen -t ed25519-sk -O resident -O application=ssh:YourTextHere -f ~/.ssh/id_mykey_sk`
and
ssh-copy-id -i ~/.ssh/id_mykey_sk.pub USER#<IP-Address>
Now I have the problem when I try to establish the SSH connection it still asks for the password instead of the FIDO2 stick. What could be the reason for this?
I found out, that when I run the commands
eval `ssh-agent -s`
and
ssh-add -K
it works as expected.
Establish a connection to my server via
ssh USER#<IP-Address>
will now work with the FIDO2 key.

How to run an ansible-playbook with a passphrase-protected-ssh-private-key?

I have created an autoscaling group for Amazon EC2 and I have added my public key when I created the AMI with packer, I can run ansible-playbook and ssh to the hosts.
But there is a problem when I run the playbook like this
ansible-playbook load.yml I am getting this message that I need to write my password
Enter passphrase for key '/Users/XXX/.ssh/id_rsa':
Enter passphrase
for key '/Users/XXX/.ssh/id_rsa':
Enter passphrase for key
'/Users/XXX/.ssh/id_rsa':
The problem is it doesn't accept my password (I am sure I am typing my password correctly).
I found that I can send my password with ask-pass flag, so I have changed my command to ansible-playbook load.yml --ask-pass and I got some progress but again for some other task it asks for the password again and it didn't accept my password
[WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *************************************************************************************************************
TASK [ec2_instance_facts] ****************************************************************************************************
ok: [localhost]
TASK [add_host] **************************************************************************************************************
changed: [localhost] => (item=xx.xxx.xx.xxx)
changed: [localhost] => (item=yy.yyy.yyy.yyy)
PLAY [instances] *************************************************************************************************************
TASK [Copy gatling.conf] *****************************************************************************************************
ok: [xx.xxx.xx.xxx]
ok: [yy.yyy.yyy.yyy]
Enter passphrase for key '/Users/ccc/.ssh/id_rsa': Enter passphrase for key '/Users/ccc/.ssh/id_rsa':
Enter passphrase for key '/Users/ccc/.ssh/id_rsa':
Enter passphrase for key '/Users/ccc/.ssh/id_rsa':
Enter passphrase for key '/Users/ccc/.ssh/id_rsa':
If I don't use ask-pass flag even the task [Copy gatling.conf] doesn't complete and complaining about could not access the hosts. By adding the flag this part passes, but my next task again asks for pass.
How should I solve this issue? What am I doing wrong here?
In ansible There is no option to store passphrase-protected private key
For that we need to add the passphrase-protected private key in the ssh-agent
Start the ssh-agent in the background.
# eval "$(ssh-agent -s)"
Add SSH private key to the ssh-agent
# ssh-add ~/.ssh/id_rsa
Now try running ansible-playbook and ssh to the hosts.
I solved it by running ssh-add once and use it like if it's not password protected.
Building up on #javeed-shakeel's answer, I added the following lines to my .bashrc:
command -v ansible > /dev/null &&
alias ansible='ssh-add -l > /dev/null || ssh-add 2> /dev/null && ansible'
command -v ansible-playbook > /dev/null &&
alias ansible-playbook='ssh-add -l > /dev/null || ssh-add 2> /dev/null && ansible-playbook'
This will run ssh-add before ansible(-playbook) iff there was no key added to the ssh-agent, yet. This has the advantage that one does not need to run ssh-add by hand and one will be asked for the passphrase only if it is necessary.
A passphrase is something else than a password. When ssh asks for a password, then you login as a user with a password that is checked by the computer you ssh into. It might be a company-wide password (AD, LDAP, etc.), or a local admin password.
A passphrase is used to encrypt the private key of your ssh key-pair. Typically the key-pair is generated with ssh-keygen, this command asks for a passphrase twice when you create your key-pair. That is for security, because when there is no passphrase on the private key, then anyone with a copy of the file can impersonate you! The passphrase is an added factor to the security.
ssh-agent is a program that creates a session for the use of your key-pair.
If you want to automate in GitLab with the use of an encrypted ssh key-pair this might help:
##
## Install ssh-agent if not already installed.
## (change apt-get to yum if you use an RPM-based image)
##
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
##
## Run ssh-agent (on the Ansible control host)
##
- eval $(ssh-agent)
##
## Create the SSH directory and give it the right permissions
##
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
## Create a shell script that will echo the environment variable SSH_PASSPHRASE
- echo 'echo $SSH_PASSPHRASE' > ~/.ssh/tmp && chmod 700 ~/.ssh/tmp
## If ssh-add needs a passphrase, it will read the passphrase from the current
## terminal if it was run from a terminal. If ssh-add does not have a terminal
## associated with it but DISPLAY and SSH_ASKPASS are set, it will execute the
## program specified by SSH_ASKPASS and open an X11 window to read the
## passphrase. This is particularly useful when calling ssh-add from a
## .xsession or related script. Setting DISPLAY=None drops the use of X11.
## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
## We're using tr to fix line endings which makes ed25519 keys work
## without extra base64 encoding.
## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
##
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | DISPLAY=None SSH_ASKPASS=~/.ssh/tmp ssh-add -
##
## Use ssh-keyscan to scan the keys of your private server. Replace example.com
## with your own domain name. You can copy and repeat that command if you have
## more than one server to connect to.
##
- ssh-keyscan example.com >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts

Host key verification failed even though I use `-o StrictHostKeyChecking=no`

I try to run:
ssh -o StrictHostKeyChecking=no ${REGR_IP_ADDR}/deploy_jar.sh 1
and I get
14:02:54 Host key verification failed.
14:02:54 lost connection
StrictHostKeyChecking does not turn off the verification, if you already stored the public key of this host. Common combination is with UserKnownHostsFile=/dev/null, which makes sure that there is no other previous public key.
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${REGR_IP_ADDR}/deploy_jar.sh 1
Should do the job for you. But note that it is not recommended in live environment, because it is the only mechanism protecting you against MITM attack.

How to make an SSH RSA key auto accept?

I need to check a bunch of servers via SSH (RAM, disk, CPU model, etc).
I want to make a script for it. But the RSA key yes/no is getting in the way.
Is it possible to auto accept the RSA key while connecting to the server via SSH?
(I.e. ssh root#ip "yes" or some workaround?)
To tell ssh not to worry about host keys, simply set the StrictHostKeyChecking option to no, i.e.
ssh -o StrictHostKeyChecking=no root#ip
If you also want to pass the password to it, you can do that using sshpass:
sshpass -p your_password ssh -o StrictHostKeyChecking=no root#ip
However, you'd be much better off using an ssh agent (such as PAgeant) and ssh key pairs - better than having your password hard-coded into a script somewhere.

Call ssh-copy-id in an Ansible playbook - How to handle password prompt?

I have two servers. I manage serverA with Ansible. serverB is not managed with Ansible. I want serverA to be able to access serverB by copying the ssh_pub_key of serverA to serverB.
This can be done manually by calling ssh-copy-id user#serverB on serverA.
I want to do this with Ansible on serverA automatically.
- name: Register ssh key at serverB
command: ssh-copy-id -i /home/{{user}}/.ssh/id_rsa.pub -o StrictHostKeyChecking=no user#serverB
Calling ssh-copy-id requires me to enter my ssh password for user#serverB, so the key can be copied.
How can I do this via ansible? I want it to ask for the user#serverB password interactively while executing the playbook. Storing the password in ansible vault is also an option. Then I still do not know how to avoid the interactive password call of ssh-copy-id though.
I also added -o StrictHostKeyChecking=no to the call because this is another interaction that normally requires user interaction when calling ssh-copy-id.
If using the ssh-copy-id command is not a restriction, you might as well try out the Ansible authorized_key module.
Then your code could look something like this:
authorized_key:
user: <user>
key: "{{ lookup('file', '/home/' + lookup('env', 'USER') + '/.ssh/id_rsa.pub') }}"
You can try sshpass tool. It would require modification of your command like this:
command: sshpass -p password ssh-copy-id -i /home/{{user}}/.ssh/id_rsa.pub -o StrictHostKeyChecking=no user#serverB
but there are other options how to provide the password -- see the sshpass(1) manual page.