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