python312Packages.dissect-extfs: 3.11 -> 3.12
[NixPkgs.git] / nixos / modules / services / security / vault.nix
blob650f9bda99c33a985c32db1a9464dd14b854f59b
1 { config, lib, options, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.vault;
7   opt = options.services.vault;
9   configFile = pkgs.writeText "vault.hcl" ''
10     # vault in dev mode will refuse to start if its configuration sets listener
11     ${lib.optionalString (!cfg.dev) ''
12     listener "tcp" {
13       address = "${cfg.address}"
14       ${if (cfg.tlsCertFile == null || cfg.tlsKeyFile == null) then ''
15           tls_disable = "true"
16         '' else ''
17           tls_cert_file = "${cfg.tlsCertFile}"
18           tls_key_file = "${cfg.tlsKeyFile}"
19         ''}
20       ${cfg.listenerExtraConfig}
21     }
22     ''}
23     storage "${cfg.storageBackend}" {
24       ${optionalString (cfg.storagePath   != null) ''path = "${cfg.storagePath}"''}
25       ${optionalString (cfg.storageConfig != null) cfg.storageConfig}
26     }
27     ${optionalString (cfg.telemetryConfig != "") ''
28         telemetry {
29           ${cfg.telemetryConfig}
30         }
31       ''}
32     ${cfg.extraConfig}
33   '';
35   allConfigPaths = [configFile] ++ cfg.extraSettingsPaths;
36   configOptions = escapeShellArgs
37     (lib.optional cfg.dev "-dev" ++
38      lib.optional (cfg.dev && cfg.devRootTokenID != null) "-dev-root-token-id=${cfg.devRootTokenID}"
39       ++ (concatMap (p: ["-config" p]) allConfigPaths));
44   options = {
45     services.vault = {
46       enable = mkEnableOption "Vault daemon";
48       package = mkPackageOption pkgs "vault" { };
50       dev = mkOption {
51         type = types.bool;
52         default = false;
53         description = ''
54           In this mode, Vault runs in-memory and starts unsealed. This option is not meant production but for development and testing i.e. for nixos tests.
55         '';
56       };
58       devRootTokenID = mkOption {
59         type = types.nullOr types.str;
60         default = null;
61         description = ''
62           Initial root token. This only applies when {option}`services.vault.dev` is true
63         '';
64       };
66       address = mkOption {
67         type = types.str;
68         default = "127.0.0.1:8200";
69         description = "The name of the ip interface to listen to";
70       };
72       tlsCertFile = mkOption {
73         type = types.nullOr types.str;
74         default = null;
75         example = "/path/to/your/cert.pem";
76         description = "TLS certificate file. TLS will be disabled unless this option is set";
77       };
79       tlsKeyFile = mkOption {
80         type = types.nullOr types.str;
81         default = null;
82         example = "/path/to/your/key.pem";
83         description = "TLS private key file. TLS will be disabled unless this option is set";
84       };
86       listenerExtraConfig = mkOption {
87         type = types.lines;
88         default = ''
89           tls_min_version = "tls12"
90         '';
91         description = "Extra text appended to the listener section.";
92       };
94       storageBackend = mkOption {
95         type = types.enum [ "inmem" "file" "consul" "zookeeper" "s3" "azure" "dynamodb" "etcd" "mssql" "mysql" "postgresql" "swift" "gcs" "raft" ];
96         default = "inmem";
97         description = "The name of the type of storage backend";
98       };
100       storagePath = mkOption {
101         type = types.nullOr types.path;
102         default = if cfg.storageBackend == "file" || cfg.storageBackend == "raft" then "/var/lib/vault" else null;
103         defaultText = literalExpression ''
104           if config.${opt.storageBackend} == "file" || cfg.storageBackend == "raft"
105           then "/var/lib/vault"
106           else null
107         '';
108         description = "Data directory for file backend";
109       };
111       storageConfig = mkOption {
112         type = types.nullOr types.lines;
113         default = null;
114         description = ''
115           HCL configuration to insert in the storageBackend section.
117           Confidential values should not be specified here because this option's
118           value is written to the Nix store, which is publicly readable.
119           Provide credentials and such in a separate file using
120           [](#opt-services.vault.extraSettingsPaths).
121         '';
122       };
124       telemetryConfig = mkOption {
125         type = types.lines;
126         default = "";
127         description = "Telemetry configuration";
128       };
130       extraConfig = mkOption {
131         type = types.lines;
132         default = "";
133         description = "Extra text appended to {file}`vault.hcl`.";
134       };
136       extraSettingsPaths = mkOption {
137         type = types.listOf types.path;
138         default = [];
139         description = ''
140           Configuration files to load besides the immutable one defined by the NixOS module.
141           This can be used to avoid putting credentials in the Nix store, which can be read by any user.
143           Each path can point to a JSON- or HCL-formatted file, or a directory
144           to be scanned for files with `.hcl` or
145           `.json` extensions.
147           To upload the confidential file with NixOps, use for example:
149           ```
150           # https://releases.nixos.org/nixops/latest/manual/manual.html#opt-deployment.keys
151           deployment.keys."vault.hcl" = let db = import ./db-credentials.nix; in {
152             text = ${"''"}
153               storage "postgresql" {
154                 connection_url = "postgres://''${db.username}:''${db.password}@host.example.com/exampledb?sslmode=verify-ca"
155               }
156             ${"''"};
157             user = "vault";
158           };
159           services.vault.extraSettingsPaths = ["/run/keys/vault.hcl"];
160           services.vault.storageBackend = "postgresql";
161           users.users.vault.extraGroups = ["keys"];
162           ```
163         '';
164       };
165     };
166   };
168   config = mkIf cfg.enable {
169     assertions = [
170       {
171         assertion = cfg.storageBackend == "inmem" -> (cfg.storagePath == null && cfg.storageConfig == null);
172         message = ''The "inmem" storage expects no services.vault.storagePath nor services.vault.storageConfig'';
173       }
174       {
175         assertion = (
176           (cfg.storageBackend == "file" -> (cfg.storagePath != null && cfg.storageConfig == null)) &&
177           (cfg.storagePath != null -> (cfg.storageBackend == "file" || cfg.storageBackend == "raft"))
178         );
179         message = ''You must set services.vault.storagePath only when using the "file" or "raft" backend'';
180       }
181     ];
183     users.users.vault = {
184       name = "vault";
185       group = "vault";
186       uid = config.ids.uids.vault;
187       description = "Vault daemon user";
188     };
189     users.groups.vault.gid = config.ids.gids.vault;
191     systemd.tmpfiles.rules = optional (cfg.storagePath != null)
192       "d '${cfg.storagePath}' 0700 vault vault - -";
194     systemd.services.vault = {
195       description = "Vault server daemon";
197       wantedBy = ["multi-user.target"];
198       after = [ "network.target" ]
199            ++ optional (config.services.consul.enable && cfg.storageBackend == "consul") "consul.service";
201       restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients.
203       startLimitIntervalSec = 60;
204       startLimitBurst = 3;
205       serviceConfig = {
206         User = "vault";
207         Group = "vault";
208         ExecStart = "${cfg.package}/bin/vault server ${configOptions}";
209         ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
210         StateDirectory = "vault";
211         # In `dev` mode vault will put its token here
212         Environment = lib.optional (cfg.dev) "HOME=/var/lib/vault";
213         PrivateDevices = true;
214         PrivateTmp = true;
215         ProtectSystem = "full";
216         ProtectHome = "read-only";
217         AmbientCapabilities = "cap_ipc_lock";
218         NoNewPrivileges = true;
219         LimitCORE = 0;
220         KillSignal = "SIGINT";
221         TimeoutStopSec = "30s";
222         Restart = "on-failure";
223       };
225       unitConfig.RequiresMountsFor = optional (cfg.storagePath != null) cfg.storagePath;
226     };
227   };