From e739c5d78db4ad1be56706cbaa3965aa0857aa24 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Wed, 4 Jun 2025 09:52:04 +0200 Subject: [PATCH] nixos/nextcloud: Allow disabling initial admin user creation on Nextcloud >= 32 With https://github.com/nextcloud/server/pull/53212 it is not longer necessary to specify a username and password for an initial admin account during installation. --- nixos/modules/services/web-apps/nextcloud.nix | 60 +++++++++++++------ nixos/tests/nextcloud/default.nix | 21 ++++--- nixos/tests/nextcloud/without-admin-user.nix | 48 +++++++++++++++ 3 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 nixos/tests/nextcloud/without-admin-user.nix diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index da5e02e27479..995d9a2a8f56 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -644,7 +644,7 @@ in ''; }; adminuser = lib.mkOption { - type = lib.types.str; + type = lib.types.nullOr lib.types.str; default = "root"; description = '' Username for the admin account. The username is only set during the @@ -653,7 +653,7 @@ in ''; }; adminpassFile = lib.mkOption { - type = lib.types.str; + type = lib.types.nullOr lib.types.str; description = '' The full path to a file that contains the admin's password. The password is set only in the initial setup of Nextcloud by the systemd service `nextcloud-setup.service`. @@ -1125,6 +1125,26 @@ in https://docs.nextcloud.com/server/latest/admin_manual/configuration_database/db_conversion.html ''; } + { + assertion = + lib.versionAtLeast overridePackage.version "32.0.0" + || (cfg.config.adminuser != null && cfg.config.adminpassFile != null); + message = '' + Disabling initial admin user creation is only available on Nextcloud >= 32.0.0. + ''; + } + { + assertion = cfg.config.adminuser == null -> cfg.config.adminpassFile == null; + message = '' + If `services.nextcloud.config.adminuser` is null, `services.nextcloud.config.adminpassFile` must be null as well in order to disable initial admin user creation. + ''; + } + { + assertion = cfg.config.adminpassFile == null -> cfg.config.adminuser == null; + message = '' + If `services.nextcloud.config.adminpassFile` is null, `services.nextcloud.config.adminuser` must be null as well in order to disable initial admin user creation. + ''; + } ]; } @@ -1168,10 +1188,14 @@ in arg = "DBPASS"; value = if c.dbpassFile != null then ''"$(<"$CREDENTIALS_DIRECTORY/dbpass")"'' else ''""''; }; - adminpass = { - arg = "ADMINPASS"; - value = ''"$(<"$CREDENTIALS_DIRECTORY/adminpass")"''; - }; + adminpass = + if c.adminpassFile != null then + { + arg = "ADMINPASS"; + value = ''"$(<"$CREDENTIALS_DIRECTORY/adminpass")"''; + } + else + null; installFlags = lib.concatStringsSep " \\\n " ( lib.mapAttrsToList (k: v: "${k} ${toString v}") { "--database" = ''"${c.dbtype}"''; @@ -1182,15 +1206,16 @@ in ${if c.dbhost != null then "--database-host" else null} = ''"${c.dbhost}"''; ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"''; "--database-pass" = "\"\$${dbpass.arg}\""; - "--admin-user" = ''"${c.adminuser}"''; - "--admin-pass" = "\"\$${adminpass.arg}\""; + ${if c.adminuser != null then "--admin-user" else null} = ''"${c.adminuser}"''; + ${if adminpass != null then "--admin-pass" else null} = "\"\$${adminpass.arg}\""; + ${if c.adminuser == null && adminpass == null then "--disable-admin-user" else null} = ""; "--data-dir" = ''"${datadir}/data"''; } ); in '' ${mkExport dbpass} - ${mkExport adminpass} + ${lib.optionalString (adminpass != null) (mkExport adminpass)} ${lib.getExe occ} maintenance:install \ ${installFlags} ''; @@ -1217,10 +1242,12 @@ in exit 1 fi ''} - if [ -z "$(<"$CREDENTIALS_DIRECTORY/adminpass")" ]; then - echo "adminpassFile ${c.adminpassFile} is empty!" - exit 1 - fi + ${lib.optionalString (c.adminpassFile != null) '' + if [ -z "$(<"$CREDENTIALS_DIRECTORY/adminpass")" ]; then + echo "adminpassFile ${c.adminpassFile} is empty!" + exit 1 + fi + ''} # Check if systemd-tmpfiles setup worked correctly if [[ ! -O "${datadir}/config" ]]; then @@ -1262,10 +1289,9 @@ in ''; serviceConfig.Type = "oneshot"; serviceConfig.User = "nextcloud"; - serviceConfig.LoadCredential = [ - "adminpass:${cfg.config.adminpassFile}" - ] - ++ runtimeSystemdCredentials; + serviceConfig.LoadCredential = + lib.optional (cfg.config.adminpassFile != null) "adminpass:${cfg.config.adminpassFile}" + ++ runtimeSystemdCredentials; # On Nextcloud ≥ 26, it is not necessary to patch the database files to prevent # an automatic creation of the database user. environment.NC_setup_create_db_user = "false"; diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix index b69a520bf544..f452a4bf1218 100644 --- a/nixos/tests/nextcloud/default.nix +++ b/nixos/tests/nextcloud/default.nix @@ -30,8 +30,8 @@ let } ]; - adminuser = "root"; - adminpass = "hunter2"; + adminuser = pkgs.lib.mkDefault "root"; + adminpass = pkgs.lib.mkDefault "hunter2"; test-helpers.rclone = "${pkgs.writeShellScript "rclone" '' set -euo pipefail @@ -129,13 +129,16 @@ let } ); in - map callNextcloudTest [ - ./basic.nix - ./with-declarative-redis-and-secrets.nix - ./with-mysql-and-memcached.nix - ./with-postgresql-and-redis.nix - ./with-objectstore.nix - ]; + map callNextcloudTest ( + [ + ./basic.nix + ./with-declarative-redis-and-secrets.nix + ./with-mysql-and-memcached.nix + ./with-postgresql-and-redis.nix + ./with-objectstore.nix + ] + ++ (pkgs.lib.optional (version >= 32) ./without-admin-user.nix) + ); in listToAttrs ( concatMap genTests [ diff --git a/nixos/tests/nextcloud/without-admin-user.nix b/nixos/tests/nextcloud/without-admin-user.nix new file mode 100644 index 000000000000..260d3dfc6fea --- /dev/null +++ b/nixos/tests/nextcloud/without-admin-user.nix @@ -0,0 +1,48 @@ +{ + name, + pkgs, + testBase, + system, + ... +}: + +with import ../../lib/testing-python.nix { inherit system pkgs; }; +runTest ( + { + config, + lib, + ... + }: + rec { + inherit name; + + meta.maintainers = lib.teams.nextcloud.members; + + imports = [ testBase ]; + + nodes = { + nextcloud = + { config, pkgs, ... }: + { + services.nextcloud = { + config = { + dbtype = "sqlite"; + adminuser = null; + adminpassFile = lib.mkForce null; + }; + }; + }; + }; + + adminuser = "root"; + # This needs to be a "secure" password, since the password_policy app is enabled after installation and will forbid "simple" passwords. + adminpass = "+CVpTwaOEktxsFc6"; + + # Manually create the adminuser to make the default set of tests pass. + # If adminuser was already created during the installation this command would not succeed. + # This user name must always match the default value in services.nextcloud.config.adminuser! + test-helpers.init = '' + nextcloud.succeed("OC_PASS=${adminpass} nextcloud-occ user:add ${adminuser} --password-from-env") + ''; + } +)