Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
exclude:
- python_version: "3.9"
ansible_version: "2.18"
type:
- openbao
- vault
steps:
- name: Github Checkout 🛎
uses: actions/checkout@v4
Expand All @@ -42,4 +45,4 @@ jobs:

- name: Run integration tests 🧪
run: |
ansible-playbook -i tests/inventory -v tests/*.yml -e ansible_python_interpreter=$(which python3)
ansible-playbook -i tests/inventory -v tests/test_${{ matrix.type }}.yml -e ansible_python_interpreter=$(which python3)
129 changes: 129 additions & 0 deletions roles/openbao/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
This role deploys and initializes OpenBao with a Raft backend.

Requirements
------------

`ansible-modules-hashivault` Python package installed on the Ansible control host
`hvac` Python package installed on the remote hosts

Note that since version `4.6.4`, `ansible-modules-hashivault` requires
`ansible>4`.

Role variables
--------------

* Common variables
* Optional
* `openbao_registry_url`: Address of the Docker registry used to authenticate (default: "")
* `openbao_registry_username`: Username used to authenticate with the Docker registry (default: "")
* `openbao_registry_password`: Password used to authenticate with the Docker registry (default: "")

* OpenBao
* Mandatory
* `openbao_cluster_name`: OpenBao cluster name (e.g. "prod_cluster")
* `openbao_config_dir`: Directory into which to bind mount OpenBao configuration
* Optional
* `openbao_bind_address`: Which IP address should OpenBao bind to (default: "127.0.0.1")
* `openbao_api_addr`: OpenBao [API addr](https://openbao.org/docs/configuration/#high-availability-parameters) - Full URL including protocol and port (default: "http://127.0.0.1:8200")
* `openbao_init_addr`: OpenBao init addr (used only for initialisation purposes) - full URL including protocol and port (default: "http://127.0.0.1:8200")
* `openbao_docker_name`: Docker - under which name to run the OpenBao image (default: "bao")
* `openbao_docker_image`: Docker image for OpenBao (default: "openbao/openbao")
* `openbao_docker_tag`: Docker image tag for OpenBao (default: "latest")
* `openbao_extra_volumes`: List of `"<host_location>:<container_mountpoint>"`
* `openbao_ca_cert`: Path to CA certificate used to verify OpenBao server TLS cert
* `openbao_tls_key`: Path to TLS key to use by OpenBao
* `openbao_tls_cert`: Path to TLS cert to use by OpenBao
* `openbao_log_keys`: Whether to log the root token and unseal keys in the Ansible output. Default `false`
* `openbao_set_keys_fact`: Whether to set a `openbao_keys` fact containing the root token and unseal keys. Default `false`
* `openbao_write_keys_file`: Whether to write the root token and unseal keys to a file. Default `false`
* `openbao_write_keys_file_host`: Host on which to write root token and unseal keys. Default `localhost`
* `openbao_write_keys_file_path`: Path of file to write root token and unseal keys. Default `bao-keys.json`

Root and unseal keys
--------------------

After OpenBao has been initialised, a root token and a set of unseal keys are emitted.
It is very important to store these keys safely and securely.
This role provides several mechanisms for extracting the root token and unseal keys:

1. Print to Ansible log output (`openbao_log_keys`)
1. Set a `openbao_keys` fact (`openbao_set_keys_fact`)
1. Write to a file (`openbao_write_keys_file`)

In each case, the output will contain the following:

```json
{
"keys": [
"...",
"..."
],
"keys_base64": [
"...",
"..."
],
"root_token": "..."
}
```

Example playbook (used with OpenStack Kayobe)
---------------------------------------------

```
---
- name: Prepare for OpenBao role
any_errors_fatal: True
gather_facts: True
hosts: consul
tasks:
- name: Ensure /opt/kayobe/bao exists
file:
path: /opt/kayobe/bao
state: directory

- name: Template out tls key and cert
vars:
tls_files:
- content: "{{ secrets_external_tls_cert }}"
dest: "tls.cert"
- content: "{{ secrets_external_tls_key }}"
dest: "tls.key"
copy:
content: "{{ item.content }}"
dest: "/opt/kayobe/bao/{{ item.dest }}"
owner: 100
group: 1001
mode: 0600
loop: "{{ tls_files }}"
no_log: True
become: true

- name: Run OpenBao role
any_errors_fatal: True
gather_facts: True
hosts: consul
roles:
- role: stackhpc.hashicorp.openbao
openbao_bind_address: "{{ external_net_ips[inventory_hostname] }}"
openbao_api_addr: "https://{{ external_net_fqdn }}:8200"
openbao_config_dir: "/opt/kayobe/bao"
```

Example post-config playbook to enable secrets engines:
```
---
- name: OpenBao post deployment config
any_errors_fatal: True
gather_facts: True
hosts: bao
tasks:
- name: Enable bao secrets engines
hashivault_secret_engine:
url: "https://vault.example.com:8200"
token: "{{ secrets_openbao_keys.root_token }}"
name: pki
backend: pki
run_once: True
```

NOTE: secrets_external_tls_cert/key are variables in Kayobe's secrets.yml
86 changes: 86 additions & 0 deletions roles/openbao/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
openbao_registry_url: ""
openbao_registry_username: ""
openbao_registry_password: ""

openbao_docker_name: "bao"
openbao_docker_image: "openbao/openbao"
openbao_docker_tag: "latest"

openbao_cluster_name: ""
openbao_protocol: "{{ 'https' if openbao_tls_key and openbao_tls_cert else 'http' }}"
# Allow openbao_vip_url and openbao_vip_address for backwards compatibility.
openbao_vip_address: "{{ openbao_vip_url | default(openbao_bind_address) }}"
openbao_api_addr: "{{ openbao_protocol ~ '://' ~ openbao_vip_address ~ ':8200' }}"
openbao_bind_address: "127.0.0.1"
openbao_init_addr: "http://127.0.0.1:8200"
openbao_tls_key: ""
openbao_tls_cert: ""

openbao_config_dir: ""

openbao_config: >
{
"cluster_name": "{{ openbao_cluster_name }}",
"ui": false,
"api_addr": "{{ openbao_api_addr }}",
"cluster_addr": "http://127.0.0.1:8201",
"listener": [{
"tcp": {
"address": "{{ openbao_bind_address }}:8200",
{% if openbao_tls_key and openbao_tls_cert %}
"tls_min_version": "tls12",
"tls_key_file": "/bao/config/{{ openbao_tls_key }}",
"tls_cert_file": "/bao/config/{{ openbao_tls_cert }}"
{% else %}
"tls_disable": "true"
{% endif %}
}{% if openbao_bind_address != '127.0.0.1' %},
},
{
"tcp": {
"address": "127.0.0.1:8200",
"tls_disable": "true"
}
{% endif %}
}],
"storage": {
"raft": {
"node_id": "raft_{{ ansible_facts.nodename }}",
"path": "/openbao/file"
}
},
"telemetry": {
"prometheus_retention_time": "30s",
"disable_hostname": true
}
}

# Docker options
openbao_container: {}

# Docker volumes
# Default volume mapping
_openbao_default_volumes:
- "{{ openbao_config_dir | default('openbao_config', true) }}:/openbao/config"
- "openbao_file:/openbao/file"
- "openbao_logs:/openbao/logs"

# Exposed for playbooks to access later
openbao_extra_volumes: []

# Combined volume lists
_openbao_volumes: "{{ _openbao_default_volumes + openbao_extra_volumes }}"

# Whether to log the root token and unseal keys in the Ansible output.
openbao_log_keys: false

# Whether to set a openbao_keys fact containing the root token and unseal keys.
openbao_set_keys_fact: false

# Whether to write the root token and unseal keys to a file.
openbao_write_keys_file: false
# Host on which to write root token and unseal keys.
openbao_write_keys_file_host: localhost
# Path of file to write root token and unseal keys.
openbao_write_keys_file_path: bao-keys.json
6 changes: 6 additions & 0 deletions roles/openbao/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
# WORKAROUND: Without this, we see the following in some setups:
# ERROR! couldn't resolve module/action 'hashivault_unseal'. This often indicates a misspelling, missing collection, or incorrect module path.
# Seen using Kayobe on Ansible 2.10.17, running modules on a remote host.
collections:
- community.docker
6 changes: 6 additions & 0 deletions roles/openbao/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
- name: "Vault Prerequisites"
import_tasks: prereqs.yml

- name: "Deploy OpenBao"
import_tasks: openbao.yml
59 changes: 59 additions & 0 deletions roles/openbao/tasks/openbao.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
- name: Ensure OpenBao container is running
docker_container:
name: "{{ openbao_docker_name }}"
image: "{{ openbao_docker_image }}:{{ openbao_docker_tag }}"
network_mode: host
etc_hosts: "{{ openbao_container.etc_hosts | default(omit) }}"
capabilities: IPC_LOCK
volumes: "{{ _openbao_volumes }}"
comparisons:
'*': strict
restart_policy: "always"
env:
BAO_LOCAL_CONFIG: "{{ openbao_config | to_json }}"
command: >
server
become: true

- name: Check if OpenBao is initialized
uri:
url: "{{ openbao_init_addr }}/v1/sys/init"
register: openbao_init_status
retries: 50
delay: 1
run_once: true
until: openbao_init_status.status == 200

- name: "Initialize OpenBao"
run_once: true
when:
- not openbao_init_status.json.initialized
block:
- name: Initialize OpenBao
hashivault_init:
url: "{{ openbao_init_addr }}"
ca_cert: "{{ openbao_ca_cert | default(omit) }}"
no_log: true
register: openbao_keys_result

- name: Print OpenBao keys
debug:
var: openbao_keys_result
when:
- openbao_log_keys | bool

- name: Set openbao_keys fact
set_fact:
openbao_keys: "{{ openbao_keys_result }}"
when:
- openbao_set_keys_fact | bool

- name: Write OpenBao keys to a file
copy:
content: "{{ openbao_keys_result | to_nice_json }}"
dest: "{{ openbao_write_keys_file_path }}"
mode: "0600"
delegate_to: "{{ openbao_write_keys_file_host }}"
when:
- openbao_write_keys_file | bool
8 changes: 8 additions & 0 deletions roles/openbao/tasks/prereqs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- name: Log into Docker registry
docker_login:
registry: "{{ openbao_registry_url }}"
username: "{{ openbao_registry_username }}"
password: "{{ openbao_registry_password }}"
when: openbao_registry_username | length > 0
become: true
3 changes: 3 additions & 0 deletions tests/inventory
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[consul]
localhost ansible_connection=local

[openbao]
localhost ansible_connection=local
Loading
Loading