diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml index 25b3a686c0d9005..7f788be2b0a55e7 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml @@ -607,6 +607,23 @@ binaries, use the p4d package instead. + + + The openssl-extension for the PHP + interpreter used by Nextcloud is built against OpenSSL 1.1 if + is below + 22.11. This is to make sure that people + using + server-side + encryption don’t loose access to their files. + + + In any other case it’s safe to use OpenSSL 3 for PHP’s openssl + extension. This can be done by setting + + to false. + + The coq package and versioned variants diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md index 583480bec02046a..bc7f86bf5526ebe 100644 --- a/nixos/doc/manual/release-notes/rl-2211.section.md +++ b/nixos/doc/manual/release-notes/rl-2211.section.md @@ -196,6 +196,13 @@ Available as [services.patroni](options.html#opt-services.patroni.enable). - The `p4` package now only includes the open-source Perforce Helix Core command-line client and APIs. It no longer installs the unfree Helix Core Server binaries `p4d`, `p4broker`, and `p4p`. To install the Helix Core Server binaries, use the `p4d` package instead. +- The `openssl`-extension for the PHP interpreter used by Nextcloud is built against OpenSSL 1.1 if + [](#opt-system.stateVersion) is below `22.11`. This is to make sure that people using [server-side encryption](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html) + don't loose access to their files. + + In any other case it's safe to use OpenSSL 3 for PHP's openssl extension. This can be done by setting + [](#opt-services.nextcloud.enableBrokenCiphersForSSE) to `false`. + - The `coq` package and versioned variants starting at `coq_8_14` no longer include CoqIDE, which is now available through `coqPackages.coqide`. It is still possible to get CoqIDE as part of diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix index 04599884f139c64..da621573f2a2642 100644 --- a/nixos/modules/services/web-apps/nextcloud.nix +++ b/nixos/modules/services/web-apps/nextcloud.nix @@ -13,7 +13,12 @@ let phpPackage = cfg.phpPackage.buildEnv { extensions = { enabled, all }: (with all; - enabled + # disable default openssl extension + (lib.filter (e: e.pname != "php-openssl") enabled) + # use OpenSSL 1.1 for RC4 Nextcloud encryption if user + # has acknowledged the brokeness of the ciphers (RC4). + # TODO: remove when https://github.com/nextcloud/server/issues/32003 is fixed. + ++ (if cfg.enableBrokenCiphersForSSE then [ cfg.phpPackage.extensions.openssl-legacy ] else [ cfg.phpPackage.extensions.openssl ]) ++ optional cfg.enableImagemagick imagick # Optionally enabled depending on caching settings ++ optional cfg.caching.apcu apcu @@ -80,6 +85,40 @@ in { options.services.nextcloud = { enable = mkEnableOption (lib.mdDoc "nextcloud"); + + enableBrokenCiphersForSSE = mkOption { + type = types.bool; + default = versionOlder stateVersion "22.11"; + defaultText = literalExpression "versionOlder system.stateVersion \"22.11\""; + description = lib.mdDoc '' + This option enables using the OpenSSL PHP extension linked against OpenSSL 1.1 + rather than latest OpenSSL (≥ 3), this is not recommended unless you need + it for server-side encryption (SSE). SSE uses the legacy RC4 cipher which is + considered broken for several years now. See also [RFC7465](https://datatracker.ietf.org/doc/html/rfc7465). + + This cipher has been disabled in OpenSSL ≥ 3 and requires + a specific legacy profile to re-enable it. + + If you deploy Nextcloud using OpenSSL ≥ 3 for PHP and have + server-side encryption configured, you will not be able to access + your files anymore. Enabling this option can restore access to your files. + Upon testing we didn't encounter any data corruption when turning + this on and off again, but this cannot be guaranteed for + each Nextcloud installation. + + It is `true` by default for systems with a [](#opt-system.stateVersion) below + `22.11` to make sure that existing installations won't break on update. On newer + NixOS systems you have to explicitly enable it on your own. + + Please note that this only provides additional value when using + external storage such as S3 since it's not an end-to-end encryption. + If this is not the case, + it is advised to [disable server-side encryption](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption) and set this to `false`. + + In the future, Nextcloud may move to AES-256-GCM, by then, + this option will be removed. + ''; + }; hostName = mkOption { type = types.str; description = lib.mdDoc "FQDN for the nextcloud instance."; @@ -649,6 +688,23 @@ in { ++ (optional (versionOlder cfg.package.version "23") (upgradeWarning 22 "22.05")) ++ (optional (versionOlder cfg.package.version "24") (upgradeWarning 23 "22.05")) ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11")) + ++ (optional cfg.enableBrokenCiphersForSSE '' + You're using PHP's openssl extension built against OpenSSL 1.1 for Nextcloud. + This is only necessary if you're using Nextcloud's server-side encryption. + Please keep in mind that it's using the broken RC4 cipher. + + If you don't use that feature, you can switch to OpenSSL 3 and get + rid of this warning by declaring + + services.nextcloud.enableBrokenCiphersForSSE = false; + + If you need to use server-side encryption you can ignore this waring. + Otherwise you'd have to disable server-side encryption first in order + to be able to safely disable this option and get rid of this warning. + See on how to achieve this. + + For more context, here is the implementing pull request: https://github.com/NixOS/nixpkgs/pull/198470 + '') ++ (optional isUnsupportedMariadb '' You seem to be using MariaDB at an unsupported version (i.e. at least 10.6)! Please note that this isn't supported officially by Nextcloud. You can either diff --git a/nixos/modules/services/web-apps/nextcloud.xml b/nixos/modules/services/web-apps/nextcloud.xml index a0b69dbd606ce57..ca57692fc16a89e 100644 --- a/nixos/modules/services/web-apps/nextcloud.xml +++ b/nixos/modules/services/web-apps/nextcloud.xml @@ -170,6 +170,20 @@ + + + Server-side encryption + + Nextcloud supports server-side encryption (SSE). + This is not an end-to-end encryption, but can be used to encrypt files that will be persisted + to external storage such as S3. Please note that this won't work anymore when using OpenSSL 3 + for PHP's openssl extension because this is implemented using the legacy cipher RC4. + If is above 22.05, + this is disabled by default. To turn it on again and for further information please refer to + . + + + diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix index eb37470a4c7bb9a..a475049e7b26471 100644 --- a/nixos/tests/nextcloud/basic.nix +++ b/nixos/tests/nextcloud/basic.nix @@ -37,6 +37,8 @@ in { "d /var/lib/nextcloud-data 0750 nextcloud nginx - -" ]; + system.stateVersion = "22.11"; # stateVersion >=21.11 to make sure that we use OpenSSL3 + services.nextcloud = { enable = true; datadir = "/var/lib/nextcloud-data"; @@ -99,6 +101,10 @@ in { # This is just to ensure the nextcloud-occ program is working nextcloud.succeed("nextcloud-occ status") nextcloud.succeed("curl -sSf http://nextcloud/login") + # Ensure that no OpenSSL 1.1 is used. + nextcloud.succeed( + "${nodes.nextcloud.services.phpfpm.pools.nextcloud.phpPackage}/bin/php -i | grep 'OpenSSL Library Version' | awk -F'=>' '{ print $2 }' | awk '{ print $2 }' | grep -v 1.1" + ) nextcloud.succeed( "${withRcloneEnv} ${copySharedFile}" ) @@ -108,5 +114,6 @@ in { "${withRcloneEnv} ${diffSharedFile}" ) assert "hi" in client.succeed("cat /mnt/dav/test-shared-file") + nextcloud.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud-data/data/root/files/test-shared-file") ''; })) args diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix index 7dbdff9882387ef..b8d3ba75b51a95c 100644 --- a/nixos/tests/nextcloud/default.nix +++ b/nixos/tests/nextcloud/default.nix @@ -8,6 +8,10 @@ with pkgs.lib; foldl (matrix: ver: matrix // { "basic${toString ver}" = import ./basic.nix { inherit system pkgs; nextcloudVersion = ver; }; + "openssl-sse${toString ver}" = import ./openssl-sse.nix { + inherit system pkgs; + nextcloudVersion = ver; + }; "with-postgresql-and-redis${toString ver}" = import ./with-postgresql-and-redis.nix { inherit system pkgs; nextcloudVersion = ver; diff --git a/nixos/tests/nextcloud/openssl-sse.nix b/nixos/tests/nextcloud/openssl-sse.nix new file mode 100644 index 000000000000000..7595ee2c67e317a --- /dev/null +++ b/nixos/tests/nextcloud/openssl-sse.nix @@ -0,0 +1,105 @@ +args@{ pkgs, nextcloudVersion ? 25, ... }: + +(import ../make-test-python.nix ({ pkgs, ...}: let + adminuser = "root"; + adminpass = "notproduction"; + nextcloudBase = { + networking.firewall.allowedTCPPorts = [ 80 ]; + system.stateVersion = "22.05"; # stateVersions <22.11 use openssl 1.1 by default + services.nextcloud = { + enable = true; + config.adminpassFile = "${pkgs.writeText "adminpass" adminpass}"; + package = pkgs.${"nextcloud" + (toString nextcloudVersion)}; + }; + }; +in { + name = "nextcloud-openssl"; + meta = with pkgs.lib.maintainers; { + maintainers = [ ma27 ]; + }; + nodes.nextcloudwithopenssl1 = { + imports = [ nextcloudBase ]; + services.nextcloud.hostName = "nextcloudwithopenssl1"; + }; + nodes.nextcloudwithopenssl3 = { + imports = [ nextcloudBase ]; + services.nextcloud = { + hostName = "nextcloudwithopenssl3"; + enableBrokenCiphersForSSE = false; + }; + }; + testScript = { nodes, ... }: let + withRcloneEnv = host: pkgs.writeScript "with-rclone-env" '' + #!${pkgs.runtimeShell} + export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav + export RCLONE_CONFIG_NEXTCLOUD_URL="http://${host}/remote.php/webdav/" + export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud" + export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}" + export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})" + "''${@}" + ''; + withRcloneEnv1 = withRcloneEnv "nextcloudwithopenssl1"; + withRcloneEnv3 = withRcloneEnv "nextcloudwithopenssl3"; + copySharedFile1 = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.runtimeShell} + echo 'hi' | ${withRcloneEnv1} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file + ''; + copySharedFile3 = pkgs.writeScript "copy-shared-file" '' + #!${pkgs.runtimeShell} + echo 'bye' | ${withRcloneEnv3} ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file2 + ''; + openssl1-node = nodes.nextcloudwithopenssl1.config.system.build.toplevel; + openssl3-node = nodes.nextcloudwithopenssl3.config.system.build.toplevel; + in '' + nextcloudwithopenssl1.start() + nextcloudwithopenssl1.wait_for_unit("multi-user.target") + nextcloudwithopenssl1.succeed("nextcloud-occ status") + nextcloudwithopenssl1.succeed("curl -sSf http://nextcloudwithopenssl1/login") + + with subtest("With OpenSSL 1 SSE can be enabled and used"): + nextcloudwithopenssl1.succeed("nextcloud-occ app:enable encryption") + nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable") + + with subtest("Upload file and ensure it's encrypted"): + nextcloudwithopenssl1.succeed("${copySharedFile1}") + nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") + nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") + + with subtest("Switch to OpenSSL 3"): + nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test") + nextcloudwithopenssl1.wait_for_open_port(80) + nextcloudwithopenssl1.succeed("nextcloud-occ status") + + with subtest("Existing encrypted files cannot be read, but new files can be added"): + nextcloudwithopenssl1.fail("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file >&2") + nextcloudwithopenssl1.succeed("nextcloud-occ encryption:disable") + nextcloudwithopenssl1.succeed("${copySharedFile3}") + nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2") + nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") + + with subtest("Switch back to OpenSSL 1.1 and ensure that encrypted files are readable again"): + nextcloudwithopenssl1.succeed("${openssl1-node}/bin/switch-to-configuration test") + nextcloudwithopenssl1.wait_for_open_port(80) + nextcloudwithopenssl1.succeed("nextcloud-occ status") + nextcloudwithopenssl1.succeed("nextcloud-occ encryption:enable") + nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") + nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") + nextcloudwithopenssl1.succeed("grep -E '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") + nextcloudwithopenssl1.succeed("grep bye /var/lib/nextcloud/data/root/files/test-shared-file2") + + with subtest("Ensure that everything can be decrypted"): + nextcloudwithopenssl1.succeed("echo y | nextcloud-occ encryption:decrypt-all >&2") + nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") + nextcloudwithopenssl1.succeed("${withRcloneEnv1} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") + nextcloudwithopenssl1.succeed("grep -vE '^HBEGIN:oc_encryption_module' /var/lib/nextcloud/data/root/files/test-shared-file") + + with subtest("Switch to OpenSSL 3 ensure that all files are usable now"): + nextcloudwithopenssl1.succeed("${openssl3-node}/bin/switch-to-configuration test") + nextcloudwithopenssl1.wait_for_open_port(80) + nextcloudwithopenssl1.succeed("nextcloud-occ status") + nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file2 | grep bye") + nextcloudwithopenssl1.succeed("${withRcloneEnv3} ${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file | grep hi") + + nextcloudwithopenssl1.shutdown() + ''; +})) args diff --git a/pkgs/development/interpreters/php/generic.nix b/pkgs/development/interpreters/php/generic.nix index d1b7c6829557897..ae59bf349a9cd6c 100644 --- a/pkgs/development/interpreters/php/generic.nix +++ b/pkgs/development/interpreters/php/generic.nix @@ -91,7 +91,7 @@ let [ ] allExtensionFunctions; - getExtName = ext: lib.removePrefix "php-" (builtins.parseDrvName ext.name).name; + getExtName = ext: ext.extensionName; # Recursively get a list of all internal dependencies # for a list of extensions. diff --git a/pkgs/top-level/php-packages.nix b/pkgs/top-level/php-packages.nix index 0b9f4237327b067..af3bb5451025a6d 100644 --- a/pkgs/top-level/php-packages.nix +++ b/pkgs/top-level/php-packages.nix @@ -71,16 +71,17 @@ lib.makeScope pkgs.newScope (self: with self; { # will mark the extension as a zend extension or not. mkExtension = lib.makeOverridable ({ name - , configureFlags ? [ "--enable-${name}" ] + , configureFlags ? [ "--enable-${extName}" ] , internalDeps ? [ ] , postPhpize ? "" , buildInputs ? [ ] , zendExtension ? false , doCheck ? true + , extName ? name , ... }@args: stdenv.mkDerivation ((builtins.removeAttrs args [ "name" ]) // { pname = "php-${name}"; - extensionName = name; + extensionName = extName; outputs = [ "out" "dev" ]; @@ -103,7 +104,7 @@ lib.makeScope pkgs.newScope (self: with self; { cdToExtensionRootPhase = '' # Go to extension source root. - cd "ext/${name}" + cd "ext/${extName}" ''; preConfigure = '' @@ -139,7 +140,7 @@ lib.makeScope pkgs.newScope (self: with self; { runHook preInstall mkdir -p $out/lib/php/extensions - cp modules/${name}.so $out/lib/php/extensions/${name}.so + cp modules/${extName}.so $out/lib/php/extensions/${extName}.so mkdir -p $dev/include ${rsync}/bin/rsync -r --filter="+ */" \ --filter="+ *.h" \ @@ -414,6 +415,16 @@ lib.makeScope pkgs.newScope (self: with self; { configureFlags = [ "--with-openssl" ]; doCheck = false; } + # This provides a legacy OpenSSL PHP extension + # For situations where OpenSSL 3 do not support a set of features + # without a specific openssl.cnf file + { + name = "openssl-legacy"; + extName = "openssl"; + buildInputs = [ openssl_1_1 ]; + configureFlags = [ "--with-openssl" ]; + doCheck = false; + } { name = "pcntl"; } { name = "pdo"; doCheck = false; } {