From de69ca69c9270b902a45e8d0a5f135118fc9e073 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 25 Nov 2023 22:41:48 +0000 Subject: [PATCH] bitwarden: brought in line with the nixpkgs again. (again) --- .../_bitwarden-directory-connector.nix | 27 ++- .../bitwarden/_bitwarden_sync_module.nix | 208 +++++++++--------- applications/bitwarden/bitwarden_sync.nix | 2 +- 3 files changed, 116 insertions(+), 121 deletions(-) diff --git a/applications/bitwarden/_bitwarden-directory-connector.nix b/applications/bitwarden/_bitwarden-directory-connector.nix index 249ac82..95fd336 100644 --- a/applications/bitwarden/_bitwarden-directory-connector.nix +++ b/applications/bitwarden/_bitwarden-directory-connector.nix @@ -3,12 +3,11 @@ buildNpmPackage, fetchFromGitHub, pkgs, - git, + jq, python3, pkg-config, libsecret, nodejs_18, - makeWrapper, }: let buildNpmPackage' = buildNpmPackage.override {nodejs = nodejs_18;}; in @@ -20,10 +19,14 @@ in owner = "bitwarden"; repo = "directory-connector"; rev = "v${version}"; - hash = "sha256-CgaCnMWNVWCJBypNcdoseVCwD8Mlq4YaWpK+VZT/7Qk="; - leaveDotGit = true; + hash = "sha256-PlOtTh+rpTxAv8ajHBDHZuL7yeeLVpbAfKEDPQlejIg="; }; + postPatch = '' + jq 'del(.scripts.preinstall)' package.json > package.json.tmp + mv -f package.json{.tmp,} + ''; + npmDepsHash = "sha256-jBAWWY12qeX2EDhUvT3TQpnQvYXRsIilRrXGpVzxYvw="; env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1"; @@ -33,15 +36,16 @@ in installPhase = '' runHook preInstall - mkdir -p $out/bin - cp -R {build-cli,node_modules} $out + mkdir -p $out/libexec/bitwarden-directory-connector + cp -R {build-cli,node_modules} $out/libexec/bitwarden-directory-connector runHook postInstall ''; # needs to be wrapped with nodejs so that it can be executed postInstall = '' - chmod +x $out/build-cli/bwdc.js - makeWrapper $out/build-cli/bwdc.js $out/bin/${pname} --prefix PATH:"${lib.makeBinPath [nodejs_18]}" + chmod +x $out/libexec/bitwarden-directory-connector/build-cli/bwdc.js + mkdir -p $out/bin + ln -s $out/libexec/bitwarden-directory-connector/build-cli/bwdc.js $out/bin/bitwarden-directory-connector ''; buildInputs = [ @@ -49,18 +53,17 @@ in ]; nativeBuildInputs = [ - git + jq python3 pkg-config - makeWrapper ]; meta = with lib; { - description = "A LDAP connector for Bitwarden"; + description = "LDAP connector for Bitwarden"; homepage = "https://github.com/bitwarden/directory-connector"; license = licenses.gpl3Only; maintainers = with maintainers; [Silver-Golden]; platforms = platforms.linux; - mainProgram = "${pname}"; + mainProgram = "bitwarden-directory-connector"; }; } diff --git a/applications/bitwarden/_bitwarden_sync_module.nix b/applications/bitwarden/_bitwarden_sync_module.nix index 7582397..939e403 100644 --- a/applications/bitwarden/_bitwarden_sync_module.nix +++ b/applications/bitwarden/_bitwarden_sync_module.nix @@ -1,37 +1,20 @@ { - pkgs, config, lib, + pkgs, ... }: with lib; let - cfg = config.services.bitwarden_directory_connector; - - ldap_data = builtins.toJSON cfg.ldap; - sync_data = builtins.toJSON cfg.sync; + cfg = config.services.bitwarden-directory-connector; in { - imports = []; - - options.services.bitwarden_directory_connector = { + options.services.bitwarden-directory-connector = { enable = mkEnableOption "Bitwarden Directory Connector"; - package = mkOption { - type = types.package; - default = pkgs.bitwarden-directory-connector; - defaultText = literalExpression "pkgs.bitwarden-directory-connector"; - description = lib.mdDoc "Reference to the Bitwarden Directory Connector package"; - example = literalExpression "pkgs.bitwarden-directory-connector-example"; - }; - - binary_name = mkOption { - type = types.str; - description = lib.mdDoc "The main binary for the connector."; - default = "bitwarden-directory-connector"; - }; + package = mkPackageOption pkgs "bitwarden-directory-connector" {}; domain = mkOption { type = types.str; - description = lib.mdDoc "The domain the Bitwarden/Vaultwarden is accessable on."; + description = lib.mdDoc "The domain the Bitwarden/Vaultwarden is accessible on."; example = "https://vaultwarden.example.com"; }; @@ -41,68 +24,71 @@ in { default = "bwdc"; }; - directory = mkOption { - type = types.str; - description = lib.mdDoc "Folder to store the config file."; - default = "/etc/bitwarden/bwdc"; - }; interval = mkOption { type = types.str; default = "*:0,15,30,45"; - description = lib.mdDoc "When to run the connector, OnCalendar syntax."; + description = lib.mdDoc "The interval when to run the connector. This uses systemd's OnCalendar syntax."; }; ldap = mkOption { - description = lib.mdDoc "Options to configurate LDAP."; - type = types.submodule { + description = lib.mdDoc "Options to configure the LDAP connection."; + default = {}; + + type = types.submodule ({ + config, + options, + ... + }: { freeformType = types.attrsOf (pkgs.formats.json {}).type; + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + ssl = mkOption { type = types.bool; default = false; - description = lib.mdDoc "Use SSL."; + description = lib.mdDoc "Whether to use TLS."; }; startTls = mkOption { type = types.bool; default = false; - description = lib.mdDoc "Use STARTTLS."; - }; - sslAllowUnauthorized = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc ""; - }; - port = mkOption { - type = types.int; - default = 389; - description = lib.mdDoc "Port LDAP is accessable on"; - }; - currentUser = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Unknown what this does."; - }; - ad = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Is Active Directory."; - }; - pagedSearch = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "The LDAP server paginates search results."; + description = lib.mdDoc "Whether to use STARTTLS."; }; hostname = mkOption { type = types.str; - description = lib.mdDoc "The host the LDAP is accessable on."; + description = lib.mdDoc "The host the LDAP is accessible on."; example = "ldap.example.com"; }; + port = mkOption { + type = types.port; + default = 389; + description = lib.mdDoc "Port LDAP is accessible on."; + }; + + ad = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP Server is an Active Directory."; + }; + + pagedSearch = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Whether the LDAP server paginates search results."; + }; + rootPath = mkOption { type = types.str; - description = lib.mdDoc "Root path for LDAP"; + description = lib.mdDoc "Root path for LDAP."; example = "dc=example,dc=com"; }; @@ -112,26 +98,43 @@ in { example = "cn=admin,dc=example,dc=com"; }; }; - }; + }); }; sync = mkOption { - description = lib.mdDoc "Options to configurate what gets synced."; - type = types.submodule { + description = lib.mdDoc "Options to configure what gets synced."; + default = {}; + + type = types.submodule ({ + config, + options, + ... + }: { freeformType = types.attrsOf (pkgs.formats.json {}).type; + config.finalJSON = builtins.toJSON (removeAttrs config (filter (x: x == "finalJSON" || ! options.${x}.isDefined or false) (attrNames options))); + options = { + finalJSON = mkOption { + type = (pkgs.formats.json {}).type; + internal = true; + readOnly = true; + visible = false; + }; + removeDisabled = mkOption { type = types.bool; default = true; description = lib.mdDoc "Remove users from bitwarden groups if no longer in the ldap group."; }; + overwriteExisting = mkOption { type = types.bool; default = false; description = lib.mdDoc "Remove and re-add users/groups, See https://bitwarden.com/help/user-group-filters/#overwriting-syncs for more details."; }; + largeImport = mkOption { type = types.bool; default = false; @@ -146,7 +149,7 @@ in { creationDateAttribute = mkOption { type = types.str; - description = lib.mdDoc "Attribute that lists a users creation date."; + description = lib.mdDoc "Attribute that lists a user's creation date."; example = "whenCreated"; }; @@ -157,13 +160,13 @@ in { }; emailPrefixAttribute = mkOption { type = types.str; - description = lib.mdDoc "Attribute that has a users username."; - default = "accountName"; + description = lib.mdDoc "The attribute that contains the users username."; + example = "accountName"; }; emailSuffix = mkOption { type = types.str; description = lib.mdDoc "Suffix for the email, normally @example.com."; - default = "@example.com"; + example = "@example.com"; }; users = mkOption { @@ -178,7 +181,7 @@ in { }; userObjectClass = mkOption { type = types.str; - description = lib.mdDoc "A class that users will have."; + description = lib.mdDoc "Class that users must have."; default = "inetOrgPerson"; }; userEmailAttribute = mkOption { @@ -188,7 +191,7 @@ in { }; userFilter = mkOption { type = types.str; - description = lib.mdDoc "Filter for users."; + description = lib.mdDoc "LDAP filter for users."; example = "(memberOf=cn=sales,ou=groups,dc=example,dc=com)"; default = ""; }; @@ -196,7 +199,7 @@ in { groups = mkOption { type = types.bool; default = false; - description = lib.mdDoc "Sync groups."; + description = lib.mdDoc "Whether to sync ldap groups into BitWarden."; }; groupPath = mkOption { type = types.str; @@ -215,25 +218,26 @@ in { }; groupFilter = mkOption { type = types.str; - description = lib.mdDoc "Filter for groups."; + description = lib.mdDoc "LDAP filter for groups."; example = "(cn=sales)"; default = ""; }; }; - }; + }); }; secrets = { - ldap = mkOption rec { + ldap = mkOption { type = types.str; - description = "Auth for the LDAP, has value defined in {option}`pw_env"; + description = "Path to file that contains LDAP password for user in {option}`ldap.username"; }; + bitwarden = { - client_path_id = mkOption rec { + client_path_id = mkOption { type = types.str; description = "Path to file that contains Client ID."; }; - client_path_secret = mkOption rec { + client_path_secret = mkOption { type = types.str; description = "Path to file that contains Client Secret."; }; @@ -243,20 +247,16 @@ in { config = mkIf cfg.enable { users.groups."${cfg.user}" = {}; - users.users."${cfg.user}" = { - createHome = true; isSystemUser = true; - home = "${cfg.directory}"; - group = "${cfg.user}"; - homeMode = "711"; + group = cfg.user; }; systemd = { timers.bitwarden_directory_connector = { description = "Sync timer for Bitwarden Directory Connector"; wantedBy = ["timers.target"]; - partOf = ["bitwarden_directory_connector.service"]; + after = ["network-online.target"]; timerConfig = { OnCalendar = cfg.interval; Unit = "bitwarden_directory_connector.service"; @@ -266,62 +266,54 @@ in { services.bitwarden_directory_connector = { description = "Main process for Bitwarden Directory Connector"; - wantedBy = ["multi-user.target"]; - after = ["network-online.target"]; - wants = []; path = [pkgs.jq]; environment = { - BITWARDENCLI_CONNECTOR_APPDATA_DIR = cfg.directory; + BITWARDENCLI_CONNECTOR_APPDATA_DIR = "/tmp"; BITWARDENCLI_CONNECTOR_PLAINTEXT_SECRETS = "true"; }; serviceConfig = { Type = "oneshot"; User = "${cfg.user}"; - Group = "${cfg.user}"; + PrivateTmp = true; ExecStartPre = pkgs.writeShellScript "bitwarden_directory_connector-config" '' - # create the config file - ${cfg.package}/bin/${cfg.binary_name} data-file - touch -- ${escapeShellArg cfg.directory}/data.json.tmp - chmod 600 ${escapeShellArg cfg.directory}/data.json - chmod 600 -- ${escapeShellArg cfg.directory}/data.json.tmp + set -eo pipefail - ${cfg.package}/bin/${cfg.binary_name} config server ${cfg.domain} + # create the config file + ${lib.getExe cfg.package} data-file + touch /tmp/data.json.tmp + chmod 600 /tmp/data.json{,.tmp} + + ${lib.getExe cfg.package} config server ${cfg.domain} # now login to set credentials export BW_CLIENTID="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_id})" export BW_CLIENTSECRET="$(< ${escapeShellArg cfg.secrets.bitwarden.client_path_secret})" - ${cfg.package}/bin/${cfg.binary_name} login + ${lib.getExe cfg.package} login jq '.authenticatedAccounts[0] as $account | .[$account].directoryConfigurations.ldap |= $ldap_data | .[$account].directorySettings.organizationId |= $orgID | .[$account].directorySettings.sync |= $sync_data' \ - --argjson ldap_data ${escapeShellArg ldap_data} \ + --argjson ldap_data ${escapeShellArg cfg.ldap.finalJSON} \ --arg orgID "''${BW_CLIENTID//organization.}" \ - --argjson sync_data ${escapeShellArg sync_data} \ - ${escapeShellArg cfg.directory}/data.json \ - > ${escapeShellArg cfg.directory}/data.json.tmp + --argjson sync_data ${escapeShellArg cfg.sync.finalJSON} \ + /tmp/data.json \ + > /tmp/data.json.tmp - mv -f -- ${escapeShellArg cfg.directory}/data.json.tmp ${escapeShellArg cfg.directory}/data.json + mv -f /tmp/data.json.tmp /tmp/data.json # final config - ${cfg.package}/bin/${cfg.binary_name} config directory 0 - ${cfg.package}/bin/${cfg.binary_name} config ldap.password --secretfile ${cfg.secrets.ldap} + ${lib.getExe cfg.package} config directory 0 + ${lib.getExe cfg.package} config ldap.password --secretfile ${cfg.secrets.ldap} ''; - ExecStart = "${cfg.package}/bin/${cfg.binary_name} sync"; - - ExecStartPost = pkgs.writeShellScript "bitwarden_directory_connector-cleanup" '' - rm -f -- ${escapeShellArg cfg.directory}/data.json - ''; + ExecStart = "${lib.getExe cfg.package} sync"; }; }; }; }; - meta = with lib; { - maintainers = with maintainers; [Silver-Golden]; - }; + meta.maintainers = with maintainers; [Silver-Golden]; } diff --git a/applications/bitwarden/bitwarden_sync.nix b/applications/bitwarden/bitwarden_sync.nix index 4136b97..880d4fa 100644 --- a/applications/bitwarden/bitwarden_sync.nix +++ b/applications/bitwarden/bitwarden_sync.nix @@ -29,7 +29,7 @@ in { group = user; }; - services.bitwarden_directory_connector = { + services.bitwarden-directory-connector = { enable = true; user = user;