Unverified Commit 06388167 authored by github-actions[bot]'s avatar github-actions[bot] Committed by GitHub
Browse files

Merge master into staging-next

parents d322e62e 50aad914
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -322,7 +322,9 @@ In addition to numerous new and upgraded packages, this release has the followin
  replacement. It stores backups as volume dump files and thus better integrates
  into contemporary backup solutions.

- `services.maddy` now allows to configure users and their credentials using `services.maddy.ensureCredentials`.
- `services.maddy` got several updates:
  - Configuration of users and their credentials using `services.maddy.ensureCredentials`.
  - Configuration of TLS key and certificate files using `services.maddy.tls`.

- The `dnsmasq` service now takes configuration via the
  `services.dnsmasq.settings` attribute set. The option
+80 −3
Original line number Diff line number Diff line
@@ -13,8 +13,6 @@ let
    # configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf
    # Do not use this in production!

    tls off

    auth.pass_table local_authdb {
      table sql_table {
        driver sqlite3
@@ -35,6 +33,7 @@ let
      }
      optional_step file /etc/maddy/aliases
    }

    msgpipeline local_routing {
      destination postmaster $(local_domains) {
        modify {
@@ -215,6 +214,63 @@ in {
        '';
      };

      tls = {
        loader = mkOption {
          type = with types; nullOr (enum [ "file" "off" ]);
          default = "off";
          description = lib.mdDoc ''
            TLS certificates are obtained by modules called "certificate
            loaders". Currently only the file loader is supported which reads
            certificates from files specifying the options `keyPaths` and
            `certPaths`.
          '';
        };

        certificates = mkOption {
          type = with types; listOf (submodule {
            options = {
              keyPath = mkOption {
                type = types.path;
                example = "/etc/ssl/mx1.example.org.key";
                description = lib.mdDoc ''
                  Path to the private key used for TLS.
                '';
              };
              certPath = mkOption {
                type = types.path;
                example = "/etc/ssl/mx1.example.org.crt";
                description = lib.mdDoc ''
                  Path to the certificate used for TLS.
                '';
              };
            };
          });
          default = [];
          example = lib.literalExpression ''
            [{
              keyPath = "/etc/ssl/mx1.example.org.key";
              certPath = "/etc/ssl/mx1.example.org.crt";
            }]
          '';
          description = lib.mdDoc ''
            A list of attribute sets containing paths to TLS certificates and
            keys. Maddy will use SNI if multiple pairs are selected.
          '';
        };

        extraConfig = mkOption {
          type = with types; nullOr lines;
          description = lib.mdDoc ''
            Arguments for the specific certificate loader. Note that Maddy uses
            secure defaults for the TLS configuration so there is no need to
            change anything in most cases.
            See [upstream manual](https://maddy.email/reference/tls/) for
            available options.
          '';
          default = "";
        };
      };

      openFirewall = mkOption {
        type = types.bool;
        default = false;
@@ -224,7 +280,7 @@ in {
      };

      ensureAccounts = mkOption {
        type = types.listOf types.str;
        type = with types; listOf str;
        default = [];
        description = lib.mdDoc ''
          List of IMAP accounts which get automatically created. Note that for
@@ -270,6 +326,16 @@ in {

  config = mkIf cfg.enable {

    assertions = [{
      assertion = cfg.tls.loader == "file" -> cfg.tls.certificates != [];
      message = ''
        If maddy is configured to use TLS, tls.certificates with attribute sets
        of certPath and keyPath must be provided.
        Read more about obtaining TLS certificates here:
        https://maddy.email/tutorials/setting-up/#tls-certificates
      '';
    }];

    systemd = {

      packages = [ pkgs.maddy ];
@@ -318,6 +384,17 @@ in {
        $(primary_domain) = ${cfg.primaryDomain}
        $(local_domains) = ${toString cfg.localDomains}
        hostname ${cfg.hostname}

        ${if (cfg.tls.loader == "file") then ''
          tls file ${concatStringsSep " " (
            map (x: x.certPath + " " + x.keyPath
          ) cfg.tls.certificates)} ${optionalString (cfg.tls.extraConfig != "") ''
            { ${cfg.tls.extraConfig} }
          ''}
        '' else if (cfg.tls.loader == "off") then ''
          tls off
        '' else ""}

        ${cfg.config}
      '';
    };
+1 −1
Original line number Diff line number Diff line
@@ -393,7 +393,7 @@ in {
  lxd-image-server = handleTest ./lxd-image-server.nix {};
  #logstash = handleTest ./logstash.nix {};
  lorri = handleTest ./lorri/default.nix {};
  maddy = handleTest ./maddy.nix {};
  maddy = discoverTests (import ./maddy { inherit handleTest; });
  maestral = handleTest ./maestral.nix {};
  magic-wormhole-mailbox-server = handleTest ./magic-wormhole-mailbox-server.nix {};
  magnetico = handleTest ./magnetico.nix {};
+6 −0
Original line number Diff line number Diff line
{ handleTest }:

{
  unencrypted = handleTest ./unencrypted.nix { };
  tls = handleTest ./tls.nix { };
}
+94 −0
Original line number Diff line number Diff line
import ../make-test-python.nix ({ pkgs, ... }:
let
  certs = import ../common/acme/server/snakeoil-certs.nix;
  domain = certs.domain;
in {
  name = "maddy-tls";
  meta = with pkgs.lib.maintainers; { maintainers = [ onny ]; };

  nodes = {
    server = { options, ... }: {
      services.maddy = {
        enable = true;
        hostname = domain;
        primaryDomain = domain;
        openFirewall = true;
        ensureAccounts = [ "postmaster@${domain}" ];
        ensureCredentials = {
          # Do not use this in production. This will make passwords world-readable
          # in the Nix store
          "postmaster@${domain}".passwordFile = "${pkgs.writeText "postmaster" "test"}";
        };
        tls = {
          loader = "file";
          certificates = [{
            certPath = "${certs.${domain}.cert}";
            keyPath = "${certs.${domain}.key}";
          }];
        };
        # Enable TLS listeners. Configuring this via the module is not yet
        # implemented.
        config = builtins.replaceStrings [
          "imap tcp://0.0.0.0:143"
          "submission tcp://0.0.0.0:587"
        ] [
          "imap tls://0.0.0.0:993 tcp://0.0.0.0:143"
          "submission tls://0.0.0.0:465 tcp://0.0.0.0:587"
        ] options.services.maddy.config.default;
      };
      # Not covered by openFirewall yet
      networking.firewall.allowedTCPPorts = [ 993 465 ];
    };

    client = { nodes, ... }: {
      security.pki.certificateFiles = [
        certs.ca.cert
      ];
      networking.extraHosts = ''
        ${nodes.server.networking.primaryIPAddress} ${domain}
     '';
      environment.systemPackages = [
        (pkgs.writers.writePython3Bin "send-testmail" { } ''
          import smtplib
          import ssl
          from email.mime.text import MIMEText

          context = ssl.create_default_context()
          msg = MIMEText("Hello World")
          msg['Subject'] = 'Test'
          msg['From'] = "postmaster@${domain}"
          msg['To'] = "postmaster@${domain}"
          with smtplib.SMTP_SSL(host='${domain}', port=465, context=context) as smtp:
              smtp.login('postmaster@${domain}', 'test')
              smtp.sendmail(
                'postmaster@${domain}', 'postmaster@${domain}', msg.as_string()
              )
        '')
        (pkgs.writers.writePython3Bin "test-imap" { } ''
          import imaplib

          with imaplib.IMAP4_SSL('${domain}') as imap:
              imap.login('postmaster@${domain}', 'test')
              imap.select()
              status, refs = imap.search(None, 'ALL')
              assert status == 'OK'
              assert len(refs) == 1
              status, msg = imap.fetch(refs[0], 'BODY[TEXT]')
              assert status == 'OK'
              assert msg[0][1].strip() == b"Hello World"
        '')
      ];
    };
  };

  testScript = ''
    start_all()
    server.wait_for_unit("maddy.service")
    server.wait_for_open_port(143)
    server.wait_for_open_port(993)
    server.wait_for_open_port(587)
    server.wait_for_open_port(465)
    client.succeed("send-testmail")
    client.succeed("test-imap")
  '';
})
Loading