at 23.11-pre 5.8 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.clamsmtp; 6 clamdSocket = "/run/clamav/clamd.ctl"; # See services/security/clamav.nix 7in 8{ 9 ##### interface 10 options = { 11 services.clamsmtp = { 12 enable = mkOption { 13 type = types.bool; 14 default = false; 15 description = lib.mdDoc "Whether to enable clamsmtp."; 16 }; 17 18 instances = mkOption { 19 description = lib.mdDoc "Instances of clamsmtp to run."; 20 type = types.listOf (types.submodule { options = { 21 action = mkOption { 22 type = types.enum [ "bounce" "drop" "pass" ]; 23 default = "drop"; 24 description = 25 lib.mdDoc '' 26 Action to take when a virus is detected. 27 28 Note that viruses often spoof sender addresses, so bouncing is 29 in most cases not a good idea. 30 ''; 31 }; 32 33 header = mkOption { 34 type = types.str; 35 default = ""; 36 example = "X-Virus-Scanned: ClamAV using ClamSMTP"; 37 description = 38 lib.mdDoc '' 39 A header to add to scanned messages. See clamsmtpd.conf(5) for 40 more details. Empty means no header. 41 ''; 42 }; 43 44 keepAlives = mkOption { 45 type = types.int; 46 default = 0; 47 description = 48 lib.mdDoc '' 49 Number of seconds to wait between each NOOP sent to the sending 50 server. 0 to disable. 51 52 This is meant for slow servers where the sending MTA times out 53 waiting for clamd to scan the file. 54 ''; 55 }; 56 57 listen = mkOption { 58 type = types.str; 59 example = "127.0.0.1:10025"; 60 description = 61 lib.mdDoc '' 62 Address to wait for incoming SMTP connections on. See 63 clamsmtpd.conf(5) for more details. 64 ''; 65 }; 66 67 quarantine = mkOption { 68 type = types.bool; 69 default = false; 70 description = 71 lib.mdDoc '' 72 Whether to quarantine files that contain viruses by leaving them 73 in the temporary directory. 74 ''; 75 }; 76 77 maxConnections = mkOption { 78 type = types.int; 79 default = 64; 80 description = lib.mdDoc "Maximum number of connections to accept at once."; 81 }; 82 83 outAddress = mkOption { 84 type = types.str; 85 description = 86 lib.mdDoc '' 87 Address of the SMTP server to send email to once it has been 88 scanned. 89 ''; 90 }; 91 92 tempDirectory = mkOption { 93 type = types.str; 94 default = "/tmp"; 95 description = 96 lib.mdDoc '' 97 Temporary directory that needs to be accessible to both clamd 98 and clamsmtpd. 99 ''; 100 }; 101 102 timeout = mkOption { 103 type = types.int; 104 default = 180; 105 description = lib.mdDoc "Time-out for network connections."; 106 }; 107 108 transparentProxy = mkOption { 109 type = types.bool; 110 default = false; 111 description = lib.mdDoc "Enable clamsmtp's transparent proxy support."; 112 }; 113 114 virusAction = mkOption { 115 type = with types; nullOr path; 116 default = null; 117 description = 118 lib.mdDoc '' 119 Command to run when a virus is found. Please see VIRUS ACTION in 120 clamsmtpd(8) for a discussion of this option and its safe use. 121 ''; 122 }; 123 124 xClient = mkOption { 125 type = types.bool; 126 default = false; 127 description = 128 lib.mdDoc '' 129 Send the XCLIENT command to the receiving server, for forwarding 130 client addresses and connection information if the receiving 131 server supports this feature. 132 ''; 133 }; 134 };}); 135 }; 136 }; 137 }; 138 139 ##### implementation 140 config = let 141 configfile = conf: pkgs.writeText "clamsmtpd.conf" 142 '' 143 Action: ${conf.action} 144 ClamAddress: ${clamdSocket} 145 Header: ${conf.header} 146 KeepAlives: ${toString conf.keepAlives} 147 Listen: ${conf.listen} 148 Quarantine: ${if conf.quarantine then "on" else "off"} 149 MaxConnections: ${toString conf.maxConnections} 150 OutAddress: ${conf.outAddress} 151 TempDirectory: ${conf.tempDirectory} 152 TimeOut: ${toString conf.timeout} 153 TransparentProxy: ${if conf.transparentProxy then "on" else "off"} 154 User: clamav 155 ${optionalString (conf.virusAction != null) "VirusAction: ${conf.virusAction}"} 156 XClient: ${if conf.xClient then "on" else "off"} 157 ''; 158 in 159 mkIf cfg.enable { 160 assertions = [ 161 { assertion = config.services.clamav.daemon.enable; 162 message = "clamsmtp requires clamav to be enabled"; 163 } 164 ]; 165 166 systemd.services = listToAttrs (imap1 (i: conf: 167 nameValuePair "clamsmtp-${toString i}" { 168 description = "ClamSMTP instance ${toString i}"; 169 wantedBy = [ "multi-user.target" ]; 170 script = "exec ${pkgs.clamsmtp}/bin/clamsmtpd -f ${configfile conf}"; 171 after = [ "clamav-daemon.service" ]; 172 requires = [ "clamav-daemon.service" ]; 173 serviceConfig.Type = "forking"; 174 serviceConfig.PrivateTmp = "yes"; 175 unitConfig.JoinsNamespaceOf = "clamav-daemon.service"; 176 } 177 ) cfg.instances); 178 }; 179 180 meta.maintainers = with lib.maintainers; [ ekleog ]; 181}