A tutorial that helps to get to know important Ansible features around SSH Key authentication, secret encryption and Ansible Galaxy role management required for any mature Ansible project.
This project relies on nl2go/docker-ansible providing a Docker image for Ansible with additional convenience features.
Before getting started, following packages must be installed.
- Git
- Docker
>= 17.x
- Docker Compose
>= 2.x
-
Clone this project to the directory of your choice.
$ git clone https://github.com/nl2go/ansible-warrior.git $ cd ansible-warrior
The tutorial solely involves interactions within the host machine. The setup consists of two Docker containers:
ansible
- the Ansible Controllernode
- a node to be controlled by Ansible
See docker-compose.yml for further details.
Using Ansible inside a container gets rid of very annoying issues that might appear when running it directly on the host a.k.a the "works on my machine" issue set, which includes:
- Missing dependencies
- Deviating package versions
- Local quirks of different host operating systems
Those issues become even bigger when your team grows.
SSH key authentication is widely preferred over password authentication because it provides more flexibility and safety for the user. Private keys are stored on the disk protected by a passphrase specified by the user to prevent unauthorized access to the key content.
This scenario shows how to use the private key protected by a passphrase when running the Ansible container utilizing the ssh-agent to prevent passphrase retyping.
-
The test private key is located at
.docker/root/.ssh/id_rsa
.$ ls .docker/root/.ssh drwxr-xr-x 3 user user 4096 Aug 28 12:40 . drwxr-xr-x 3 user user 4096 Aug 28 12:36 .. -rw-r--r-- 1 user user 381 Aug 28 12:36 authorized_keys -rw-r--r-- 1 user user 1766 Aug 28 12:36 id_rsa
-
Run Ansible container.
$ docker-compose run ansible
-
Specify test private key passphrase
Abcd1234
.... Starting SSH Agent. Enter passphrase for /root/.ssh/id_rsa:
-
Ensure test private key was added to the SSH agent.
... Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa) ...
-
Inspect
key_authentication
Ansible playbook.$ cat key_authentication.yml --- - hosts: node gather_facts: no tasks: - name: Test SSH connection using private/public key pair. ping:
The playbook connects to the remote
node
host and executes theping
module, which performs basic connection and host sanity checks. -
Run
key_authentication
Ansible playbook.$ ansible-playbook -i inventories/dev/hosts.ini key_authentication.yml PLAY [node] **************************************************** TASK [Test SSH connection using private/public key pair.] ****************** ok: [node] PLAY RECAP ***************************************************************** node : ok=1 changed=0 unreachable=0 failed=0
-
Exit Ansible container.
$ exit
You have successfully run the playbook key_authentication.yml
against node
host using SSH key authentication.
In advanced Ansible projects it's not uncommon to rely on already existing roles created and shared by the community members as roles within Ansible Galaxy.
This scenario shows how to handle the role dependency management.
-
Inspect
galaxy_role.yml
to extract the required role dependencies.$ cat galaxy_role.yml --- - hosts: node become: true roles: - role: chusiang.helloworld
The playbook
galaxy_role
relies on the Ansible Galaxy rolechusiang.helloworld
. To be able to execute the playbook the role must be installed first. -
Create
requirements.yml
within theroles
directory as required by Ansible Tower.$ mkdir roles && touch roles/requirements.yml
-
Add
chusiang.helloworld
role to theroles/requirements.yml
.$ echo '- src: chusiang.helloworld' > roles/requirements.yml
-
Inspect the content of
roles/requirements.yml
.$ cat roles/requirements.yml - src: chusiang.helloworld
The
requirements.yml
now contains the required role dependency that will be installed from the Ansible Galaxy. -
Run Ansible container.
$ docker-compose run ansible Skipping SSH Agent start. No private key was found at /tmp/.ssh/id_rsa. Skpping Anisble Vault password decryption. No .vault-password files present. Installing Ansible Galaxy roles from /ansible/roles/requirements.yml. - downloading role 'helloworld', owned by chusiang - downloading role from https://github.com/chusiang/helloworld.ansible.role/archive/master.tar.gz - extracting chusiang.helloworld to /root/.ansible/roles/chusiang.helloworld - chusiang.helloworld (master) was installed successfully
-
Run
galaxy_role.yml
playbook.$ ansible-playbook -i inventories/dev/hosts.ini galaxy_role.yml
-
Exit Ansible container.
$ exit
-
Remove
roles/requirements.yml
.$ rm roles/requirements.yml
You have successfully installed an Ansible Galaxy Role and run the galaxy_role.yml
playbook.
Working with Ansible Vault passwords directly can be cumbersome since it's common to protect the secrets using at least one dedicated password per inventory or environment. Besides that the secret for a specific inventory/environment must be specified on every playbook execution. This might reduce the productivity while working with Ansible.
To overcome this issues a personal master password for Ansible Vault inventory/environment password encryption can be used.
This scenario shows how to encrypt and persist the Ansible Vault inventory/environment password protected by a personal master password.
-
Run Ansible container.
$ docker-compose run ansible
-
Generate encrypted vault password file for the
dev
inventory using master passwordmaster
and the inventory Vault passwordAbcd1234
.$ cd inventories/dev $ ansible-encrypt-vault-password Enter the master password for .vault-password files: Enter the vault password for dev inventory: Created /ansible/inventories/dev/.vault-password.
-
Inspect the content of the encrypted vault password file:
$ cat .vault-password Salted__��iwC�Z���+'�|���;��
-
Exit Ansible container.
$ exit
-
Run Ansible container and specify the Vault master password
master
.$ docker-compose run ansible Skipping SSH Agent start. No private key was found at /tmp/.ssh/id_rsa. Decrypting Ansible Vault passwords. Enter decryption password for .vault-password files: Decrypting /ansible/inventories/dev/.vault-password. Skipping Ansible Galaxy roles installation. No /ansible/roles/requirements.yml file present.
-
Run
vault_master_password
playbook.$ ansible-playbook -i inventories/dev/hosts.ini vault_master_password.yml PLAY [localhost] *********************************************************** TASK [Gathering Facts] ***************************************************** ok: [localhost] TASK [debug] *************************************************************** ok: [localhost] => { "msg": "foobar123" } PLAY RECAP ***************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0
-
Exit Ansible container.
$ exit
You have successfully run the vault_master_password
playbook while decrypting the secret_message
variable to Hello World!
.
To protect sensitive data it must be encrypted before sharing it with remote sources. In general any kind of passwords, secrets or keys must be encrypted.
This scenario shows how to encrypt arbitrary values for particular inventories/environments. It relies on the existing
encrypted Vault password file for the dev
inventory located at inventories/dev/.vault-password
from the previous
scenario.
-
Run Ansible container.
$ docker-compose run ansible
-
Run Ansible container and specify the Vault master password
master
.$ docker-compose run ansible Skipping SSH Agent start. No private key was found at /tmp/.ssh/id_rsa. Decrypting Ansible Vault passwords. Enter decryption password for .vault-password files: Decrypting /ansible/inventories/dev/.vault-password. Skipping Ansible Galaxy roles installation. No /ansible/roles/requirements.yml file present.
-
Encrypt secret value
foobar123
for thedev
inventory.$ ansible-vault encrypt_string --encrypt-vault-id 'dev' 'foobar123' !vault | $ANSIBLE_VAULT;1.2;AES256;dev 34313833626331373036336338663831333833356532306363336532306362376232653835613035 6131303730313238633938636564663866356164383735610a353133613363663239326337313231 64333737343634356531383864313031333134646264373035626363363865343037306436363462 3832363461623233620a383135343062643433613763656462623565346363303866376264643661 6236 Encryption successful
-
Replace the plain
bar
value withininventories/dev/host_vars/localhost/foo.yml
with the encrypted value from the previous step and verify the result.$ cat inventories/dev/host_vars/localhost/foo.yml --- bar: !vault | $ANSIBLE_VAULT;1.2;AES256;dev 34313833626331373036336338663831333833356532306363336532306362376232653835613035 6131303730313238633938636564663866356164383735610a353133613363663239326337313231 64333737343634356531383864313031333134646264373035626363363865343037306436363462 3832363461623233620a383135343062643433613763656462623565346363303866376264643661 6236
-
Run the
vault_secret
playbook.$ ansible-playbook -i inventories/dev/hosts.ini vault_secret.yml PLAY [localhost] *********************************************************** TASK [Gathering Facts] ***************************************************** ok: [localhost] TASK [debug] *************************************************************** ok: [localhost] => { "msg": "foobar123" } PLAY RECAP ***************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0
You have successfully encrypted an existing variable bar
and executed the vault_secret
playbook that
utilizes the encrypted variable.
See the LICENSE.md file for details