1{ lib, ... }:
2let
3 certs = import ./common/acme/server/snakeoil-certs.nix;
4 domain = certs.domain;
5in
6{
7 name = "openbao";
8
9 meta.maintainers = with lib.maintainers; [ kranzes ];
10
11 nodes.machine =
12 { config, ... }:
13 {
14 security.pki.certificateFiles = [ certs.ca.cert ];
15
16 networking.extraHosts = ''
17 127.0.0.1 ${domain}
18 '';
19
20 services.openbao = {
21 enable = true;
22
23 settings = {
24 ui = true;
25
26 listener = {
27 default = {
28 type = "tcp";
29 tls_cert_file = certs.${domain}.cert;
30 tls_key_file = certs.${domain}.key;
31 };
32
33 unix = {
34 type = "unix";
35 };
36 };
37
38 cluster_addr = "https://127.0.0.1:8201";
39 api_addr = "https://${domain}:8200";
40
41 storage.raft.path = "/var/lib/openbao";
42 };
43 };
44
45 environment.variables = {
46 BAO_ADDR = config.services.openbao.settings.api_addr;
47 BAO_FORMAT = "json";
48 };
49 };
50
51 testScript =
52 { nodes, ... }:
53 ''
54 import json
55
56 start_all()
57
58 with subtest("Wait for OpenBao to start up"):
59 machine.wait_for_unit("openbao.service")
60 machine.wait_for_open_port(8200)
61 machine.wait_for_open_unix_socket("${nodes.machine.services.openbao.settings.listener.unix.address}")
62
63 with subtest("Check that the web UI is being served"):
64 machine.succeed("curl -L --fail --show-error --silent $BAO_ADDR | grep '<title>OpenBao</title>'")
65
66 with subtest("Check that OpenBao is not initialized"):
67 status_output = json.loads(machine.fail("bao status"))
68 assert not status_output["initialized"]
69
70 with subtest("Initialize OpenBao"):
71 init_output = json.loads(machine.succeed("bao operator init"))
72
73 with subtest("Check that OpenBao is initialized and sealed"):
74 status_output = json.loads(machine.fail("bao status"))
75 assert status_output["initialized"]
76 assert status_output["sealed"]
77
78 with subtest("Unseal OpenBao"):
79 for key in init_output["unseal_keys_b64"][:init_output["unseal_threshold"]]:
80 machine.succeed(f"bao operator unseal {key}")
81
82 with subtest("Check that OpenBao is not sealed"):
83 status_output = json.loads(machine.succeed("bao status"))
84 assert not status_output["sealed"]
85
86 with subtest("Login with root token"):
87 machine.succeed(f"bao login {init_output["root_token"]}")
88
89 with subtest("Enable userpass auth method"):
90 machine.succeed("bao auth enable userpass")
91
92 with subtest("Create a user in userpass"):
93 machine.succeed("bao write auth/userpass/users/testuser password=testpassword")
94
95 with subtest("Login to a user from userpass"):
96 machine.succeed("bao login -method userpass username=testuser password=testpassword")
97
98 with subtest("Write a secret to cubbyhole"):
99 machine.succeed("bao write cubbyhole/my-secret my-value=s3cr3t")
100
101 with subtest("Read a secret from cubbyhole"):
102 read_output = json.loads(machine.succeed("bao read cubbyhole/my-secret"))
103 assert read_output["data"]["my-value"] == "s3cr3t"
104 '';
105}