Skip to content

Install Ansible

All centralized maintanance should be initiated from kb2018

root@kb2018:~# apt-get install ansible
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  ieee-data python-asn1crypto python-certifi python-cffi-backend python-chardet python-cryptography python-enum34 python-httplib2 python-idna python-ipaddress
  python-jinja2 python-jmespath python-kerberos python-libcloud python-lockfile python-markupsafe python-netaddr python-openssl python-paramiko python-pkg-resources
  python-pyasn1 python-requests python-selinux python-simplejson python-six python-urllib3 python-xmltodict python-yaml
Suggested packages:
  cowsay sshpass python-cryptography-doc python-cryptography-vectors python-enum34-doc python-jinja2-doc python-lockfile-doc ipython python-netaddr-docs
  python-openssl-doc python-openssl-dbg python-gssapi python-setuptools python-socks python-ntlm
...
root@kb2018:~#

Install python to all containers

root@kb2018:~# for h in `lxc list local: -c n --format csv ` ;do echo $h;lxc exec local:$h  -- apt-get install -y python; done
...
root@kb2018:~# for h in `lxc list bs2020: -c n --format csv ` ;do echo $h;lxc exec bs2020:$h  -- apt-get install -y python; done
...

Seed /etc/ansible/hosts

localhost (kb2018)

Adding the entry for the localhost is simple

root@kb2018:~#  nano /etc/ansible/hosts

[pets:children]
servers
containers

[servers]
kb2018   ansible_connection=local
..
root@kb2018:~#

local containers

entries for local containers is equally straightforward.

hostname ansible_connection=lxd

Which we can generate using lxc list and awk

root@kb2018:~# lxc list -c n --format=csv local:|awk '{print $1,"ansible_connection=lxd";}'>>/etc/ansible/hosts

containers on remote host

Containers on the remote host (bs2020) require an additional parameter

 remotecontainer  ansible_connection=lxd ansible_host=remotehost:remotecontainer

Which we again generate using lxc list and awk

root@kb2018:~# lxc list -c n --format=csv bs2020:|awk '{print $1," ansible_connection=lxd ansible_host=bs2020:"$1;}'>>/etc/ansible/hosts

adding access to bs2020 (via ssh to unprivileged account)

Our current security model expressly forbids direct access to all root accounts, users must connect using an ssh key and escalate using their password.

To control a remote server from ansible user (root@kb2018) we:

Create a sudo user for our ansible host

root@bs2020:~# useradd kb2018 -c"Governer Kate Brown" -m -g sudo
root@bs2020:~# passwd kb2018
... remember this one for later ...

Restrict ssh access to that account to the ip of that particular host.

root@bs2020:~# nano /etc/ssh/sshd_config 
...
PermitRootLogin no
....
DenyUsers kb2018@"!192.168.31.159,*"
...
root@bs2020:~# service ssh restart

Generate key for our ansible user (root@kb2018)

haifisch:~ don$ ssh -p22222 feurig@bs2020.suspectdevices.com
...
Last login: Mon Feb 25 18:56:59 2019 from 97.115.103.251
feurig@kb2018:~$ sudo bash
[sudo] password for feurig: 
root@kb2018:~# ssh-key
ssh-keygen   ssh-keyscan  
root@kb2018:~# ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
... 
... add ssh key to kb2018@bs2020:.ssh/authorized_keys ...
...

root@kb2018:~# ssh kb2018@bs2020.suspectdevices.com

Testing connectivity.

At this point we can add the remote server to ansible's inventory and check the connectivity.

bs2020   ansible_connection=ssh ansible_ssh_user=kb2018

note kb2018 is the localhost, ernest24jan19 (stopped) and douglas are local containers, bs2020 is a remote host and teddy is a container that it hosts

root@kb2018:~# ansible pets -m ping
kb2018 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
ernest24jan19 | UNREACHABLE! => {
    "changed": false, 
    "msg": "... , exited with result 1", 
    "unreachable": true
}
...
douglas | SUCCESS => {                                                                                                                                           
    "changed": false, 
    "ping": "pong"
}
...
bs2020 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
...
teddy | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
root@kb2018:~#

However we cannot run privileged commands on our remote host.

root@kb2018:~# ansible servers -m apt -a "force_apt_get=yes upgrade=yes update_cache=yes autoremove=yes"
bs2020 | FAILED! => {
    "changed": false, 
    "msg": "Failed to lock apt for exclusive operation"
}
kb2018 | SUCCESS => {

We can fix this by telling ansible to escalate using our user and password

bs2020   ansible_host=bs2020.suspectdevices.com ansible_user=kb2018ansible_become=yes ansible_become_user=root ansible_become_pass=my_super_secret_password

And we can see that this works. Next we encrypt the password using ansible's vault feature and moving the username and password to the host_vars file.

feurig@kb2018:~$ grep 'bs2020 ' /etc/ansible/hosts
bs2020   ansible_host=bs2020.suspectdevices.com ansible_user='{{ bs2020_unprivilaged_user }}' ansible_become=yes ansible_become_user=root ansible_become_pass='{{ bs2020_become_pass }}'
root@kb2018:~# ansible servers -m apt -a "force_apt_get=yes upgrade=yes update_cache=yes autoremove=yes" 
kb2018 | SUCCESS => {

... WIP: you are here ... Create and protect vault password file

root@kb2018:~# openssl rand -base64 2048 >  /root/.vault_passwd
root@kb2018:~# chmod 600 /root/.vault_passwd

Add password file to ansible.cfg

root@kb2018:~# nano /etc/ansible/ansible.cfg
...
# If set, configures the path to the Vault password file as an alternative to
# specifying --vault-password-file on the command line.
#vault_password_file = /path/to/vault_password_file
vault_password_file=/root/.vault_passwd
...

Encrypt sudo password

root@kb2018:~# ansible-vault encrypt_string 'mybigsecret' --name 'kb2018_become_pass'
kb2018_become_pass: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          663 .... 462
Encryption successful

Add user and encrypted password to /etc/ansible/host_vars/bs2020.yml

root@kb2018:~# mkdir /etc/ansible/host_vars
root@kb2018:~# nano /etc/ansible/host_vars/bs2020.yml
bs2020_unprivilaged_user: kb2018
bs2020_become_pass: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          663 .... 462

Add variables to inventory

[pets:children]
servers
containers

[servers]
kb2018   ansible_connection=local
bs2020   ansible_host=bs2020.suspectdevices.com ansible_user='{{ bs2020_unprivilaged_user }}' ansible_become=yes ansible_become_user=root ansible_become_pass='{{ bs2020_become_pass }}'
#bs2020   ansible_connection=ssh ansible_ssh_user=kb2018

[containers:children]
local-containers
remote-containers

[local-containers]
douglas ansible_connection=lxd
...
[remote-containers]
...
goethe  ansible_connection=lxd ansible_host=bs2020:goethe

And now we can treat all of our pets with the same love and affection.

root@kb2018:~# ansible pets -m apt -a "force_apt_get=yes upgrade=yes update_cache=yes autoremove=yes"

References/Linkdump

  • https://stackoverflow.com/questions/37297249/how-to-store-ansible-become-pass-in-a-vault-and-how-to-use-it
  • https://docs.ansible.com/ansible/latest/user_guide/vault.html#id6