Skip to content

Commit b247767

Browse files
authored
Merge pull request #78 from stackhpc/openbao-raft-ha-support
feat: add support for `Raft` HA in `OpenBao`
2 parents 1b62b6b + 0b3a591 commit b247767

File tree

8 files changed

+216
-23
lines changed

8 files changed

+216
-23
lines changed

.ansible-lint

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ skip_list:
88
- meta-no-info
99
warn_list:
1010
- yaml[line-length]
11+
- run-once[task]

.github/workflows/pull_request.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
ansible_version: "2.18"
2525
type:
2626
- openbao
27+
- openbao_ha
2728
- vault
2829
steps:
2930
- name: Github Checkout 🛎

roles/openbao/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Role variables
2323
* `openbao_cluster_name`: OpenBao cluster name (e.g. "prod_cluster")
2424
* `openbao_config_dir`: Directory into which to bind mount OpenBao configuration
2525
* Optional
26-
* `openbao_bind_address`: Which IP address should OpenBao bind to (default: "127.0.0.1")
26+
* `openbao_bind_addr`: Which IP address should OpenBao bind to (default: "127.0.0.1")
2727
* `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")
2828
* `openbao_init_addr`: OpenBao init addr (used only for initialisation purposes) - full URL including protocol and port (default: "http://127.0.0.1:8200")
2929
* `openbao_docker_name`: Docker - under which name to run the OpenBao image (default: "bao")
@@ -38,6 +38,7 @@ Role variables
3838
* `openbao_write_keys_file`: Whether to write the root token and unseal keys to a file. Default `false`
3939
* `openbao_write_keys_file_host`: Host on which to write root token and unseal keys. Default `localhost`
4040
* `openbao_write_keys_file_path`: Path of file to write root token and unseal keys. Default `bao-keys.json`
41+
* `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.
4142
* `openbao_enable_ui`: Whether to enable user interface that could be accessed from the `openbao_api_addr`. Default `false`
4243

4344
Root and unseal keys

roles/openbao/defaults/main.yml

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ openbao_docker_name: "openbao"
77
openbao_docker_image: "openbao/openbao"
88
openbao_docker_tag: "latest"
99

10+
openbao_config_dir: ""
11+
1012
openbao_cluster_name: ""
11-
openbao_protocol: "{{ 'https' if openbao_tls_key and openbao_tls_cert else 'http' }}"
12-
# Allow openbao_vip_url and openbao_vip_address for backwards compatibility.
13-
openbao_vip_address: "{{ openbao_vip_url | default(openbao_bind_address) }}"
14-
openbao_api_addr: "{{ openbao_protocol ~ '://' ~ openbao_vip_address ~ ':8200' }}"
15-
openbao_bind_address: "127.0.0.1"
16-
openbao_init_addr: "http://127.0.0.1:8200"
13+
1714
openbao_tls_key: ""
1815
openbao_tls_cert: ""
1916

20-
openbao_config_dir: ""
17+
openbao_protocol: "{{ 'https' if openbao_tls_key and openbao_tls_cert else 'http' }}"
18+
19+
openbao_api_addr: "{{ openbao_bind_addr ~ ':' ~ openbao_api_port }}"
20+
openbao_bind_addr: "127.0.0.1"
21+
openbao_init_addr: "{{ openbao_api_addr }}"
22+
openbao_cluster_addr: "{{ openbao_bind_addr ~ ':' ~ openbao_cluster_port }}"
23+
24+
openbao_api_port: 8200
25+
openbao_cluster_port: 8201
26+
27+
openbao_raft_leaders: []
2128

2229
openbao_enable_ui: false
2330

@@ -26,30 +33,28 @@ openbao_config: >
2633
"cluster_name": "{{ openbao_cluster_name }}",
2734
"ui": "{{ openbao_enable_ui }}",
2835
"api_addr": "{{ openbao_api_addr }}",
29-
"cluster_addr": "http://127.0.0.1:8201",
36+
"cluster_addr": "{{ openbao_protocol }}://{{ openbao_cluster_addr }}",
3037
"listener": [{
3138
"tcp": {
32-
"address": "{{ openbao_bind_address }}:8200",
39+
"address": "{{ openbao_bind_addr }}:{{ openbao_api_port }}",
3340
{% if openbao_tls_key and openbao_tls_cert %}
3441
"tls_min_version": "tls12",
3542
"tls_key_file": "/openbao/config/{{ openbao_tls_key }}",
3643
"tls_cert_file": "/openbao/config/{{ openbao_tls_cert }}"
3744
{% else %}
3845
"tls_disable": "true"
3946
{% endif %}
40-
}{% if openbao_bind_address != '127.0.0.1' %},
41-
},
42-
{
43-
"tcp": {
44-
"address": "127.0.0.1:8200",
45-
"tls_disable": "true"
4647
}
47-
{% endif %}
4848
}],
4949
"storage": {
50-
"raft": {
51-
"node_id": "raft_{{ ansible_facts.nodename }}",
52-
"path": "/openbao/file"
50+
"raft": {
51+
"node_id": "raft_{{ inventory_hostname }}",
52+
"path": "/openbao/file",
53+
{% if openbao_raft_leaders | length > 0 %}
54+
"retry_join": {
55+
"leader_api_addr": "{{ openbao_protocol }}://{{ openbao_raft_leaders | first }}:{{ openbao_api_port }}"
56+
}
57+
{% endif %}
5358
}
5459
},
5560
"telemetry": {

tests/inventory

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@ localhost ansible_connection=local
33

44
[openbao]
55
localhost ansible_connection=local
6+
7+
[openbao_ha]
8+
raft_01 ansible_connection=local openbao_bind_addr=127.0.0.1 openbao_docker_name=bao_01 openbao_config_dir=/etc/bao_01
9+
raft_02 ansible_connection=local openbao_bind_addr=127.0.0.2 openbao_docker_name=bao_02 openbao_config_dir=/etc/bao_02
10+
raft_03 ansible_connection=local openbao_bind_addr=127.0.0.3 openbao_docker_name=bao_03 openbao_config_dir=/etc/bao_03

tests/test_openbao.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
vars:
66
openbao_config_dir: "/etc/openbao"
77
openbao_log_keys: true
8-
openbao_api_addr: "{{ 'http' ~ '://' ~ '127.0.0.1' ~ ':8200' }}"
8+
openbao_bind_addr: "127.0.0.1"
9+
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
910
openbao_set_keys_fact: true
1011
openbao_write_keys_file: true
1112
tasks:
@@ -24,7 +25,7 @@
2425
ansible.builtin.include_role:
2526
name: openbao
2627

27-
- name: Include openbao role (idemoptence test)
28+
- name: Include openbao role (idempotence test)
2829
ansible.builtin.include_role:
2930
name: openbao
3031

tests/test_openbao_ha.yml

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
---
2+
- name: Deploy HA OpenBao
3+
gather_facts: true
4+
hosts: openbao_ha
5+
vars:
6+
openbao_log_keys: true
7+
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
8+
openbao_set_keys_fact: true
9+
openbao_write_keys_file: true
10+
openbao_raft_leaders:
11+
- "127.0.0.1"
12+
_openbao_default_volumes:
13+
- "{{ openbao_config_dir }}/config:/openbao/config"
14+
- "{{ openbao_config_dir }}/openbao_file:/openbao/file"
15+
- "{{ openbao_config_dir }}/openbao_logs:/openbao/logs"
16+
tasks:
17+
- name: Debug
18+
ansible.builtin.debug:
19+
var: openbao_api_addr
20+
21+
- name: Ensure /etc/openbao exists
22+
ansible.builtin.file:
23+
path: /etc/openbao
24+
state: directory
25+
mode: "0700"
26+
become: true
27+
28+
- name: Include openbao role
29+
ansible.builtin.include_role:
30+
name: openbao
31+
32+
- name: Include openbao role (idempotence test)
33+
ansible.builtin.include_role:
34+
name: openbao
35+
36+
# As this test is evaluating OpenBao configured for high availability backed
37+
# by `Raft` we must first ensure that the primary or leader instance is unsealed
38+
# before attempting to unseal the other members.
39+
- name: Unseal vault
40+
ansible.builtin.include_role:
41+
name: vault_unseal
42+
vars:
43+
vault_api_addr: "{{ openbao_api_addr }}"
44+
vault_unseal_keys: "{{ openbao_keys.keys_base64 }}"
45+
run_once: true
46+
47+
# As the first instance is now unsealed the other instances will now need some
48+
# time to connect before we can proceed.
49+
- name: Wait for OpenBao Raft peers to connect
50+
ansible.builtin.wait_for:
51+
timeout: 30
52+
delegate_to: localhost
53+
54+
# Raft peers take few seconds before they report an unsealed state therefore
55+
# we must wait.
56+
- name: Unseal vault
57+
ansible.builtin.include_role:
58+
name: vault_unseal
59+
vars:
60+
vault_api_addr: "{{ openbao_api_addr }}"
61+
vault_unseal_keys: "{{ openbao_keys.keys_base64 }}"
62+
vault_unseal_timeout: 10
63+
64+
- name: Deploy HA OpenBao
65+
gather_facts: true
66+
hosts: openbao_ha
67+
run_once: true
68+
vars:
69+
openbao_log_keys: true
70+
openbao_api_addr: "{{ 'http' ~ '://' ~ openbao_bind_addr ~ ':8200' }}"
71+
openbao_set_keys_fact: true
72+
openbao_write_keys_file: true
73+
tasks:
74+
- name: Include OpenBao keys
75+
ansible.builtin.include_vars:
76+
file: "bao-keys.json"
77+
name: openbao_keys
78+
79+
- name: Configure PKI - create root/intermediate and generate certificates
80+
vars:
81+
vault_pki_certificate_subject:
82+
- role: 'ServerCert'
83+
common_name: "OS-CERT-TEST"
84+
extra_params:
85+
ttl: "8760h"
86+
ip_sans: "127.0.0.1"
87+
alt_names: "example.com"
88+
exclude_cn_from_sans: true
89+
vault_pki_certificates_directory: "/tmp/"
90+
vault_pki_generate_certificates: true
91+
vault_pki_intermediate_ca_name: "OS-TLS-INT"
92+
vault_pki_intermediate_create: true
93+
vault_pki_intermediate_roles:
94+
- name: "ServerCert"
95+
config:
96+
max_ttl: 8760h
97+
ttl: 8760h
98+
allow_any_name: true
99+
allow_ip_sans: true
100+
require_cn: false
101+
server_flag: true
102+
key_type: rsa
103+
key_bits: 4096
104+
country: ["UK"]
105+
locality: ["Bristol"]
106+
organization: ["StackHPC"]
107+
ou: ["HPC"]
108+
vault_pki_root_ca_name: "OS-TLS-ROOT"
109+
vault_pki_root_create: true
110+
vault_pki_write_certificate_files: true
111+
vault_pki_write_int_ca_to_file: true
112+
vault_pki_write_pem_bundle: false
113+
vault_pki_write_root_ca_to_file: true
114+
vault_api_addr: "{{ openbao_api_addr }}"
115+
vault_token: "{{ openbao_keys.root_token }}"
116+
block:
117+
- name: Configure PKI - create root/intermediate and generate certificates
118+
ansible.builtin.include_role:
119+
name: vault_pki
120+
121+
- name: Configure PKI - create root/intermediate and generate certificates (idempotence test)
122+
ansible.builtin.include_role:
123+
name: vault_pki
124+
125+
- name: Configure PKI - generate certificate pem bundle
126+
vars:
127+
vault_pki_certificate_subject:
128+
- role: 'ServerCert'
129+
common_name: "OS-CERT-TEST2"
130+
extra_params:
131+
ttl: "8760h"
132+
ip_sans: "192.168.38.72"
133+
exclude_cn_from_sans: true
134+
vault_pki_certificates_directory: "/tmp/"
135+
vault_pki_generate_certificates: true
136+
vault_pki_intermediate_ca_name: "OS-TLS-INT"
137+
vault_pki_intermediate_create: false
138+
vault_pki_root_ca_name: "OS-TLS-ROOT"
139+
vault_pki_root_create: false
140+
vault_pki_write_certificate_files: true
141+
vault_pki_write_pem_bundle: true
142+
vault_api_addr: "{{ openbao_api_addr }}"
143+
vault_token: "{{ openbao_keys.root_token }}"
144+
block:
145+
- name: Configure PKI - generate certificate pem bundle
146+
ansible.builtin.include_role:
147+
name: vault_pki
148+
149+
- name: Configure PKI - generate certificate pem bundle (idempotence test)
150+
ansible.builtin.include_role:
151+
name: vault_pki
152+
153+
- name: Validate if certificates exist
154+
ansible.builtin.stat:
155+
path: "/tmp/{{ item }}"
156+
register: stat_result
157+
failed_when: not stat_result.stat.exists
158+
loop:
159+
- OS-CERT-TEST.crt
160+
- OS-CERT-TEST2.pem
161+
162+
- name: Concatenate CAs
163+
ansible.builtin.shell: |
164+
cat /tmp/OS-TLS-ROOT.pem /tmp/OS-TLS-INT.crt > /tmp/CA-CHAIN.pem
165+
args:
166+
executable: /bin/bash
167+
become: true
168+
changed_when: true
169+
170+
- name: Verify certificate chain
171+
ansible.builtin.command: |
172+
openssl verify -CAfile /tmp/CA-CHAIN.pem
173+
/tmp/{{ item }}
174+
register: verify_result
175+
failed_when: verify_result.rc != 0
176+
loop:
177+
- OS-CERT-TEST.crt
178+
- OS-CERT-TEST2.pem
179+
changed_when: false

tests/test_vault.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
include_role:
2121
name: vault
2222

23-
- name: Include vault role (idemoptence test)
23+
- name: Include vault role (idempotence test)
2424
include_role:
2525
name: vault
2626

0 commit comments

Comments
 (0)