Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ skip_list:
- meta-no-info
warn_list:
- yaml[line-length]
- run-once[task]
1 change: 1 addition & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ansible_version: "2.18"
type:
- openbao
- openbao_ha
- vault
steps:
- name: Github Checkout 🛎
Expand All @@ -38,7 +39,7 @@
run: |
pipx uninstall ansible-core
python3 -m pip install --upgrade pip
python3 -m pip install ansible-core==${{ matrix.ansible_version }}.* docker git+https://github.com/TerryHowe/ansible-modules-hashivault@c22434d887f0b8a5ac3ebda710664a027291e71c

Check warning on line 42 in .github/workflows/pull_request.yml

View workflow job for this annotation

GitHub Actions / lint / Ansible 2.14 lint

yaml[line-length]

Line too long (186 > 160 characters)
ansible-galaxy collection build
ansible-galaxy collection install *.tar.gz
ansible-galaxy collection install community.general
Expand Down
3 changes: 2 additions & 1 deletion roles/openbao/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Role variables
* `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_bind_addr`: 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")
Expand All @@ -38,6 +38,7 @@ Role variables
* `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`
* `openbao_raft_leaders`: List of IPs belonging to Raft leaders. Expected that the first and only entry is the IP address of the first OpenBao instance as this would be initialised whereas as the others will not.
* `openbao_enable_ui`: Whether to enable user interface that could be accessed from the `openbao_api_addr`. Default `false`

Root and unseal keys
Expand Down
43 changes: 24 additions & 19 deletions roles/openbao/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ openbao_docker_name: "openbao"
openbao_docker_image: "openbao/openbao"
openbao_docker_tag: "latest"

openbao_config_dir: ""

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_protocol: "{{ 'https' if openbao_tls_key and openbao_tls_cert else 'http' }}"

openbao_api_addr: "{{ openbao_bind_addr ~ ':' ~ openbao_api_port }}"
openbao_bind_addr: "127.0.0.1"
openbao_init_addr: "{{ openbao_api_addr }}"
openbao_cluster_addr: "{{ openbao_bind_addr ~ ':' ~ openbao_cluster_port }}"

openbao_api_port: 8200
openbao_cluster_port: 8201

openbao_raft_leaders: []

openbao_enable_ui: false

Expand All @@ -26,30 +33,28 @@ openbao_config: >
"cluster_name": "{{ openbao_cluster_name }}",
"ui": "{{ openbao_enable_ui }}",
"api_addr": "{{ openbao_api_addr }}",
"cluster_addr": "http://127.0.0.1:8201",
"cluster_addr": "{{ openbao_protocol }}://{{ openbao_cluster_addr }}",
"listener": [{
"tcp": {
"address": "{{ openbao_bind_address }}:8200",
"address": "{{ openbao_bind_addr }}:{{ openbao_api_port }}",
{% if openbao_tls_key and openbao_tls_cert %}
"tls_min_version": "tls12",
"tls_key_file": "/openbao/config/{{ openbao_tls_key }}",
"tls_cert_file": "/openbao/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"
"raft": {
"node_id": "raft_{{ inventory_hostname }}",
"path": "/openbao/file",
{% if openbao_raft_leaders | length > 0 %}
"retry_join": {
"leader_api_addr": "{{ openbao_protocol }}://{{ openbao_raft_leaders | first }}:{{ openbao_api_port }}"
}
{% endif %}
}
},
"telemetry": {
Expand Down
5 changes: 5 additions & 0 deletions tests/inventory
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ localhost ansible_connection=local

[openbao]
localhost ansible_connection=local

[openbao_ha]
raft_01 ansible_connection=local openbao_bind_addr=127.0.0.1 openbao_docker_name=bao_01 openbao_config_dir=/etc/bao_01
raft_02 ansible_connection=local openbao_bind_addr=127.0.0.2 openbao_docker_name=bao_02 openbao_config_dir=/etc/bao_02
raft_03 ansible_connection=local openbao_bind_addr=127.0.0.3 openbao_docker_name=bao_03 openbao_config_dir=/etc/bao_03
5 changes: 3 additions & 2 deletions tests/test_openbao.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
vars:
openbao_config_dir: "/etc/openbao"
openbao_log_keys: true
openbao_api_addr: "{{ 'http' ~ '://' ~ '127.0.0.1' ~ ':8200' }}"
openbao_bind_addr: "127.0.0.1"
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
openbao_set_keys_fact: true
openbao_write_keys_file: true
tasks:
Expand All @@ -24,7 +25,7 @@
ansible.builtin.include_role:
name: openbao

- name: Include openbao role (idemoptence test)
- name: Include openbao role (idempotence test)
ansible.builtin.include_role:
name: openbao

Expand Down
179 changes: 179 additions & 0 deletions tests/test_openbao_ha.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
---
- name: Deploy HA OpenBao
gather_facts: true
hosts: openbao_ha
vars:
openbao_log_keys: true
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
openbao_set_keys_fact: true
openbao_write_keys_file: true
openbao_raft_leaders:
- "127.0.0.1"
_openbao_default_volumes:
- "{{ openbao_config_dir }}/config:/openbao/config"
- "{{ openbao_config_dir }}/openbao_file:/openbao/file"
- "{{ openbao_config_dir }}/openbao_logs:/openbao/logs"
tasks:
- name: Debug
ansible.builtin.debug:
var: openbao_api_addr

- name: Ensure /etc/openbao exists
ansible.builtin.file:
path: /etc/openbao
state: directory
mode: "0700"
become: true

- name: Include openbao role
ansible.builtin.include_role:
name: openbao

- name: Include openbao role (idempotence test)
ansible.builtin.include_role:
name: openbao

# As this test is evaluating OpenBao configured for high availability backed
# by `Raft` we must first ensure that the primary or leader instance is unsealed
# before attempting to unseal the other members.
- name: Unseal vault

Check warning on line 39 in tests/test_openbao_ha.yml

View workflow job for this annotation

GitHub Actions / lint / Ansible 2.14 lint

run-once[task]

Using run_once may behave differently if strategy is set to free.
ansible.builtin.include_role:
name: vault_unseal
vars:
vault_api_addr: "{{ openbao_api_addr }}"
vault_unseal_keys: "{{ openbao_keys.keys_base64 }}"
run_once: true

# As the first instance is now unsealed the other instances will now need some
# time to connect before we can proceed.
- name: Wait for OpenBao Raft peers to connect
ansible.builtin.wait_for:
timeout: 30
delegate_to: localhost

# Raft peers take few seconds before they report an unsealed state therefore
# we must wait.
- name: Unseal vault
ansible.builtin.include_role:
name: vault_unseal
vars:
vault_api_addr: "{{ openbao_api_addr }}"
vault_unseal_keys: "{{ openbao_keys.keys_base64 }}"
vault_unseal_timeout: 10

- name: Deploy HA OpenBao
gather_facts: true
hosts: openbao_ha
run_once: true
vars:
openbao_log_keys: true
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
openbao_set_keys_fact: true
openbao_write_keys_file: true
tasks:
- name: Include OpenBao keys
ansible.builtin.include_vars:
file: "bao-keys.json"
name: openbao_keys

- name: Configure PKI - create root/intermediate and generate certificates
vars:
vault_pki_certificate_subject:
- role: 'ServerCert'
common_name: "OS-CERT-TEST"
extra_params:
ttl: "8760h"
ip_sans: "127.0.0.1"
alt_names: "example.com"
exclude_cn_from_sans: true
vault_pki_certificates_directory: "/tmp/"
vault_pki_generate_certificates: true
vault_pki_intermediate_ca_name: "OS-TLS-INT"
vault_pki_intermediate_create: true
vault_pki_intermediate_roles:
- name: "ServerCert"
config:
max_ttl: 8760h
ttl: 8760h
allow_any_name: true
allow_ip_sans: true
require_cn: false
server_flag: true
key_type: rsa
key_bits: 4096
country: ["UK"]
locality: ["Bristol"]
organization: ["StackHPC"]
ou: ["HPC"]
vault_pki_root_ca_name: "OS-TLS-ROOT"
vault_pki_root_create: true
vault_pki_write_certificate_files: true
vault_pki_write_int_ca_to_file: true
vault_pki_write_pem_bundle: false
vault_pki_write_root_ca_to_file: true
vault_api_addr: "{{ openbao_api_addr }}"
vault_token: "{{ openbao_keys.root_token }}"
block:
- name: Configure PKI - create root/intermediate and generate certificates
ansible.builtin.include_role:
name: vault_pki

- name: Configure PKI - create root/intermediate and generate certificates (idempotence test)
ansible.builtin.include_role:
name: vault_pki

- name: Configure PKI - generate certificate pem bundle
vars:
vault_pki_certificate_subject:
- role: 'ServerCert'
common_name: "OS-CERT-TEST2"
extra_params:
ttl: "8760h"
ip_sans: "192.168.38.72"
exclude_cn_from_sans: true
vault_pki_certificates_directory: "/tmp/"
vault_pki_generate_certificates: true
vault_pki_intermediate_ca_name: "OS-TLS-INT"
vault_pki_intermediate_create: false
vault_pki_root_ca_name: "OS-TLS-ROOT"
vault_pki_root_create: false
vault_pki_write_certificate_files: true
vault_pki_write_pem_bundle: true
vault_api_addr: "{{ openbao_api_addr }}"
vault_token: "{{ openbao_keys.root_token }}"
block:
- name: Configure PKI - generate certificate pem bundle
ansible.builtin.include_role:
name: vault_pki

- name: Configure PKI - generate certificate pem bundle (idempotence test)
ansible.builtin.include_role:
name: vault_pki

- name: Validate if certificates exist
ansible.builtin.stat:
path: "/tmp/{{ item }}"
register: stat_result
failed_when: not stat_result.stat.exists
loop:
- OS-CERT-TEST.crt
- OS-CERT-TEST2.pem

- name: Concatenate CAs
ansible.builtin.shell: |
cat /tmp/OS-TLS-ROOT.pem /tmp/OS-TLS-INT.crt > /tmp/CA-CHAIN.pem
args:
executable: /bin/bash
become: true
changed_when: true

- name: Verify certificate chain
ansible.builtin.command: |
openssl verify -CAfile /tmp/CA-CHAIN.pem
/tmp/{{ item }}
register: verify_result
failed_when: verify_result.rc != 0
loop:
- OS-CERT-TEST.crt
- OS-CERT-TEST2.pem
changed_when: false
2 changes: 1 addition & 1 deletion tests/test_vault.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
include_role:
name: vault

- name: Include vault role (idemoptence test)
- name: Include vault role (idempotence test)
include_role:
name: vault

Expand Down