Merge pull request #198470 from RaitoBezarius/nc25-openssl

nextcloud25: use openssl 1.1 as a PHP extension to fix RC4 encryption

Changed files
+227 -6
nixos
doc
manual
from_md
release-notes
release-notes
modules
services
tests
pkgs
development
interpreters
top-level
+17
nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
···
</listitem>
<listitem>
<para>
+
The <literal>openssl</literal>-extension for the PHP
+
interpreter used by Nextcloud is built against OpenSSL 1.1 if
+
<xref linkend="opt-system.stateVersion" /> is below
+
<literal>22.11</literal>. This is to make sure that people
+
using
+
<link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side
+
encryption</link> don’t loose access to their files.
+
</para>
+
<para>
+
In any other case it’s safe to use OpenSSL 3 for PHP’s openssl
+
extension. This can be done by setting
+
<xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />
+
to <literal>false</literal>.
+
</para>
+
</listitem>
+
<listitem>
+
<para>
The <literal>coq</literal> package and versioned variants
starting at <literal>coq_8_14</literal> no longer include
CoqIDE, which is now available through
+7
nixos/doc/manual/release-notes/rl-2211.section.md
···
- 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
+57 -1
nixos/modules/services/web-apps/nextcloud.nix
···
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
···
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.";
···
++ (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 <https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption> 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
+14
nixos/modules/services/web-apps/nextcloud.xml
···
</listitem>
</itemizedlist>
</listitem>
+
<listitem>
+
<formalpara>
+
<title>Server-side encryption</title>
+
<para>
+
Nextcloud supports <link xlink:href="https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html">server-side encryption (SSE)</link>.
+
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 <xref linkend="opt-system.stateVersion" /> is <emphasis>above</emphasis> <literal>22.05</literal>,
+
this is disabled by default. To turn it on again and for further information please refer to
+
<xref linkend="opt-services.nextcloud.enableBrokenCiphersForSSE" />.
+
</para>
+
</formalpara>
+
</listitem>
</itemizedlist>
</section>
+7
nixos/tests/nextcloud/basic.nix
···
"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";
···
# 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}"
)
···
"${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
+4
nixos/tests/nextcloud/default.nix
···
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;
+105
nixos/tests/nextcloud/openssl-sse.nix
···
+
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
+1 -1
pkgs/development/interpreters/php/generic.nix
···
[ ]
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.
+15 -4
pkgs/top-level/php-packages.nix
···
# 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" ];
···
cdToExtensionRootPhase = ''
# Go to extension source root.
-
cd "ext/${name}"
+
cd "ext/${extName}"
'';
preConfigure = ''
···
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" \
···
{
name = "openssl";
buildInputs = if (lib.versionAtLeast php.version "8.1") then [ openssl ] else [ openssl_1_1 ];
+
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;
}