at master 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 "network.target" 229 ] 230 ++ lib.optional (config.services.consul.enable && cfg.storageBackend == "consul") "consul.service"; 231 232 restartIfChanged = false; # do not restart on "nixos-rebuild switch". It would seal the storage and disrupt the clients. 233 234 startLimitIntervalSec = 60; 235 startLimitBurst = 3; 236 serviceConfig = { 237 User = "vault"; 238 Group = "vault"; 239 ExecStart = "${cfg.package}/bin/vault server ${configOptions}"; 240 ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; 241 StateDirectory = "vault"; 242 # In `dev` mode vault will put its token here 243 Environment = lib.optional (cfg.dev) "HOME=/var/lib/vault"; 244 PrivateDevices = true; 245 PrivateTmp = true; 246 ProtectSystem = "full"; 247 ProtectHome = "read-only"; 248 AmbientCapabilities = "cap_ipc_lock"; 249 NoNewPrivileges = true; 250 LimitCORE = 0; 251 KillSignal = "SIGINT"; 252 TimeoutStopSec = "30s"; 253 Restart = "on-failure"; 254 }; 255 256 unitConfig.RequiresMountsFor = lib.optional (cfg.storagePath != null) cfg.storagePath; 257 }; 258 }; 259 260}