182 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { 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);
 | |
|     };
 | |
| 
 | |
|   meta.maintainers = with lib.maintainers; [ ekleog ];
 | |
| }
 | 
