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