1 { config, lib, pkgs, ... }:
3 cfg = config.services.clamsmtp;
4 clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix
10 enable = lib.mkOption {
11 type = lib.types.bool;
13 description = "Whether to enable clamsmtp.";
16 instances = lib.mkOption {
17 description = "Instances of clamsmtp to run.";
18 type = lib.types.listOf (lib.types.submodule { options = {
19 action = lib.mkOption {
20 type = lib.types.enum [ "bounce" "drop" "pass" ];
23 Action to take when a virus is detected.
25 Note that viruses often spoof sender addresses, so bouncing is
26 in most cases not a good idea.
30 header = lib.mkOption {
33 example = "X-Virus-Scanned: ClamAV using ClamSMTP";
35 A header to add to scanned messages. See clamsmtpd.conf(5) for
36 more details. Empty means no header.
40 keepAlives = lib.mkOption {
44 Number of seconds to wait between each NOOP sent to the sending
47 This is meant for slow servers where the sending MTA times out
48 waiting for clamd to scan the file.
52 listen = lib.mkOption {
54 example = "127.0.0.1:10025";
56 Address to wait for incoming SMTP connections on. See
57 clamsmtpd.conf(5) for more details.
61 quarantine = lib.mkOption {
62 type = lib.types.bool;
65 Whether to quarantine files that contain viruses by leaving them
66 in the temporary directory.
70 maxConnections = lib.mkOption {
73 description = "Maximum number of connections to accept at once.";
76 outAddress = lib.mkOption {
79 Address of the SMTP server to send email to once it has been
84 tempDirectory = lib.mkOption {
88 Temporary directory that needs to be accessible to both clamd
93 timeout = lib.mkOption {
96 description = "Time-out for network connections.";
99 transparentProxy = lib.mkOption {
100 type = lib.types.bool;
102 description = "Enable clamsmtp's transparent proxy support.";
105 virusAction = lib.mkOption {
106 type = with lib.types; nullOr path;
109 Command to run when a virus is found. Please see VIRUS ACTION in
110 clamsmtpd(8) for a discussion of this option and its safe use.
114 xClient = lib.mkOption {
115 type = lib.types.bool;
118 Send the XCLIENT command to the receiving server, for forwarding
119 client addresses and connection information if the receiving
120 server supports this feature.
130 configfile = conf: pkgs.writeText "clamsmtpd.conf"
132 Action: ${conf.action}
133 ClamAddress: ${clamdSocket}
134 Header: ${conf.header}
135 KeepAlives: ${toString conf.keepAlives}
136 Listen: ${conf.listen}
137 Quarantine: ${if conf.quarantine then "on" else "off"}
138 MaxConnections: ${toString conf.maxConnections}
139 OutAddress: ${conf.outAddress}
140 TempDirectory: ${conf.tempDirectory}
141 TimeOut: ${toString conf.timeout}
142 TransparentProxy: ${if conf.transparentProxy then "on" else "off"}
144 ${lib.optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"}
145 XClient: ${if conf.xClient then "on" else "off"}
148 lib.mkIf cfg.enable {
150 { assertion = config.services.clamav.daemon.enable;
151 message = "clamsmtp requires clamav to be enabled";
155 systemd.services = lib.listToAttrs (lib.imap1 (i: conf:
156 lib.nameValuePair "clamsmtp-${toString i}" {
157 description = "ClamSMTP instance ${toString i}";
158 wantedBy = [ "multi-user.target" ];
159 script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}";
160 after = [ "clamav-daemon.service" ];
161 requires = [ "clamav-daemon.service" ];
162 serviceConfig.Type = "forking";
163 serviceConfig.PrivateTmp = "yes";
164 unitConfig.JoinsNamespaceOf = "clamav-daemon.service";
169 meta.maintainers = with lib.maintainers; [ ekleog ];