• Operand
  • can sell console.

gram:build

> ./nixos/module/maddy.nix

Lenses
(coming soon!)


# Based on:
# https://wiki.nixos.org/wiki/Maddy
# https://wiki.nixos.org/wiki/ACME

{ ipv4, ipv6, domain }:
{ options, config, pkgs, lib, ... }:
{
  networking.firewall.allowedTCPPorts = [ 80 443 53 465 993 ];

    # "due.social"
    # "operand.online"
    # "assembled.app"
    # "opendemocracy.group"

  security.acme = {
    acceptTerms = true;
    defaults.email = "mail@assembled.app";
  #   certs."${domain}" = {
  #     group = config.services.maddy.group;
  #     dnsProvider = "namedotcom";
  #     environmentFile = pkgs.writeText "name-com-creds" ''
  #       NAMECOM_USERNAME=c_lliope
  #       NAMECOM_API_TOKEN=faf580aa639bb52c84d353d64b7d756fe525d58e
  #     '';
  #   };
  };

  services.maddy = {
    enable = true;
    openFirewall = true;
    primaryDomain = domain;
    hostname = domain;
    # group = "nginx";

    ensureAccounts = [
      "message@due.social"
      "mail@due.social"
      "calliope@operand.online"
      "mail@operand.online"
      "calliope@assembled.app"
      "mail@assembled.app"
      "commercial@assembled.app"
      "bill@assembled.app"
      "code@assembled.app"
      "g@assembled.app"
      "domains@assembled.app"
      "calliope@opendemocracy.group"
      "mail@opendemocracy.group"
    ];

    ensureCredentials = {
      "message@due.social".passwordFile = (pkgs.writeText "checkcheck" "abcd1234");
      "mail@due.social".passwordFile = (pkgs.writeText "checkcheck" "abcd1234");
      "mail@operand.online".passwordFile = (pkgs.writeText "checkcheck" "abcd1234");
    };

    tls = {
      loader = "acme";
      extraConfig = ''
      email calli.youngblood@gmail.com
      agreed
      host ${domain}
      # challenge tls-alpn-01
      # challenge http-01
      challenge dns-01
      dns namedotcom {
        user c_lliope
        token faf580aa639bb52c84d353d64b7d756fe525d58e
      }
      '';
    };

    # tls = { loader = "file"; certificates = [ {
    #   keyPath = "/var/lib/acme/${domain}/key.pem";
    #   certPath = "/var/lib/acme/${domain}/cert.pem";
    # } ]; };

    # Enable TLS listeners. Configuring this via the module is not yet
    # implemented, see https://github.com/NixOS/nixpkgs/pull/153372
    config = builtins.replaceStrings [
      "imap tcp://0.0.0.0:143"
      "submission tcp://0.0.0.0:587"
      "msgpipeline local_routing {"
      "min_tls_level encrypted"
      "dmarc yes"
      "optional_step file /etc/maddy/aliases"
    ] [
      "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"

      ''msgpipeline local_routing {
        check {
          rspamd {
            api_path http://localhost:11334
          }
        }''

      "min_tls_level none"
      ''dmarc yes
       max_message_size 64M''
      "optional_step static {
         entry mailA@due.social mailB@due.social
       }"
    ] options.services.maddy.config.default;
  };

  # services.nsd = {
  #   enable = true;
  #   interfaces = [ ipv4 ipv6 ]; # [ "0.0.0.0" "::" 
  #   zones."${domain}.".data = let
  #   # domain key is produced by maddy, in:
  #   # /var/lib/maddy/dkim_keys/${domain}_default.dns
  #     domainkey = ''v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr+RvDNgsbyjbvl/VprKxwrZPqDJIYnjIvA9wvp08hCLOXq1MDf20Uo6GKpCF5mJ+l02soVuUiewrSk0pG9P3RkzBw/fwBkKcFLsClQBLtFxaSDFrN3tUmKFt4Epi25VVHZeiAzPUEbHAzVqzK9k4h63XljjMMY/n+HknI4A0VXudBlOCRDW7xzmxubkpQMz0EJxyq1RKPBsS5jtOZVuLeQrRXmCLZrpv7+AAKgJiIXb0kAZHsHHSecxYv7/i8PUVtLPlQovdelALP5NchkUqecJdfTFX9Vz+0BsB+8Zc4ZV3EB7IARcVHTNpU0jsbBAIJx8De+RIEc0LToPjKC+8pwIDAQAB'';
  #   segments = ((pkgs.lib.stringLength domainkey) / 255);
  #   dk_prepared = map (x: pkgs.lib.substring (x*255) 255 domainkey) (pkgs.lib.range 0 segments);
  #   in ''
  #     @ SOA ns.${domain} noc.${domain} 666 7200 3600 1209600 3600
  #     @ A ${ipv4}
  #     @ AAAA ${ipv6}
  #     * A ${ipv4}
  #     * AAAA ${ipv6}
  #     @ MX 10 @
  #     @ A ${ipv4}
  #     @ AAAA ${ipv6}
  #     @ TXT "v=spf1 mx ~all"
  #     _dmarc TXT "v=DMARC1; p=quarantine; ruf=mailto:mail@${domain}
  #     _mta-sts TXT "v=STSv1; id=1"
  #     _smtp._tls TXT "v=TLSRPTv1;rua=mailto:mail@${domain}"
  #     _autodiscover._tcp SRV 0 0 443 autoconfig
  #     _25._tcp.${domain}. TLSA 3 1 1 8440c25ffc0eb3270e6467a0a4309859eb9e5771b3ae83704e3d2ce721c943d1
  #     default._domainkey TXT "${lib.concatStringsSep "\" \"" dk_prepared}"
  #   '';
  # };

  services.roundcube = {
    enable = true; configureNginx = true;
    hostName = "${domain}";
    extraConfig = ''
    $config['smtp_server'] = "tls://${config.services.maddy.primaryDomain}";
    $config['smtp_user'] = "%u";
    $config['smtp_pass'] = "%p";
    '';
  };

  # services.nginx.enable = true;
  # users.users.nginx.extraGroups = [ "maddy" ];
  # services.nginx.virtualHosts."${domain}" = { forceSSL = true; enableACME = true; };

  services.go-autoconfig = {
    enable = true;
    settings = {
      service_addr = ":1323";
      domain = "map.${domain}";
      imap = { server = "${domain}"; port = 993; };
      smtp = { server = "${domain}"; port = 587; };
    };
  };

  services.rspamd = {
    enable = true;
    locals."dkim_signing.conf".text = ''
      selector = "default";
      domain = "project-insanity.org";
      path = "/var/lib/maddy/dkim_keys/$domain_$selector.key";
    '';
  };

  systemd.services.rspamd.serviceConfig.SupplementaryGroups = [ "maddy" ];

  services.caddy = {
    enable = true;
    virtualHosts."mta-sts.${domain}".extraConfig = ''
      encode gzip
      file_server
      root * ${
        pkgs.runCommand "testdir" {} ''
          mkdir -p "$out/.well-known"
          echo "
          version: STSv1
          mode: enforce
          max_age: 604800
          mx: ${domain}
          " > "$out/.well-known/mta-sts.txt"
          ''
      }
    '';
    virtualHosts."map.${domain}".extraConfig = ''
      reverse_proxy http://localhost:1323
    '';
    virtualHosts."${domain}".extraConfig = ''
      reverse_proxy http://localhost:80
      tls "/var/lib/acme/${domain}/cert.pem" "/var/lib/acme/${domain}/key.pem"
    '';
  };
}

# Of course autoconfig.${domain} domain should point to your server running the SSL enabled web service.

# TLS it not available or unauthenticated but required
# This error occurs if the receiving mail server has a invalid or none TLS
# configuration. The default configuration of Maddy enforces a valid TLS
# connection to the remote server for delivery. If you want to disable this
# default policy, apply following configuration hack

# Maddy is a composable, modern mail server written in Go. It includes
# everything required to manage users, inboxes, send and receive mails while
# supporting all important secure protocols and standards.

# The following example enables the Maddy mail server on localhost, listening
# on mail delivery SMTP/Submission ports (25, 587) and IMAP port (143) for mail
# clients to connect to. Mailboxes for the accounts mail@due.social and
# user1@due.social get created if they don't exist yet.

# This local test setup doesn't provide secure TLS connections and should be
# used only for testing purpose.

# The following example changes the hostname for the mail server to the public
# domain due.social. TLS certificates are obtained using using the ACME dns-01
# challenge. This requires API access to your domain provider. See upstream
# documentation for a list on supported providers and how to configure them.

# Further the TLS connection is enabled on IMAP port 993 and Submission port 465.

# Alternativley certificates can be manually loaded with setting tls.loader =
# "file"; and manually specifiying key and certificates file paths using the
# tls.certificates = []; option. In this case, more ACME protocols and
# providers are available when using the native NixOS ACME module or manual
# client tools like Certbot.

# DNS records: It is possibly easier to configure our own authoritative-only
# DNS server, which provides important setup information to other mail servers
# and clients. For details about the meaning of the specific DNS records or
# manual setup instructions see the Maddy setup tutorial.

# Update the IPv4 and IPv6 addresses after A and AAAA to the one which points
# to the publc IP addresses of your mail server. The last entry is used by the
# DKIM authentication mechanism which enables recipients to verify the
# authenticity of mails send by your server. They key is read from the file
# generated by Maddy on the first startup at
# /var/lib/maddy/dkim_keys/${domain}_default.dns and spitted in segments of
# 255 chars length to fulfill the DNS record requirements.
#
# Now that your server also runs a DNS daemon besides the mail server, you have
# to configure it as the external nameserver of your domain ${domain}. Please
# consult your domain provider on how to do that.

# rDNS: It is important that the public facing IP of your mail server resolves
# to the MX domain domain. This is something you would normally configure on your
# server provider site. You can check if it's resolving correctly by running
# this command

# # nix shell nixpkgs#bind --command dig -x 172.233.225.32
# 172.233.225.32.in-addr.arpa. 6244	IN	PTR	${domain}.

# MTA-STS enforces secure TLS configuration for servers which support this
# standard. We already advertised this feature in the DNS records above, but we
# also have to serve a static configuration file using a web server. We use the
# web server Caddy to do this but of course you can other Web Servers too.

# Replace the domain mta-sts.${domain} and the domain ${domain} with
# the ones you're using.

# Using a TLSA (DANE) record is recommended to bind TLS-certificates to a
# server. Your nameserver needs DNSSEC support for it. You can generate the key
# using following command

# # nsh hash-slinger tlsa  --create --selector 1 --protocol tcp -p 25 --create ${domain}

# Add the key to a new TLSA record in your nameserver

# To verify if the record is set correctly
#
# # nix shell nixpkgs#dnsutils --command dig _25._tcp.${domain} TLSA +short
# 3 1 1 7f59d873a70e224b184c95a4eb54caa9621e47d48b4a25d312d83d96 e3498238
#
# Check if DNSSEC is working correctly for your new TLSA record

# # nix shell nixpkgs#dnsutils --command delv _25._tcp.${domain} TLSA @1.1.1.1
# ; fully validated
# _25._tcp.${domain}. 10800 IN TLSA 3 1 1 7f59d873a70e224b184c95a4eb54caa9621e47d48b4a25d312d83d96 e3498238
# _25._tcp.${domain}. 10800 IN RRSIG	TLSA 13 5 10800 20230601000000 20230511000000 39688 ${domain}. He9VYZ35xTC3fNo8GJa6swPrZodSnjjIWPG6Th2YbsOEKTV1E8eGtJ2A +eyBd9jgG+B3cA/jw8EJHmpvy/buCw==

# To verify that the TLSA record matches the TLS certificate of the mail
# server, issue following openssl command
#
# # openssl s_client -connect ${domain}:25 -starttls smtp -dane_tlsa_domain ${domain} -dane_tlsa_rrdata "3 1 1 7f59d873a70e224b184c95a4eb54caa9621e47d48b4a25d312d83d96"
# Verify return code: 0 (ok)
# Replace the hostnames and the TLSA hash according to your configuration.

# Spam filtering: You can enable and use rspamd spam filtering daemon like this;
# The second part in this example replaces a part in the default config of the
# Maddy module and inserts the rspamd check to the message pipeline as
# described in the upstream documentation.
# The rspamd article also has some notes on how to achieve training for
# spam/ham mails using an additional helper script.

# Mail attachement size: The default max mail attachement size is set to 32MB,
# for a higher value (in this case 64MB) change the default configuration via
# this workaround

# Alias addresses: The following example will add an alias mailA@${domain} for the local mail
# address mailB@${domain} meaning that every mail send to mailA will get
# delivered to mailB.

# Test mail server:
# You can use several online tools to test your mail server configuration:
#
# - en.internet.nl/test-mail: Test your mail server configuration for validity and security.
# - mail-tester.com: Send a mail to this service and get a rating about the "spaminess" of your mail server.
#     Send a mail to the echo server echo@univie.ac.at. You should receive a response containing your message in several seconds.

# Autoconfig: Since Maddy does not support this feature yet, you can run an
# additional web service which provides autoconfig or autodiscover files for
# various mail clients like Thunderbird, iOS Mail or Outlook, so you don't have
# to manually configure your server settings into these apps. In this example,
# we're going to tell the clients, that our mail server is running on the
# domain ${domain} and which IMAP/SMTP ports to use

# Users and inboxes: Creating credentials and inboxes for a specific account.
# The first command creates the user and will prompt for a password.
# # maddyctl creds create mail@${domain}
# # maddyctl imap-acct create mail@${domain}
# # maddyctl creds password mail@${domain}