From cb506e6e2ed9133b43eb0c54699b8df4c9957cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Gaspard?= Date: Sun, 31 Dec 2017 04:09:23 +0100 Subject: [PATCH] nixos/clamsmtp: init --- nixos/modules/module-list.nix | 1 + nixos/modules/services/mail/clamsmtp.nix | 179 +++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 nixos/modules/services/mail/clamsmtp.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d4e4b965a2d..bea7d96ef09 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -260,6 +260,7 @@ ./services/logging/rsyslogd.nix ./services/logging/syslog-ng.nix ./services/logging/syslogd.nix + ./services/mail/clamsmtp.nix ./services/mail/dkimproxy-out.nix ./services/mail/dovecot.nix ./services/mail/dspam.nix diff --git a/nixos/modules/services/mail/clamsmtp.nix b/nixos/modules/services/mail/clamsmtp.nix new file mode 100644 index 00000000000..8f4f39aa728 --- /dev/null +++ b/nixos/modules/services/mail/clamsmtp.nix @@ -0,0 +1,179 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.clamsmtp; + clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix +in +{ + ##### interface + options = { + services.clamsmtp = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable clamsmtp."; + }; + + instances = mkOption { + description = "Instances of clamsmtp to run."; + type = types.listOf (types.submodule { options = { + action = mkOption { + type = types.enum [ "bounce" "drop" "pass" ]; + default = "drop"; + description = + '' + Action to take when a virus is detected. + + Note that viruses often spoof sender addresses, so bouncing is + in most cases not a good idea. + ''; + }; + + header = mkOption { + type = types.str; + default = ""; + example = "X-Virus-Scanned: ClamAV using ClamSMTP"; + description = + '' + A header to add to scanned messages. See clamsmtpd.conf(5) for + more details. Empty means no header. + ''; + }; + + keepAlives = mkOption { + type = types.int; + default = 0; + description = + '' + Number of seconds to wait between each NOOP sent to the sending + server. 0 to disable. + + This is meant for slow servers where the sending MTA times out + waiting for clamd to scan the file. + ''; + }; + + listen = mkOption { + type = types.str; + example = "127.0.0.1:10025"; + description = + '' + Address to wait for incoming SMTP connections on. See + clamsmtpd.conf(5) for more details. + ''; + }; + + quarantine = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to quarantine files that contain viruses by leaving them + in the temporary directory. + ''; + }; + + maxConnections = mkOption { + type = types.int; + default = 64; + description = "Maximum number of connections to accept at once."; + }; + + outAddress = mkOption { + type = types.str; + description = + '' + Address of the SMTP server to send email to once it has been + scanned. + ''; + }; + + tempDirectory = mkOption { + type = types.str; + default = "/tmp"; + description = + '' + Temporary directory that needs to be accessible to both clamd + and clamsmtpd. + ''; + }; + + timeout = mkOption { + type = types.int; + default = 180; + description = "Time-out for network connections."; + }; + + transparentProxy = mkOption { + type = types.bool; + default = false; + description = "Enable clamsmtp's transparent proxy support."; + }; + + virusAction = mkOption { + type = with types; nullOr path; + default = null; + description = + '' + Command to run when a virus is found. Please see VIRUS ACTION in + clamsmtpd(8) for a discussion of this option and its safe use. + ''; + }; + + xClient = mkOption { + type = types.bool; + default = false; + description = + '' + Send the XCLIENT command to the receiving server, for forwarding + client addresses and connection information if the receiving + server supports this feature. + ''; + }; + };}); + }; + }; + }; + + ##### implementation + config = let + configfile = conf: pkgs.writeText "clamsmtpd.conf" + '' + Action: ${conf.action} + ClamAddress: ${clamdSocket} + Header: ${conf.header} + KeepAlives: ${toString conf.keepAlives} + Listen: ${conf.listen} + Quarantine: ${if conf.quarantine then "on" else "off"} + MaxConnections: ${toString conf.maxConnections} + OutAddress: ${conf.outAddress} + TempDirectory: ${conf.tempDirectory} + TimeOut: ${toString conf.timeout} + TransparentProxy: ${if conf.transparentProxy then "on" else "off"} + User: clamav + ${optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"} + XClient: ${if conf.xClient then "on" else "off"} + ''; + in + mkIf cfg.enable { + assertions = [ + { assertion = config.services.clamav.daemon.enable; + message = "clamsmtp requires clamav to be enabled"; + } + ]; + + systemd.services = listToAttrs (imap1 (i: conf: + nameValuePair "clamsmtp-${toString i}" { + description = "ClamSMTP instance ${toString i}"; + wantedBy = [ "multi-user.target" ]; + script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}"; + after = [ "clamav-daemon.service" ]; + requires = [ "clamav-daemon.service" ]; + serviceConfig.Type = "forking"; + serviceConfig.PrivateTmp = "yes"; + unitConfig.JoinsNamespaceOf = "clamav-daemon.service"; + } + ) cfg.instances); + }; +}