at 24.11-pre 7.8 kB view raw
1{ config, lib, options, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.vault; 7 opt = options.services.vault; 8 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 ''; 34 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)); 40 41in 42 43{ 44 options = { 45 services.vault = { 46 enable = mkEnableOption "Vault daemon"; 47 48 package = mkPackageOption pkgs "vault" { }; 49 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 }; 57 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 }; 65 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 }; 71 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 }; 78 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 }; 85 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 }; 93 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 }; 99 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 }; 110 111 storageConfig = mkOption { 112 type = types.nullOr types.lines; 113 default = null; 114 description = '' 115 HCL configuration to insert in the storageBackend section. 116 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 }; 123 124 telemetryConfig = mkOption { 125 type = types.lines; 126 default = ""; 127 description = "Telemetry configuration"; 128 }; 129 130 extraConfig = mkOption { 131 type = types.lines; 132 default = ""; 133 description = "Extra text appended to {file}`vault.hcl`."; 134 }; 135 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. 142 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. 146 147 To upload the confidential file with NixOps, use for example: 148 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 }; 167 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 ]; 182 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; 190 191 systemd.tmpfiles.rules = optional (cfg.storagePath != null) 192 "d '${cfg.storagePath}' 0700 vault vault - -"; 193 194 systemd.services.vault = { 195 description = "Vault server daemon"; 196 197 wantedBy = ["multi-user.target"]; 198 after = [ "network.target" ] 199 ++ optional (config.services.consul.enable && cfg.storageBackend == "consul") "consul.service"; 200 201 restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients. 202 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 }; 224 225 unitConfig.RequiresMountsFor = optional (cfg.storagePath != null) cfg.storagePath; 226 }; 227 }; 228 229}