about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/security/bitwarden_rs/backup.sh17
-rw-r--r--nixos/modules/services/security/bitwarden_rs/default.nix126
-rw-r--r--pkgs/tools/security/bitwarden_rs/default.nix27
-rw-r--r--pkgs/tools/security/bitwarden_rs/vault.nix25
-rw-r--r--pkgs/top-level/all-packages.nix4
6 files changed, 200 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 341e45f9e142..9e589258ee0b 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -707,6 +707,7 @@
   ./services/search/hound.nix
   ./services/search/kibana.nix
   ./services/search/solr.nix
+  ./services/security/bitwarden_rs/default.nix
   ./services/security/certmgr.nix
   ./services/security/cfssl.nix
   ./services/security/clamav.nix
diff --git a/nixos/modules/services/security/bitwarden_rs/backup.sh b/nixos/modules/services/security/bitwarden_rs/backup.sh
new file mode 100644
index 000000000000..264a7da9cbb6
--- /dev/null
+++ b/nixos/modules/services/security/bitwarden_rs/backup.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+# Based on: https://github.com/dani-garcia/bitwarden_rs/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/bitwarden_rs/default.nix b/nixos/modules/services/security/bitwarden_rs/default.nix
new file mode 100644
index 000000000000..bb036ee020f4
--- /dev/null
+++ b/nixos/modules/services/security/bitwarden_rs/default.nix
@@ -0,0 +1,126 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.bitwarden_rs;
+  user = config.users.users.bitwarden_rs.name;
+  group = config.users.groups.bitwarden_rs.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;
+
+  configFile = pkgs.writeText "bitwarden_rs.env" (concatMapStrings (s: s + "\n") (
+    (concatLists (mapAttrsToList (name: value:
+      if value != null then [ "${nameToEnvVar name}=${if isBool value then boolToString value else toString value}" ] else []
+    ) cfg.config))));
+
+in {
+  options.services.bitwarden_rs = with types; {
+    enable = mkEnableOption "bitwarden_rs";
+
+    backupDir = mkOption {
+      type = nullOr str;
+      default = null;
+      description = ''
+        The directory under which bitwarden_rs will backup its persistent data.
+      '';
+    };
+
+    config = mkOption {
+      type = attrsOf (nullOr (either (either bool int) str));
+      default = {};
+      example = literalExample ''
+        {
+          domain = https://bw.domain.tld:8443;
+          signupsAllowed = true;
+          rocketPort = 8222;
+          rocketLog = "critical";
+        }
+      '';
+      description = ''
+        The configuration of bitwarden_rs 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 a environment file will be generated
+        that is passed to bitwarden_rs's systemd service.
+
+        The available configuration options can be found in
+        <link xlink:href="https://github.com/dani-garcia/bitwarden_rs/blob/1.8.0/.env.template">the environment template file</link>.
+      '';
+      apply = config: optionalAttrs config.webVaultEnabled {
+        webVaultFolder = "${pkgs.bitwarden_rs-vault}/share/bitwarden_rs/vault";
+      } // config;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.bitwarden_rs.config = {
+      dataFolder = "/var/lib/bitwarden_rs";
+      webVaultEnabled = mkDefault true;
+    };
+
+    users.users.bitwarden_rs = { inherit group; };
+    users.groups.bitwarden_rs = { };
+
+    systemd.services.bitwarden_rs = {
+      after = [ "network.target" ];
+      path = with pkgs; [ openssl ];
+      serviceConfig = {
+        User = user;
+        Group = group;
+        EnvironmentFile = configFile;
+        ExecStart = "${pkgs.bitwarden_rs}/bin/bitwarden_rs";
+        LimitNOFILE = "1048576";
+        LimitNPROC = "64";
+        PrivateTmp = "true";
+        PrivateDevices = "true";
+        ProtectHome = "true";
+        ProtectSystem = "strict";
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        StateDirectory = "bitwarden_rs";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    systemd.services.backup-bitwarden_rs = mkIf (cfg.backupDir != null) {
+      description = "Backup bitwarden_rs";
+      environment = {
+        DATA_FOLDER = "/var/lib/bitwarden_rs";
+        BACKUP_FOLDER = cfg.backupDir;
+      };
+      path = with pkgs; [ sqlite ];
+      serviceConfig = {
+        SyslogIdentifier = "backup-bitwarden_rs";
+        User = mkDefault user;
+        Group = mkDefault group;
+        ExecStart = "${pkgs.bash}/bin/bash ${./backup.sh}";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    systemd.timers.backup-bitwarden_rs = mkIf (cfg.backupDir != null) {
+      description = "Backup bitwarden_rs on time";
+      timerConfig = {
+        OnCalendar = mkDefault "23:00";
+        Persistent = "true";
+        Unit = "backup-bitwarden_rs.service";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/pkgs/tools/security/bitwarden_rs/default.nix b/pkgs/tools/security/bitwarden_rs/default.nix
new file mode 100644
index 000000000000..788c561da3c5
--- /dev/null
+++ b/pkgs/tools/security/bitwarden_rs/default.nix
@@ -0,0 +1,27 @@
+{ lib, rustPlatform, fetchFromGitHub, pkgconfig, openssl }:
+
+rustPlatform.buildRustPackage rec {
+  pname = "bitwarden_rs";
+  version = "1.8.0";
+
+  src = fetchFromGitHub {
+    owner = "dani-garcia";
+    repo = pname;
+    rev = version;
+    sha256 = "0jz9r6ck6sfz4ig95x0ja6g5ikyq6z0xw1zn9zf4kxha4klqqbkx";
+  };
+
+  buildInputs = [ pkgconfig openssl ];
+
+  RUSTC_BOOTSTRAP = 1;
+
+  cargoSha256 = "0bzid5wrpcrghazv5652ghyv4amp298p5kfridswv175kmr9gg0x";
+
+  meta = with lib; {
+    description = "An unofficial lightweight implementation of the Bitwarden server API using Rust and SQLite";
+    homepage = https://github.com/dani-garcia/bitwarden_rs;
+    license = licenses.gpl3;
+    maintainers = with maintainers; [ msteen ];
+    platforms = platforms.all;
+  };
+}
diff --git a/pkgs/tools/security/bitwarden_rs/vault.nix b/pkgs/tools/security/bitwarden_rs/vault.nix
new file mode 100644
index 000000000000..1f7f23049d06
--- /dev/null
+++ b/pkgs/tools/security/bitwarden_rs/vault.nix
@@ -0,0 +1,25 @@
+{ stdenv, fetchurl }:
+
+stdenv.mkDerivation rec {
+  pname = "bitwarden_rs-vault";
+  version = "2.9.0";
+
+  src = fetchurl {
+    url = "https://github.com/dani-garcia/bw_web_builds/releases/download/v${version}/bw_web_v${version}.tar.gz";
+    sha256 = "0kh7nqd688ilw73n4pw3s6fahghwbhiql548js6cdwsp4car3vbb";
+  };
+
+  buildCommand = ''
+    mkdir -p $out/share/bitwarden_rs/vault
+    cd $out/share/bitwarden_rs/vault
+    tar xf $src
+  '';
+
+  meta = with stdenv.lib; {
+    description = "Integrates the web vault into bitwarden_rs";
+    homepage = https://github.com/dani-garcia/bw_web_builds;
+    platforms = platforms.all;
+    license = licenses.gpl3;
+    maintainers = with maintainers; [ msteen ];
+  };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 27f88d4434b1..d7ef12042eb2 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -666,6 +666,10 @@ in
 
   bcachefs-tools = callPackage ../tools/filesystems/bcachefs-tools { };
 
+  bitwarden_rs = callPackage ../tools/security/bitwarden_rs { };
+
+  bitwarden_rs-vault = callPackage ../tools/security/bitwarden_rs/vault.nix { };
+
   bitwarden-cli = callPackage ../tools/security/bitwarden-cli { };
 
   bmap-tools = callPackage ../tools/misc/bmap-tools { };