From dc1b56c714095d787f52c45c83723944dfeef779 Mon Sep 17 00:00:00 2001 From: K900 Date: Thu, 1 Jul 2021 12:14:51 +0300 Subject: vaultwarden: update to 1.22.1, rename from bitwarden_rs I tried to make this as non-breaking as possible, but it will still break things slightly for people expecting certain file names in the packages themselves. --- .../services/security/vaultwarden/backup.sh | 17 +++ .../services/security/vaultwarden/default.nix | 168 +++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 nixos/modules/services/security/vaultwarden/backup.sh create mode 100644 nixos/modules/services/security/vaultwarden/default.nix (limited to 'nixos/modules/services/security/vaultwarden') diff --git a/nixos/modules/services/security/vaultwarden/backup.sh b/nixos/modules/services/security/vaultwarden/backup.sh new file mode 100644 index 0000000000000..2a3de0ab1deeb --- /dev/null +++ b/nixos/modules/services/security/vaultwarden/backup.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# Based on: https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault +if ! mkdir -p "$BACKUP_FOLDER"; then + echo "Could not create backup folder '$BACKUP_FOLDER'" >&2 + exit 1 +fi + +if [[ ! -f "$DATA_FOLDER"/db.sqlite3 ]]; then + echo "Could not find SQLite database file '$DATA_FOLDER/db.sqlite3'" >&2 + exit 1 +fi + +sqlite3 "$DATA_FOLDER"/db.sqlite3 ".backup '$BACKUP_FOLDER/db.sqlite3'" +cp "$DATA_FOLDER"/rsa_key.{der,pem,pub.der} "$BACKUP_FOLDER" +cp -r "$DATA_FOLDER"/attachments "$BACKUP_FOLDER" +cp -r "$DATA_FOLDER"/icon_cache "$BACKUP_FOLDER" diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix new file mode 100644 index 0000000000000..940ac7832daed --- /dev/null +++ b/nixos/modules/services/security/vaultwarden/default.nix @@ -0,0 +1,168 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.vaultwarden; + user = config.users.users.vaultwarden.name; + group = config.users.groups.vaultwarden.name; + + # Convert name from camel case (e.g. disable2FARemember) to upper case snake case (e.g. DISABLE_2FA_REMEMBER). + nameToEnvVar = name: + let + parts = builtins.split "([A-Z0-9]+)" name; + partsToEnvVar = parts: foldl' (key: x: let last = stringLength key - 1; in + if isList x then key + optionalString (key != "" && substring last 1 key != "_") "_" + head x + else if key != "" && elem (substring 0 1 x) lowerChars then # to handle e.g. [ "disable" [ "2FAR" ] "emember" ] + substring 0 last key + optionalString (substring (last - 1) 1 key != "_") "_" + substring last 1 key + toUpper x + else key + toUpper x) "" parts; + in if builtins.match "[A-Z0-9_]+" name != null then name else partsToEnvVar parts; + + # Due to the different naming schemes allowed for config keys, + # we can only check for values consistently after converting them to their corresponding environment variable name. + configEnv = + let + configEnv = listToAttrs (concatLists (mapAttrsToList (name: value: + if value != null then [ (nameValuePair (nameToEnvVar name) (if isBool value then boolToString value else toString value)) ] else [] + ) cfg.config)); + in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") { + WEB_VAULT_FOLDER = "${pkgs.vaultwarden-vault}/share/vaultwarden/vault"; + } // configEnv; + + configFile = pkgs.writeText "vaultwarden.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv)); + + vaultwarden = pkgs.vaultwarden.override { inherit (cfg) dbBackend; }; + +in { + imports = [ + (mkRenamedOptionModule [ "services" "bitwarden_rs" ] [ "services" "vaultwarden" ]) + ]; + + options.services.vaultwarden = with types; { + enable = mkEnableOption "vaultwarden"; + + dbBackend = mkOption { + type = enum [ "sqlite" "mysql" "postgresql" ]; + default = "sqlite"; + description = '' + Which database backend vaultwarden will be using. + ''; + }; + + backupDir = mkOption { + type = nullOr str; + default = null; + description = '' + The directory under which vaultwarden will backup its persistent data. + ''; + }; + + config = mkOption { + type = attrsOf (nullOr (oneOf [ bool int str ])); + default = {}; + example = literalExample '' + { + domain = "https://bw.domain.tld:8443"; + signupsAllowed = true; + rocketPort = 8222; + rocketLog = "critical"; + } + ''; + description = '' + The configuration of vaultwarden is done through environment variables, + therefore the names are converted from camel case (e.g. disable2FARemember) + to upper case snake case (e.g. DISABLE_2FA_REMEMBER). + In this conversion digits (0-9) are handled just like upper case characters, + so foo2 would be converted to FOO_2. + Names already in this format remain unchanged, so FOO2 remains FOO2 if passed as such, + even though foo2 would have been converted to FOO_2. + This allows working around any potential future conflicting naming conventions. + + Based on the attributes passed to this config option an environment file will be generated + that is passed to vaultwarden's systemd service. + + The available configuration options can be found in + the environment template file. + ''; + }; + + environmentFile = mkOption { + type = with types; nullOr path; + default = null; + example = "/root/vaultwarden.env"; + description = '' + Additional environment file as defined in + systemd.exec5 + . + + Secrets like ADMIN_TOKEN and SMTP_PASSWORD + may be passed to the service without adding them to the world-readable Nix store. + + Note that this file needs to be available on the host on which + vaultwarden is running. + ''; + }; + }; + + config = mkIf cfg.enable { + assertions = [ { + assertion = cfg.backupDir != null -> cfg.dbBackend == "sqlite"; + message = "Backups for database backends other than sqlite will need customization"; + } ]; + + users.users.vaultwarden = { + inherit group; + isSystemUser = true; + }; + users.groups.vaultwarden = { }; + + systemd.services.vaultwarden = { + aliases = [ "bitwarden_rs" ]; + after = [ "network.target" ]; + path = with pkgs; [ openssl ]; + serviceConfig = { + User = user; + Group = group; + EnvironmentFile = [ configFile ] ++ optional (cfg.environmentFile != null) cfg.environmentFile; + ExecStart = "${vaultwarden}/bin/vaultwarden"; + LimitNOFILE = "1048576"; + PrivateTmp = "true"; + PrivateDevices = "true"; + ProtectHome = "true"; + ProtectSystem = "strict"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + StateDirectory = "bitwarden_rs"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.services.backup-vaultwarden = mkIf (cfg.backupDir != null) { + aliases = [ "backup-bitwarden_rs" ]; + description = "Backup vaultwarden"; + environment = { + DATA_FOLDER = "/var/lib/bitwarden_rs"; + BACKUP_FOLDER = cfg.backupDir; + }; + path = with pkgs; [ sqlite ]; + serviceConfig = { + SyslogIdentifier = "backup-vaultwarden"; + Type = "oneshot"; + User = mkDefault user; + Group = mkDefault group; + ExecStart = "${pkgs.bash}/bin/bash ${./backup.sh}"; + }; + wantedBy = [ "multi-user.target" ]; + }; + + systemd.timers.backup-vaultwarden = mkIf (cfg.backupDir != null) { + aliases = [ "backup-bitwarden_rs" ]; + description = "Backup vaultwarden on time"; + timerConfig = { + OnCalendar = mkDefault "23:00"; + Persistent = "true"; + Unit = "backup-vaultwarden.service"; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; +} -- cgit 1.4.1