1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5 cfg = config.services.vault;
6
7 configFile = pkgs.writeText "vault.hcl" ''
8 listener "tcp" {
9 address = "${cfg.address}"
10 ${if (cfg.tlsCertFile == null || cfg.tlsKeyFile == null) then ''
11 tls_disable = "true"
12 '' else ''
13 tls_cert_file = "${cfg.tlsCertFile}"
14 tls_key_file = "${cfg.tlsKeyFile}"
15 ''}
16 ${cfg.listenerExtraConfig}
17 }
18 storage "${cfg.storageBackend}" {
19 ${optionalString (cfg.storagePath != null) ''path = "${cfg.storagePath}"''}
20 ${optionalString (cfg.storageConfig != null) cfg.storageConfig}
21 }
22 ${optionalString (cfg.telemetryConfig != "") ''
23 telemetry {
24 ${cfg.telemetryConfig}
25 }
26 ''}
27 '';
28in
29{
30 options = {
31
32 services.vault = {
33
34 enable = mkEnableOption "Vault daemon";
35
36 address = mkOption {
37 type = types.str;
38 default = "127.0.0.1:8200";
39 description = "The name of the ip interface to listen to";
40 };
41
42 tlsCertFile = mkOption {
43 type = types.nullOr types.str;
44 default = null;
45 example = "/path/to/your/cert.pem";
46 description = "TLS certificate file. TLS will be disabled unless this option is set";
47 };
48
49 tlsKeyFile = mkOption {
50 type = types.nullOr types.str;
51 default = null;
52 example = "/path/to/your/key.pem";
53 description = "TLS private key file. TLS will be disabled unless this option is set";
54 };
55
56 listenerExtraConfig = mkOption {
57 type = types.lines;
58 default = ''
59 tls_min_version = "tls12"
60 '';
61 description = "extra configuration";
62 };
63
64 storageBackend = mkOption {
65 type = types.enum [ "inmem" "file" "consul" "zookeeper" "s3" "azure" "dynamodb" "etcd" "mssql" "mysql" "postgresql" "swift" "gcs" ];
66 default = "inmem";
67 description = "The name of the type of storage backend";
68 };
69
70 storagePath = mkOption {
71 type = types.nullOr types.path;
72 default = if cfg.storageBackend == "file" then "/var/lib/vault" else null;
73 description = "Data directory for file backend";
74 };
75
76 storageConfig = mkOption {
77 type = types.nullOr types.lines;
78 default = null;
79 description = "Storage configuration";
80 };
81
82 telemetryConfig = mkOption {
83 type = types.lines;
84 default = "";
85 description = "Telemetry configuration";
86 };
87 };
88 };
89
90 config = mkIf cfg.enable {
91 assertions = [
92 { assertion = cfg.storageBackend == "inmem" -> (cfg.storagePath == null && cfg.storageConfig == null);
93 message = ''The "inmem" storage expects no services.vault.storagePath nor services.vault.storageConfig'';
94 }
95 { assertion = (cfg.storageBackend == "file" -> (cfg.storagePath != null && cfg.storageConfig == null)) && (cfg.storagePath != null -> cfg.storageBackend == "file");
96 message = ''You must set services.vault.storagePath only when using the "file" backend'';
97 }
98 ];
99
100 users.extraUsers.vault = {
101 name = "vault";
102 group = "vault";
103 uid = config.ids.uids.vault;
104 description = "Vault daemon user";
105 };
106 users.extraGroups.vault.gid = config.ids.gids.vault;
107
108 systemd.services.vault = {
109 description = "Vault server daemon";
110
111 wantedBy = ["multi-user.target"];
112 after = [ "network.target" ]
113 ++ optional (config.services.consul.enable && cfg.storageBackend == "consul") "consul.service";
114
115 restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients.
116
117 preStart = optionalString (cfg.storagePath != null) ''
118 install -d -m0700 -o vault -g vault "${cfg.storagePath}"
119 '';
120
121 serviceConfig = {
122 User = "vault";
123 Group = "vault";
124 PermissionsStartOnly = true;
125 ExecStart = "${pkgs.vault}/bin/vault server -config ${configFile}";
126 PrivateDevices = true;
127 PrivateTmp = true;
128 ProtectSystem = "full";
129 ProtectHome = "read-only";
130 AmbientCapabilities = "cap_ipc_lock";
131 NoNewPrivileges = true;
132 KillSignal = "SIGINT";
133 TimeoutStopSec = "30s";
134 Restart = "on-failure";
135 StartLimitInterval = "60s";
136 StartLimitBurst = 3;
137 };
138
139 unitConfig.RequiresMountsFor = optional (cfg.storagePath != null) cfg.storagePath;
140 };
141 };
142
143}