diff options
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/programs/i3lock.nix | 58 | ||||
-rw-r--r-- | nixos/modules/services/networking/rpcbind.nix | 10 | ||||
-rw-r--r-- | nixos/modules/services/web-apps/dolibarr.nix | 19 | ||||
-rw-r--r-- | nixos/modules/tasks/filesystems/zfs.nix | 4 | ||||
-rw-r--r-- | nixos/tests/acme.nix | 62 |
6 files changed, 125 insertions, 29 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 21845e0d8bf3..b099dcc71c40 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -180,6 +180,7 @@ ./programs/hamster.nix ./programs/htop.nix ./programs/iftop.nix + ./programs/i3lock.nix ./programs/iotop.nix ./programs/java.nix ./programs/k3b.nix diff --git a/nixos/modules/programs/i3lock.nix b/nixos/modules/programs/i3lock.nix new file mode 100644 index 000000000000..466ae59c9277 --- /dev/null +++ b/nixos/modules/programs/i3lock.nix @@ -0,0 +1,58 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.i3lock; + +in { + + ###### interface + + options = { + programs.i3lock = { + enable = mkEnableOption (mdDoc "i3lock"); + package = mkOption { + type = types.package; + default = pkgs.i3lock; + defaultText = literalExpression "pkgs.i3lock"; + example = literalExpression '' + pkgs.i3lock-color + ''; + description = mdDoc '' + Specify which package to use for the i3lock program, + The i3lock package must include a i3lock file or link in its out directory in order for the u2fSupport option to work correctly. + ''; + }; + u2fSupport = mkOption { + type = types.bool; + default = false; + example = true; + description = mdDoc '' + Whether to enable U2F support in the i3lock program. + U2F enables authentication using a hardware device, such as a security key. + When U2F support is enabled, the i3lock program will set the setuid bit on the i3lock binary and enable the pam u2fAuth service, + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + security.wrappers.i3lock = mkIf cfg.u2fSupport { + setuid = true; + owner = "root"; + group = "root"; + source = "${cfg.package.out}/bin/i3lock"; + }; + + security.pam.services.i3lock.u2fAuth = cfg.u2fSupport; + + }; + +} diff --git a/nixos/modules/services/networking/rpcbind.nix b/nixos/modules/services/networking/rpcbind.nix index aa04214debb0..60e78dfec51b 100644 --- a/nixos/modules/services/networking/rpcbind.nix +++ b/nixos/modules/services/networking/rpcbind.nix @@ -35,6 +35,16 @@ with lib; systemd.services.rpcbind = { wantedBy = [ "multi-user.target" ]; + # rpcbind performs a check for /var/run/rpcbind.lock at startup + # and will crash if /var/run isn't present. In the stock NixOS + # var.conf tmpfiles configuration file, /var/run is symlinked to + # /run, so rpcbind can enter a race condition in which /var/run + # isn't symlinked yet but tries to interact with the path, so + # controlling the order explicitly here ensures that rpcbind can + # start successfully. The `wants` instead of `requires` should + # avoid creating a strict/brittle dependency. + wants = [ "systemd-tmpfiles-setup.service" ]; + after = [ "systemd-tmpfiles-setup.service" ]; }; users.users.rpc = { diff --git a/nixos/modules/services/web-apps/dolibarr.nix b/nixos/modules/services/web-apps/dolibarr.nix index 5335c439329c..f262099354d2 100644 --- a/nixos/modules/services/web-apps/dolibarr.nix +++ b/nixos/modules/services/web-apps/dolibarr.nix @@ -1,11 +1,11 @@ { config, pkgs, lib, ... }: let - inherit (lib) any boolToString concatStringsSep isBool isString literalExpression mapAttrsToList mkDefault mkEnableOption mkIf mkOption optionalAttrs types; + inherit (lib) any boolToString concatStringsSep isBool isString mapAttrsToList mkDefault mkEnableOption mkIf mkMerge mkOption optionalAttrs types; package = pkgs.dolibarr.override { inherit (cfg) stateDir; }; cfg = config.services.dolibarr; - vhostCfg = config.services.nginx.virtualHosts."${cfg.domain}"; + vhostCfg = lib.optionalAttr (cfg.nginx != null) config.services.nginx.virtualHosts."${cfg.domain}"; mkConfigFile = filename: settings: let @@ -38,7 +38,7 @@ let force_install_database = cfg.database.name; force_install_databaselogin = cfg.database.user; - force_install_mainforcehttps = vhostCfg.forceSSL; + force_install_mainforcehttps = vhostCfg.forceSSL or false; force_install_createuser = false; force_install_dolibarrlogin = null; } // optionalAttrs (cfg.database.passwordFile != null) { @@ -183,7 +183,8 @@ in }; # implementation - config = mkIf cfg.enable { + config = mkIf cfg.enable (mkMerge [ + { assertions = [ { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user; @@ -214,7 +215,7 @@ in # Security settings dolibarr_main_prod = true; - dolibarr_main_force_https = vhostCfg.forceSSL; + dolibarr_main_force_https = vhostCfg.forceSSL or false; dolibarr_main_restrict_os_commands = "${pkgs.mariadb}/bin/mysqldump, ${pkgs.mariadb}/bin/mysql"; dolibarr_nocsrfcheck = false; dolibarr_main_instance_unique_id = '' @@ -314,7 +315,9 @@ in users.groups = optionalAttrs (cfg.group == "dolibarr") { dolibarr = { }; }; - - users.users."${config.services.nginx.group}".extraGroups = [ cfg.group ]; - }; + } + (mkIf (cfg.nginx != null) { + users.users."${config.services.nginx.group}".extraGroups = mkIf (cfg.nginx != null) [ cfg.group ]; + }) +]); } diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix index 4b4f4cc801ab..0f14f2b501c2 100644 --- a/nixos/modules/tasks/filesystems/zfs.nix +++ b/nixos/modules/tasks/filesystems/zfs.nix @@ -503,6 +503,10 @@ in assertion = !cfgZfs.forceImportAll || cfgZfs.forceImportRoot; message = "If you enable boot.zfs.forceImportAll, you must also enable boot.zfs.forceImportRoot"; } + { + assertion = cfgZfs.allowHibernation -> !cfgZfs.forceImportRoot && !cfgZfs.forceImportAll; + message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption"; + } ]; boot = { diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix index 64bc99f6d325..d62bf0c0fd92 100644 --- a/nixos/tests/acme.nix +++ b/nixos/tests/acme.nix @@ -144,7 +144,11 @@ in { name = "acme"; - meta.maintainers = lib.teams.acme.members; + meta = { + maintainers = lib.teams.acme.members; + # Hard timeout in seconds. Average run time is about 7 minutes. + timeout = 1800; + }; nodes = { # The fake ACME server which will respond to client requests @@ -357,6 +361,30 @@ in { import time + TOTAL_RETRIES = 20 + + + class BackoffTracker(object): + delay = 1 + increment = 1 + + def handle_fail(self, retries, message) -> int: + assert retries < TOTAL_RETRIES, message + + print(f"Retrying in {self.delay}s, {retries + 1}/{TOTAL_RETRIES}") + time.sleep(self.delay) + + # Only increment after the first try + if retries == 0: + self.delay += self.increment + self.increment *= 2 + + return retries + 1 + + + backoff = BackoffTracker() + + def switch_to(node, name): # On first switch, this will create a symlink to the current system so that we can # quickly switch between derivations @@ -404,9 +432,7 @@ in { assert False - def check_connection(node, domain, retries=3): - assert retries >= 0, f"Failed to connect to https://{domain}" - + def check_connection(node, domain, retries=0): result = node.succeed( "openssl s_client -brief -verify 2 -CAfile /tmp/ca.crt" f" -servername {domain} -connect {domain}:443 < /dev/null 2>&1" @@ -414,13 +440,11 @@ in { for line in result.lower().split("\n"): if "verification" in line and "error" in line: - time.sleep(3) - return check_connection(node, domain, retries - 1) + retries = backoff.handle_fail(retries, f"Failed to connect to https://{domain}") + return check_connection(node, domain, retries) - def check_connection_key_bits(node, domain, bits, retries=3): - assert retries >= 0, f"Did not find expected number of bits ({bits}) in key" - + def check_connection_key_bits(node, domain, bits, retries=0): result = node.succeed( "openssl s_client -CAfile /tmp/ca.crt" f" -servername {domain} -connect {domain}:443 < /dev/null" @@ -429,13 +453,11 @@ in { print("Key type:", result) if bits not in result: - time.sleep(3) - return check_connection_key_bits(node, domain, bits, retries - 1) - + retries = backoff.handle_fail(retries, f"Did not find expected number of bits ({bits}) in key") + return check_connection_key_bits(node, domain, bits, retries) - def check_stapling(node, domain, retries=3): - assert retries >= 0, "OCSP Stapling check failed" + def check_stapling(node, domain, retries=0): # Pebble doesn't provide a full OCSP responder, so just check the URL result = node.succeed( "openssl s_client -CAfile /tmp/ca.crt" @@ -445,21 +467,19 @@ in { print("OCSP Responder URL:", result) if "${caDomain}:4002" not in result.lower(): - time.sleep(3) - return check_stapling(node, domain, retries - 1) - + retries = backoff.handle_fail(retries, "OCSP Stapling check failed") + return check_stapling(node, domain, retries) - def download_ca_certs(node, retries=5): - assert retries >= 0, "Failed to connect to pebble to download root CA certs" + def download_ca_certs(node, retries=0): exit_code, _ = node.execute("curl https://${caDomain}:15000/roots/0 > /tmp/ca.crt") exit_code_2, _ = node.execute( "curl https://${caDomain}:15000/intermediate-keys/0 >> /tmp/ca.crt" ) if exit_code + exit_code_2 > 0: - time.sleep(3) - return download_ca_certs(node, retries - 1) + retries = backoff.handle_fail(retries, "Failed to connect to pebble to download root CA certs") + return download_ca_certs(node, retries) start_all() |