1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.openbao;
9
10 settingsFormat = pkgs.formats.json { };
11in
12{
13 options = {
14 services.openbao = {
15 enable = lib.mkEnableOption "OpenBao daemon";
16
17 package = lib.mkPackageOption pkgs "openbao" {
18 example = "pkgs.openbao.override { withHsm = false; withUi = false; }";
19 };
20
21 settings = lib.mkOption {
22 description = ''
23 Settings of OpenBao.
24
25 See [documentation](https://openbao.org/docs/configuration) for more details.
26 '';
27 example = lib.literalExpression ''
28 {
29 ui = true;
30
31 listener.default = {
32 type = "tcp";
33 tls_acme_email = config.security.acme.defaults.email;
34 tls_acme_domains = [ "example.com" ];
35 tls_acme_disable_http_challenge = true;
36 };
37
38 cluster_addr = "http://127.0.0.1:8201";
39 api_addr = "https://example.com";
40
41 storage.raft.path = "/var/lib/openbao";
42 }
43 '';
44
45 type = lib.types.submodule {
46 freeformType = settingsFormat.type;
47 options = {
48 ui = lib.mkEnableOption "the OpenBao web UI";
49
50 listener = lib.mkOption {
51 type = lib.types.attrsOf (
52 lib.types.submodule (
53 { config, ... }:
54 {
55 freeformType = settingsFormat.type;
56 options = {
57 type = lib.mkOption {
58 type = lib.types.enum [
59 "tcp"
60 "unix"
61 ];
62 description = ''
63 The listener type to enable.
64 '';
65 };
66 address = lib.mkOption {
67 type = lib.types.str;
68 default = if config.type == "unix" then "/run/openbao/openbao.sock" else "127.0.0.1:8200";
69 defaultText = lib.literalExpression ''if config.services.openbao.settings.listener.<name>.type == "unix" then "/run/openbao/openbao.sock" else "127.0.0.1:8200"'';
70 description = ''
71 The TCP address or UNIX socket path to listen on.
72 '';
73 };
74 };
75 }
76 )
77 );
78 description = ''
79 Configure a listener for responding to requests.
80 '';
81 };
82 };
83 };
84 };
85
86 extraArgs = lib.mkOption {
87 type = lib.types.listOf lib.types.str;
88 default = [ ];
89 description = ''
90 Additional arguments given to OpenBao.
91 '';
92 };
93 };
94 };
95
96 config = lib.mkIf cfg.enable {
97 environment.systemPackages = [ cfg.package ];
98
99 systemd.services.openbao = {
100 description = "OpenBao - A tool for managing secrets";
101
102 wantedBy = [ "multi-user.target" ];
103 after = [ "network.target" ];
104
105 restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients.
106
107 serviceConfig = {
108 Type = "notify";
109
110 ExecStart = lib.escapeShellArgs (
111 [
112 (lib.getExe cfg.package)
113 "server"
114 "-config"
115 (settingsFormat.generate "openbao.hcl.json" cfg.settings)
116 ]
117 ++ cfg.extraArgs
118 );
119 ExecReload = "${lib.getExe' pkgs.coreutils "kill"} -SIGHUP $MAINPID";
120
121 StateDirectory = "openbao";
122 StateDirectoryMode = "0700";
123 RuntimeDirectory = "openbao";
124 RuntimeDirectoryMode = "0700";
125
126 CapabilityBoundingSet = "";
127 DynamicUser = true;
128 LimitCORE = 0;
129 LockPersonality = true;
130 MemorySwapMax = 0;
131 MemoryZSwapMax = 0;
132 PrivateUsers = true;
133 ProcSubset = "pid";
134 ProtectClock = true;
135 ProtectControlGroups = true;
136 ProtectHome = true;
137 ProtectHostname = true;
138 ProtectKernelLogs = true;
139 ProtectKernelModules = true;
140 ProtectKernelTunables = true;
141 ProtectProc = "invisible";
142 Restart = "on-failure";
143 RestrictAddressFamilies = [
144 "AF_INET"
145 "AF_INET6"
146 "AF_UNIX"
147 ];
148 RestrictNamespaces = true;
149 RestrictRealtime = true;
150 SystemCallArchitectures = "native";
151 SystemCallFilter = [
152 "@system-service"
153 "@resources"
154 "~@privileged"
155 ];
156 UMask = "0077";
157 };
158 };
159 };
160}