Making Use of Vault: Ansible
Shortcuts
As we come towards the end of this mini series, we talked about how to bootstrap a hashicorp vault for non-prod use, what primitives vault uses for secrets management, and how to talk to vault from python.
Here we will dig into how you can access vault content within an Ansible workflow, ensuring you never more have the pain of managing secrets with ansible-vault
, or worse, storing them plain text in a repo somewhere.
Vault in Ansible⌗
Main Options:
- HTTPS API via uri (but … don’t do this to yourself)
- Community “Hashi Vault” collection: link
A little bootstrapping⌗
To be able to get this to work I will stand up a small repo in the same ~/vault
path. Be sure to activate your venv as per the python post.
Lets install ansible and the collection
pip3 install git+https://github.com/ansible/[email protected]
ansible-galaxy collection install ansible.utils # required in the last example
ansible-galaxy collection install community.hashi_vault # required in the second set of tasks
pip3 install hvac # needed to make the vault collection work!
pip3 install netaddr # needed to make the utils collection work!
Now lets create a baseline setup that we can expand as we go.
mkdir -p ~/vault/ansible/{group_vars,host_vars,inventory,tasks,templates}
lets create an inventory file in yaml for localhost
inventory/all.yaml
:
---
all:
controller:
hosts:
localhost:
In order to get to vault, we know we need some variables set. We can use env vars, but lets be explicit so we can use them in the HTTPS API case as well. These were based on the docs for the collection found here
group_vars/all.yml
:
---
# vault settings
ansible_hashi_vault_addr: http://localhost:8200
ansible_hashi_vault_auth_method: token
ansible_hashi_vault_engine_mount_point: secret
# update this to the initial root token if you are using docker
ansible_hashi_vault_token: root
ansible_hashi_vault_token_validate: true
We can quickly check the inventory and the group_vars applied correctly with ansible-inventory -i inventory --list
like so:
{
"_meta": {
"hostvars": {
"localhost": {
"ansible_hashi_vault_addr": "http://localhost:8200",
"ansible_hashi_vault_auth_method": "token",
"ansible_hashi_vault_engine_mount_point": "secret",
"ansible_hashi_vault_token": "hvs.trdFUmCJ00eTm6FuWq3NrmBR",
"ansible_hashi_vault_token_validate": true
}
}
},
"all": {
"children": [
"ungrouped",
"controller"
]
},
"controller": {
"hosts": [
"localhost"
]
}
}
finally lets create a simple playbook that we can expand as well.
test-uri.yml
:
---
- name: Run URI Tasks
hosts: controller
gather_facts: no
connection: local
tasks:
- name: include the uri_workflow tasks
ansible.builtin.include_tasks:
file: tasks/uri_workflow.yml
HTTPS API use⌗
I will hit this extremely briefly, because I think its a terrible idea. There is ONE case where this makes sense - you have to get one small little thing out and stick it in a variable - thats it. Anythind more complicated you should do it with the collection “properly”.
Because I am quite lazy, I will reuse the same examples from the primitives and python posts. There is some consistency there that helps I am sure.
Lets create a few tasks for the uri
workflow:
tasks/uri_workflow.yml
:
- name: "Push secret into Vault via HTTP API"
ansible.builtin.uri:
url: '{{ ansible_hashi_vault_addr }}/v1/{{ ansible_hashi_vault_engine_mount_point }}/data/mycomplicatedsecret'
method: POST
body_format: json
body:
data:
wireguard:
keys:
privkey: "privkey1"
pubkey: "pubkey2"
ip:
outer: "100.64.0.0/30"
inner: "100.64.255.0/31"
options: {}
headers:
X-Vault-Request: 'true'
X-Vault-Token: "{{ ansible_hashi_vault_token }}"
Content-Type: application/json
register: uri_put_result
- name: "show us what happened"
debug:
msg: "{{ uri_put_result }}"
running ansible-playbook -i inventory test-uri.yml
results in this:
PLAY [Run URI Tasks] ********************************************************************************************************************************************************************
TASK [include the uri_workflow tasks] ***************************************************************************************************************************************************
included: /Users/jhow/vault/ansible/tasks/uri_workflow.yml for localhost
TASK [Push secret into Vault via HTTP API] **********************************************************************************************************************************************
ok: [localhost]
TASK [show us what happened] ************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"ansible_facts": {
"discovered_interpreter_python": "/Users/jhow/vault/venv/bin/python3.11"
},
"cache_control": "no-store",
"changed": false,
"connection": "close",
"content_length": "294",
"content_type": "application/json",
"cookies": {},
"cookies_string": "",
"date": "Wed, 20 Nov 2024 14:57:31 GMT",
"elapsed": 0,
"failed": false,
"json": {
"auth": null,
"data": {
"created_time": "2024-11-20T14:57:31.127539Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 3
},
"lease_duration": 0,
"lease_id": "",
"mount_type": "kv",
"renewable": false,
"request_id": "acea468b-9b11-2aa5-fb95-67e9f74be084",
"warnings": null,
"wrap_info": null
},
"msg": "OK (294 bytes)",
"redirected": false,
"status": 200,
"strict_transport_security": "max-age=31536000; includeSubDomains",
"url": "http://localhost:8200/v1/secret/data/mycomplicatedsecret",
"warnings": [
"Platform darwin on host localhost is using the discovered Python interpreter at /Users/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.15/reference_appendices/interpreter_discovery.html for more information."
]
}
}
PLAY RECAP ******************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Great! we pushed something into vault. Lets extend this to include fetching stuff from vault into a fact (variable)
add this to the bottom of tasks/uri_workflow.yml
:
- name: "fetch secret from vault with the http api directly"
ansible.builtin.uri:
url: '{{ ansible_hashi_vault_addr }}/v1/{{ ansible_hashi_vault_engine_mount_point }}/data/mycomplicatedsecret'
method: GET
return_content: yes
headers:
X-Vault-Request: 'true'
X-Vault-Token: "{{ ansible_hashi_vault_token }}"
Content-Type: application/json
register: uri_get_result
- name: "show us what happened"
debug:
msg: "{{ uri_get_result }}"
- name: assign _just_ the secret content from the response to a fact (var)
set_fact:
mysecret: "{{ uri_get_result.json.data.data }}"
- name: show the secret content
debug:
msg: "{{ mysecret }}"
which when we run again (skipping the bits we already saw above):
TASK [fetch secret from vault] **********************************************************************************************************************************************************
ok: [localhost]
TASK [show us what happened] ************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"cache_control": "no-store",
"changed": false,
"connection": "close",
"content": "{\"request_id\":\"724dbeb3-680d-54e0-d760-8a2b34c29641\",\"lease_id\":\"\",\"renewable\":false,\"lease_duration\":0,\"data\":{\"data\":{\"wireguard\":{\"ip\":{\"inner\":\"100.64.255.0/31\",\"outer\":\"100.64.0.0/30\"},\"keys\":{\"privkey\":\"privkey1\",\"pubkey\":\"pubkey2\"}}},\"metadata\":{\"created_time\":\"2024-11-20T15:05:05.452399Z\",\"custom_metadata\":null,\"deletion_time\":\"\",\"destroyed\":false,\"version\":5}},\"wrap_info\":null,\"warnings\":null,\"auth\":null,\"mount_type\":\"kv\"}\n",
"content_length": "436",
"content_type": "application/json",
"cookies": {},
"cookies_string": "",
"date": "Wed, 20 Nov 2024 15:05:05 GMT",
"elapsed": 0,
"failed": false,
"json": {
"auth": null,
"data": {
"data": {
"wireguard": {
"ip": {
"inner": "100.64.255.0/31",
"outer": "100.64.0.0/30"
},
"keys": {
"privkey": "privkey1",
"pubkey": "pubkey2"
}
}
},
"metadata": {
"created_time": "2024-11-20T15:05:05.452399Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 5
}
},
"lease_duration": 0,
"lease_id": "",
"mount_type": "kv",
"renewable": false,
"request_id": "724dbeb3-680d-54e0-d760-8a2b34c29641",
"warnings": null,
"wrap_info": null
},
"msg": "OK (436 bytes)",
"redirected": false,
"status": 200,
"strict_transport_security": "max-age=31536000; includeSubDomains",
"url": "http://localhost:8200/v1/secret/data/mycomplicatedsecret"
}
}
TASK [assign _just_ the secret content from the response to a fact (var)] **********************************************************************************************************************************************
ok: [localhost]
TASK [show the secret content] *************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"wireguard": {
"ip": {
"inner": "100.64.255.0/31",
"outer": "100.64.0.0/30"
},
"keys": {
"privkey": "privkey1",
"pubkey": "pubkey2"
}
}
}
}
PLAY RECAP ******************************************************************************************************************************************************************************
localhost : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
So there is the dumb way to push things in and pull things out of vault in ansible.
Why is it dumb? Well I guess it actually isn’t but for example; that janky pattern of uri, register, pivot, use is not fabulous is it. With the collection you can use a sort of filter expression to pull things in from vault and set_fact in one line.
Lets dig into the “right” way to do it with the collection…
The hashi-vault collection⌗
The collection includes a bunch of modules, filters and lookup plugins that simplify the process of getting things out of vault and into ansible.
Modules⌗
When you peruse the list of Modules in the collection, it starts with a long list of vault_database ones. These are there to help application developers remove hardcoded credentials for database client authentication from their apps, and instead have them brokered via vault. The application gets a token, can talk to vault, get the credentials for that database, and then use them to log their client into the DB. This is brilliant, but not in our scope today. REad more from hashicorp on the feature here.
We then see the kv modules, which we will return to in a moment.
Finally we see a few more generic modules for list/login/read/write, a token_create which we will skip, and finally a very interesting pki_generate_certificate, which we will certainly revisit in another post.
Filter plugins⌗
This is sort of meh. It allows you to export a token quickly from a session that we created with our global vars. I won’t be covering that.
Lookup plugins⌗
This is probably the most useful set of objects, since it allows up to pull vault content directly into facts (vars).
For the core of this post we will focus on the hashi_vault
lookup, which is what I typically use to pull vars to ansible. I can’t really remember if all these other plugins existed when I started, and I expect this one is only there for backwards compatibility, but lets keep it simple!
Our simple example⌗
Copying the same activities we had before, lets fetch the mycomplicatedsecret
from vault using the filter plugin, and assign it directly to a fact, for use elsewhere.
Make a new file in the playbook called tasks/module_workflow.yml
:
- name: "fetch secret from vault with the module"
ansible.builtin.set_fact:
module_get_result: "{{ lookup('community.hashi_vault.hashi_vault', 'secret/data/mycomplicatedsecret') }}"
- name: "show us what happened"
debug:
msg: "{{ module_get_result }}"
now lets fork the uri playbook and pick this task instead:
cp test-uri.yml test-module.yml
update that file to look like this:
- name: Run Module Tasks
hosts: controller
gather_facts: no
connection: local
tasks:
- name: include the module_workflow tasks
ansible.builtin.include_tasks:
file: tasks/module_workflow.yml
Which when run with ansible-playbook -i inventory test-module.yml
results in this:
PLAY [Run Module Tasks] ****************************************************************************************************************************************************************************************
TASK [include the module_workflow tasks] ***********************************************************************************************************************************************************************
included: /home/jhow/vault/ansible/tasks/module_workflow.yml for localhost
TASK [fetch secret from vault with the module] *****************************************************************************************************************************************************************
ok: [localhost]
TASK [show us what happened] ***********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"wireguard": {
"ip": {
"inner": "100.64.255.0/31",
"outer": "100.64.0.0/30"
},
"keys": {
"privkey": "privkey1",
"pubkey": "pubkey2"
}
}
}
}
PLAY RECAP *****************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Thats a heck of a lot easier isn’t it. ;)
Migrate a secret from inventory variables to vault⌗
Lets assume for a second, you have some variables set into the inventory, and you really dont want to leave them there. Here is a simple example of how you could shift a pair of wireguard keys out of host vars and into vault, WITHOUT editing your playbook.
bootstrap⌗
Here are some files that we need to make the ugly way “work”. To prevent the need to create some target VMs, we will use a local path on the ansible host for the outputs of our templates.
This is not a method people use in the real world, so don’t be weirded out by it. This is a learning exercise, so DON’T straight up copy the code. apply the relevant parts to your use case ;)
Create the two hosts in your inventory under a new group called wireguard
:
vi inventory/all.yaml
---
all:
controller:
hosts:
localhost:
wireguard:
hosts:
wireguard1:
wireguard2:
Test the inventory works again with the ansible-inventory -i inventory --list
command:
note that you can add the group name at the end of the command to graph the hosts/vars that group if you prefer…
ansible-inventory -i inventory --graph --vars wireguard
{
"_meta": {
"hostvars": {
"localhost": {
"ansible_hashi_vault_addr": "http://localhost:8200",
"ansible_hashi_vault_auth_method": "token",
"ansible_hashi_vault_engine_mount_point": "secret",
"ansible_hashi_vault_token": "hvs.trdFUmCJ00eTm6FuWq3NrmBR",
"ansible_hashi_vault_token_validate": true
},
"wireguard1": {
"ansible_hashi_vault_addr": "http://localhost:8200",
"ansible_hashi_vault_auth_method": "token",
"ansible_hashi_vault_engine_mount_point": "secret",
"ansible_hashi_vault_token": "hvs.trdFUmCJ00eTm6FuWq3NrmBR",
"ansible_hashi_vault_token_validate": true
},
"wireguard2": {
"ansible_hashi_vault_addr": "http://localhost:8200",
"ansible_hashi_vault_auth_method": "token",
"ansible_hashi_vault_engine_mount_point": "secret",
"ansible_hashi_vault_token": "hvs.trdFUmCJ00eTm6FuWq3NrmBR",
"ansible_hashi_vault_token_validate": true
}
}
},
"all": {
"children": [
"ungrouped",
"controller",
"wireguard"
]
},
"controller": {
"hosts": [
"localhost"
]
},
"wireguard": {
"hosts": [
"wireguard1",
"wireguard2"
]
}
}
now lets add some public/private keys into the host_vars for the two new hosts:
vi host_vars/wireguard1.yml
---
ipam:
inner: "100.64.255.1/30"
outer: "100.64.1.1"
keychain:
privkey: "wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM="
pubkey: "EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4="
peer: wireguard2
vi host_vars/wireguard2.yml
---
ipam:
inner: "100.64.255.2/30"
outer: "100.64.2.1"
keychain:
privkey: "6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0="
pubkey: "qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do="
peer: wireguard1
again lets test the vars work (using the graph this time) ansible-inventory -i inventory --graph --vars wireguard
:
@wireguard:
|--wireguard1
| |--{ansible_hashi_vault_addr = http://localhost:8200}
| |--{ansible_hashi_vault_auth_method = token}
| |--{ansible_hashi_vault_engine_mount_point = secret}
| |--{ansible_hashi_vault_token = hvs.trdFUmCJ00eTm6FuWq3NrmBR}
| |--{ansible_hashi_vault_token_validate = True}
| |--{ipam = {'inner': '100.64.255.1/30', 'outer': '100.64.1.1'}}
| |--{keychain = {'privkey': 'wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=', 'pubkey': 'EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4='}}
| |--{peer = wireguard2}
|--wireguard2
| |--{ansible_hashi_vault_addr = http://localhost:8200}
| |--{ansible_hashi_vault_auth_method = token}
| |--{ansible_hashi_vault_engine_mount_point = secret}
| |--{ansible_hashi_vault_token = hvs.trdFUmCJ00eTm6FuWq3NrmBR}
| |--{ansible_hashi_vault_token_validate = True}
| |--{ipam = {'inner': '100.64.255.2/30', 'outer': '100.64.2.1'}}
| |--{keychain = {'privkey': '6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=', 'pubkey': 'qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do='}}
| |--{peer = wireguard1}
now lets make a jinja2 template that will render the wg0.conf file for each wireguard instance.
vi templates/wg0.conf.j2
[Interface]
Address = {{ ipam.inner }}
ListenPort = 51820
PrivateKey = {{ keychain.privkey }}
[Peer]
PublicKey = {{ hostvars[peer]['keychain']['pubkey'] }}
AllowedIPs = {{ hostvars[peer]['ipam']['outer'] -}}/32
now we need a task to render the template out to the localhost for testing purposes.
vi tasks/render_wg_conf.yml
- name: create template files in /tmp
template:
src: wg0.conf.j2
dest: "/tmp/wg0-{{ inventory_hostname }}.conf"
delegate_to: localhost
and lets make a playbook
vi test-wireguard.yml
---
- name: Run Wireguard Tasks
hosts: wireguard
gather_facts: no
connection: local
tasks:
- name: include the render_wg_conf tasks
ansible.builtin.include_tasks:
file: tasks/render_wg_conf.yml
and so when we run this with ansible-playbook -i inventory test-wireguard.yml
we will see it spit out two files in /tmp:
PLAY [Run Wireguard Tasks] *************************************************************************************************************************************************************************************
TASK [include the render_wg_conf tasks] ************************************************************************************************************************************************************************
included: /home/jhow/vault/ansible/tasks/render_wg_conf.yml for wireguard1, wireguard2
TASK [create template files in /tmp] ***************************************************************************************************************************************************************************
[WARNING]: The value 'False' is not a valid IP address or network, passing this value to ipaddr filter might result in breaking change in future.
[WARNING]: Platform linux on host wireguard1 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
changed: [wireguard1 -> localhost]
[WARNING]: Platform linux on host wireguard2 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
changed: [wireguard2 -> localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
wireguard1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
wireguard2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
which if we look at the content, we have some “valid” wireguard configs:
cat /tmp/wg0-wireguard1.conf
:
[Interface]
Address = 100.64.255.1/30
ListenPort = 51820
PrivateKey = wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=
[Peer]
PublicKey = qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do=
AllowedIPs = 100.64.2.1/32
cat /tmp/wg0-wireguard2.conf
:
[Interface]
Address = 100.64.255.2/30
ListenPort = 51820
PrivateKey = 6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=
[Peer]
PublicKey = EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4=
AllowedIPs = 100.64.1.1/32
Great!
moving that key material to vault⌗
…without updating the playbook.
what is actually secret here?⌗
If we look at the host and group vars, the only part that is genuinely secret is the keys. lets push the keys into vault via the cli first.
wireguard1: vault kv put -mount="secret" "wireguard1" privkey="wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=" pubkey="EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4="
wireguard2: vault kv put -mount="secret" "wireguard2" privkey="6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=" pubkey="qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do="
how to we set a secret per host?⌗
the set_fact uses a per host run, so we can set the host var with a connection_local override.
add the following to the top of tasks/render_wg_conf.yml
- name: get data from vault
set_fact:
keychain: "{{ lookup('community.hashi_vault.hashi_vault', 'secret/data/{{ inventory_hostname }}') }}"
- name: debug
debug:
msg: "{{ keychain }}"
Then comment out the keychain
in the host_vars/wireguard1
and host_vars/wireguard2
vi host_vars/wireguard1.yml
:
---
ipam:
inner: "100.64.255.1/30"
outer: "100.64.1.1"
#keychain:
# privkey: "wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM="
# pubkey: "EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4="
peer: "wireguard2"
vi host_vars/wireguard2.yml
:
---
ipam:
inner: "100.64.255.2/30"
outer: "100.64.2.1"
#keychain:
# privkey: "6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0="
# pubkey: "qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do="
peer: "wireguard1"
if we now run the playbook, it should say NOTHING needed updating (ok
result, not changed
):
ansible-playbook -i inventory test-wireguard.yml
PLAY [Run Wireguard Tasks] *************************************************************************************************************************************************************************************
TASK [include the render_wg_conf tasks] ************************************************************************************************************************************************************************
included: /home/jhow/vault/ansible/tasks/render_wg_conf.yml for wireguard1, wireguard2
TASK [get data from vault] *************************************************************************************************************************************************************************************
ok: [wireguard1]
ok: [wireguard2]
TASK [debug] ***************************************************************************************************************************************************************************************************
ok: [wireguard1] => {
"msg": {
"privkey": "wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=",
"pubkey": "EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4="
}
}
ok: [wireguard2] => {
"msg": {
"privkey": "6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=",
"pubkey": "qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do="
}
}
TASK [create template files in /tmp] ***************************************************************************************************************************************************************************
[WARNING]: Platform linux on host wireguard1 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
ok: [wireguard1 -> localhost]
[WARNING]: Platform linux on host wireguard2 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
ok: [wireguard2 -> localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
wireguard1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
wireguard2 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
and to be double sure, lets delete the config files, and the keys in the host vars entirely:
delete the outputs…
rm /tmp/wg0-wireguard*
remove the keys from the host_vars
vi host_vars/wireguard1.yml
:
---
ipam:
inner: "100.64.255.1/30"
outer: "100.64.1.1"
peer: "wireguard2"
vi host_vars/wireguard2.yml
:
---
ipam:
inner: "100.64.255.2/30"
outer: "100.64.2.1"
peer: "wireguard1"
run it again:
ansible-playbook -i inventory test-wireguard.yml
PLAY [Run Wireguard Tasks] *************************************************************************************************************************************************************************************
TASK [include the render_wg_conf tasks] ************************************************************************************************************************************************************************
included: /home/jhow/vault/ansible/tasks/render_wg_conf.yml for wireguard1, wireguard2
TASK [get data from vault] *************************************************************************************************************************************************************************************
ok: [wireguard1]
ok: [wireguard2]
TASK [debug] ***************************************************************************************************************************************************************************************************
ok: [wireguard1] => {
"msg": {
"privkey": "wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=",
"pubkey": "EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4="
}
}
ok: [wireguard2] => {
"msg": {
"privkey": "6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=",
"pubkey": "qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do="
}
}
TASK [create template files in /tmp] ***************************************************************************************************************************************************************************
[WARNING]: Platform linux on host wireguard1 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
changed: [wireguard1 -> localhost]
[WARNING]: Platform linux on host wireguard2 is using the discovered Python interpreter at /home/jhow/vault/venv/bin/python3.11, but future installation of another Python interpreter could change the meaning
of that path. See https://docs.ansible.com/ansible-core/2.17/reference_appendices/interpreter_discovery.html for more information.
changed: [wireguard2 -> localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************
wireguard1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
wireguard2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
and compare the content of the configs to the keys we got from vault
cat /tmp/wg0-wireguard1.conf
:
[Interface]
Address = 100.64.255.1/30
ListenPort = 51820
PrivateKey = wJGwzAUgZiTL071BTd8yM9GTMEaPqa4rWVuU/BqnlnM=
[Peer]
PublicKey = qUzoU2XMB+W0EUn0vkUyUF0o7BUiGrQIdYSaokGr2Do=
AllowedIPs = 100.64.2.1/32
cat /tmp/wg0-wireguard2.conf
:
[Interface]
Address = 100.64.255.2/30
ListenPort = 51820
PrivateKey = 6FDJPPefeCWkMEiHudfE2P3pnBai3bIPnfcBqG8BgF0=
[Peer]
PublicKey = EI2JFnpywLB7BhfyhQsePcGmmLQhYLVp2sdNZ5Lc8E4=
AllowedIPs = 100.64.1.1/32
wrapping is all up⌗
So eventually we get to the end of the demo. Hopefully you can see that vault is not actually that hard, and getting secrets out of your repo is a big deal, especially when it comes to making your git repo simpler to publish and to share.
The only last thing I would recommend is that you do is take the ansible_hashi_vault_token
out of the repo and use the VAULT_TOKEN
env var instead. You then have no secret material in your repo.
In a follow up post I will look at some of the more production use cases. But, for now, thanks for joining me on this little adventure, and let me know if there are any changes you would like to see.
Until next time! peace out. :D