Unverified Commit 40651935 authored by Maximilian Bosch's avatar Maximilian Bosch Committed by GitHub
Browse files

nixos/nextcloud: Add services.nextcloud.settings.mail_* options (#460529)

parents 0f6e6ece 27183364
Loading
Loading
Loading
Loading
+150 −1
Original line number Diff line number Diff line
@@ -120,7 +120,8 @@ let
    ++ (lib.optional (
      cfg.config.objectstore.s3.sseCKeyFile != null
    ) "s3_sse_c_key:${cfg.config.objectstore.s3.sseCKeyFile}")
    ++ (lib.optional (cfg.secretFile != null) "secret_file:${cfg.secretFile}");
    ++ (lib.optional (cfg.secretFile != null) "secret_file:${cfg.secretFile}")
    ++ (lib.mapAttrsToList (credential: file: "${credential}:${file}") cfg.secrets);

  requiresRuntimeSystemdCredentials = (lib.length runtimeSystemdCredentials) != 0;

@@ -296,6 +297,9 @@ let
        ) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
        ${lib.optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_secret('dbpass'),"}
        'dbtype' => '${c.dbtype}',
        ${lib.concatStringsSep "\n" (
          lib.mapAttrsToList (name: credential: "'${name}' => nix_read_secret('${name}'),") cfg.secrets
        )}
        ${objectstoreConfig}
      ];

@@ -390,6 +394,24 @@ in
      '';
      example = "/mnt/nextcloud-file";
    };
    secrets = lib.mkOption {
      type = lib.types.attrsOf (
        lib.types.pathWith {
          inStore = false;
          absolute = true;
        }
      );
      default = { };
      description = ''
        Secret files to read into entries in `config.php`.
        This uses `nix_read_secret` and LoadCredential to read the contents of the file into the entry in `config.php`.
      '';
      example = lib.literalExpression ''
        {
          oidc_login_client_secret = "/run/secrets/nextcloud_oidc_secret";
        }
      '';
    };
    extraApps = lib.mkOption {
      type = lib.types.attrsOf lib.types.package;
      default = { };
@@ -975,6 +997,126 @@ in
              The preview providers that should be explicitly enabled.
            '';
          };
          mail_domain = lib.mkOption {
            type = lib.types.nullOr lib.types.str;
            default = null;
            description = ''
              The return address that you want to appear on emails sent by the Nextcloud server, for example `nc-admin@example.com`, substituting your own domain, of course.
            '';
          };
          mail_from_address = lib.mkOption {
            type = lib.types.nullOr lib.types.str;
            default = null;
            description = ''
              FROM address that overrides the built-in `sharing-noreply` and `lostpassword-noreply` FROM addresses.
              Defaults to different FROM addresses depending on the feature.
            '';
          };
          mail_smtpdebug = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              Enable SMTP class debugging.
              `loglevel` will likely need to be adjusted too.
              [See docs](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/email_configuration.html#enabling-debug-mode).
            '';
          };
          mail_smtpmode = lib.mkOption {
            type = lib.types.enum [
              "sendmail"
              "smtp"
              "qmail"
              "null" # Yes, this is really a string null and not null.
            ];
            default = "smtp";
            description = ''
              Which mode to use for sending mail.
              If you are using local or remote SMTP, set this to `smtp`.
              For the `sendmail` option, you need an installed and working email system on the server, with your local `sendmail` installation.
              For `qmail`, the binary is /var/qmail/bin/sendmail, and it must be installed on your Unix system.
              Use the string null to send no mails (disable mail delivery). This can be useful if mails should be sent via APIs and rendering messages is not necessary.
            '';
          };
          mail_smtphost = lib.mkOption {
            type = lib.types.str;
            default = "127.0.0.1";
            description = ''
              This depends on `mail_smtpmode`. Specify the IP address of your mail server host. This may contain multiple hosts separated by a semicolon. If you need to specify the port number, append it to the IP address separated by a colon, like this: `127.0.0.1:24`.
            '';
          };
          mail_smtpport = lib.mkOption {
            type = lib.types.port;
            default = 25;
            description = ''
              This depends on `mail_smtpmode`. Specify the port for sending mail.
            '';
          };
          mail_smtptimeout = lib.mkOption {
            type = lib.types.int;
            default = 10;
            description = ''
              This depends on `mail_smtpmode`. This sets the SMTP server timeout, in seconds. You may need to increase this if you are running an anti-malware or spam scanner.
            '';
          };
          mail_smtpsecure = lib.mkOption {
            type = lib.types.enum [
              ""
              "ssl"
            ];
            default = "";
            description = ''
              This depends on `mail_smtpmode`. Specify `ssl` when you are using SSL/TLS. Any other value will be ignored.
              If the server advertises STARTTLS capabilities, they might be used, but they cannot be enforced by this config option.
            '';
          };
          mail_smtpauth = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              This depends on `mail_smtpmode`. Change this to `true` if your mail server requires authentication.
            '';
          };
          mail_smtpname = lib.mkOption {
            type = lib.types.str;
            default = "";
            description = ''
              This depends on `mail_smtpauth`. Specify the username for authenticating to the SMTP server.
            '';
          };
          # mail_smtppassword is skipped as it must be set through services.nextcloud.secrets
          mail_template_class = lib.mkOption {
            type = lib.types.str;
            default = "\\OC\\Mail\\EMailTemplate";
            description = ''
              Replaces the default mail template layout. This can be utilized if the options to modify the mail texts with the theming app are not enough.
              The class must extend `\OC\Mail\EMailTemplate`
            '';
          };
          mail_send_plaintext_only = lib.mkOption {
            type = lib.types.bool;
            default = false;
            description = ''
              Email will be sent by default with an HTML and a plain text body. This option allows sending only plain text emails.
            '';
          };
          mail_smtpstreamoptions = lib.mkOption {
            type = lib.types.listOf lib.types.str;
            default = [ ];
            description = ''
              This depends on `mail_smtpmode`. Array of additional streams options that will be passed to underlying Swift mailer implementation.
            '';
          };
          mail_sendmailmode = lib.mkOption {
            type = lib.types.enum [
              "smtp"
              "pipe"
            ];
            default = "smtp";
            description = ''
              For `smtp`, the sendmail binary is started with the parameter `-bs`: Use the SMTP protocol on standard input and output.
              For `pipe`, the binary is started with the parameters `-t`: Read message from STDIN and extract recipients.
            '';
          };
        };
      };
      default = { };
@@ -1165,6 +1307,13 @@ in
              If `services.nextcloud.config.adminpassFile` is null, `services.nextcloud.config.adminuser` must be null as well in order to disable initial admin user creation.
            '';
          }
          {
            assertion = !(cfg.settings ? mail_smtppassword);
            message = ''
              The option `services.nextcloud.settings.mail_smtppassword` must not be used, as it puts the password into the world-readable nix store.
              Use `services.nextcloud.secrets.mail_smtppassword` instead and set it to a file containing the password.
            '';
          }
        ];
      }

+6 −0
Original line number Diff line number Diff line
@@ -63,8 +63,11 @@ runTest (
            };
            phpExtraExtensions = all: [ all.bz2 ];
            nginx.enableFastcgiRequestBuffering = true;
            secrets.mysecret = "/etc/nextcloud/mysecretfile";
          };

          environment.etc."nextcloud/mysecretfile".text = "foobar";

          specialisation.withoutMagick.configuration = {
            services.nextcloud.enableImagemagick = false;
          };
@@ -116,6 +119,9 @@ runTest (
            client_hash = client.succeed("nix-hash testfile.bin").strip()
            nextcloud_hash = nextcloud.succeed("nix-hash /var/lib/nextcloud-data/data/root/files/testfile.bin").strip()
            t.assertEqual(client_hash, nextcloud_hash)

        with subtest("secrets"):
            assert "foobar" == nextcloud.succeed("nextcloud-occ config:system:get mysecret").strip()
      '';
  }
)
+2 −2
Original line number Diff line number Diff line
@@ -75,8 +75,7 @@ let
          inherit (config) test-helpers;
        in
        mkBefore ''
          nextcloud.start()
          client.start()
          start_all()
          nextcloud.wait_for_unit("multi-user.target")

          ${test-helpers.init}
@@ -136,6 +135,7 @@ let
        ./with-mysql-and-memcached.nix
        ./with-postgresql-and-redis.nix
        ./with-objectstore.nix
        ./with-mail.nix
      ]
      ++ (pkgs.lib.optional (version >= 32) ./without-admin-user.nix)
    );
+100 −0
Original line number Diff line number Diff line
{
  name,
  pkgs,
  testBase,
  system,
  ...
}:
with import ../../lib/testing-python.nix { inherit system pkgs; };
runTest (
  { config, lib, ... }:
  let
    certs = import ../common/acme/server/snakeoil-certs.nix;
    domain = certs.domain;
  in
  {
    inherit name;

    meta.maintainers = lib.teams.nextcloud.members;

    imports = [ testBase ];

    nodes = {
      nextcloud =
        {
          config,
          pkgs,
          nodes,
          ...
        }:
        {
          security.pki.certificateFiles = [ certs.ca.cert ];

          networking.extraHosts = ''
            ${nodes.stalwart.networking.primaryIPAddress} ${domain}
          '';

          environment.etc."nextcloud/mail_smtppassword".text = "foobar";

          services.nextcloud = {
            config.dbtype = "sqlite";

            settings = {
              mail_from_address = "alice";
              mail_domain = domain;
              mail_smtpmode = "smtp";
              mail_smtphost = domain;
              mail_smtpport = 587;
              mail_smtpauth = true;
              mail_smtpname = "alice";
              mail_send_plaintext_only = true;
            };

            secrets.mail_smtppassword = "/etc/nextcloud/mail_smtppassword";
          };
        };

      stalwart =
        { pkgs, ... }:
        {
          imports = [ ../stalwart/stalwart-mail-config.nix ];

          networking.firewall.allowedTCPPorts = [ 587 ];

          environment.systemPackages = [
            (pkgs.writers.writePython3Bin "test-imap-read" { } ''
              from imaplib import IMAP4

              with IMAP4('localhost') as imap:
                  imap.starttls()
                  status, [caps] = imap.login('bob', 'foobar')
                  assert status == 'OK'
                  imap.select()
                  status, [ref] = imap.search(None, 'ALL')
                  assert status == 'OK'
                  [msgId] = ref.split()
                  status, msg = imap.fetch(msgId, 'BODY[TEXT]')
                  assert status == 'OK'
                  assert (msg[0][1].strip()
                          == (b'Well done, ${config.adminuser}!\r\n\r\n'
                              b'If you received this email, the email configuration '
                              b's=\r\neems to be correct.\r\n\r\n\r\n--=20\r\n'
                              b'Nextcloud - a safe home for all your data=\r\n\r\n'
                              b'This is an automatically sent email, please do not reply.'))
            '')
          ];
        };
    };

    test-helpers.init = ''
      stalwart.wait_for_unit("multi-user.target")
      stalwart.wait_until_succeeds("nc -vzw 2 localhost 587")

      nextcloud.succeed("nc -vzw 2 ${domain} 587")
      nextcloud.succeed("curl -sS --fail-with-body -u ${config.adminuser}:${config.adminpass} -H 'OCS-APIRequest: true' -X PUT http://nextcloud/ocs/v2.php/cloud/users/${config.adminuser} -H 'Content-Type: application/json' --data-raw '{\"key\":\"email\",\"value\":\"bob@${domain}\"}'")
      nextcloud.succeed("curl -sS --fail-with-body -u ${config.adminuser}:${config.adminpass} -H 'OCS-APIRequest: true' -X POST http://nextcloud/settings/admin/mailtest")

      stalwart.succeed("test-imap-read")
    '';
  }
)
+0 −1
Original line number Diff line number Diff line
@@ -111,7 +111,6 @@ runTest (
    };

    test-helpers.init = ''
      minio.start()
      minio.wait_for_open_port(9000)
      minio.wait_for_unit("nginx.service")
      minio.wait_for_open_port(443)