at master 7.0 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 hasPrefix 11 literalExpression 12 mkEnableOption 13 mkIf 14 mkMerge 15 mkOption 16 mkPackageOption 17 types 18 ; 19 20 cfg = config.services.postfix-tlspol; 21 22 format = pkgs.formats.yaml_1_2 { }; 23 configFile = format.generate "postfix-tlspol.yaml" cfg.settings; 24in 25 26{ 27 meta.maintainers = pkgs.postfix-tlspol.meta.maintainers; 28 29 options.services.postfix-tlspol = { 30 enable = mkEnableOption "postfix-tlspol"; 31 32 package = mkPackageOption pkgs "postfix-tlspol" { }; 33 34 settings = mkOption { 35 type = types.submodule { 36 freeformType = format.type; 37 options = { 38 server = { 39 address = mkOption { 40 type = types.str; 41 default = "unix:/run/postfix-tlspol/tlspol.sock"; 42 example = "127.0.0.1:8642"; 43 description = '' 44 Path or address/port where postfix-tlspol binds its socket to. 45 ''; 46 }; 47 48 socket-permissions = mkOption { 49 type = types.str; 50 default = "0660"; 51 readOnly = true; 52 description = '' 53 Permissions to the UNIX socket, if configured. 54 55 ::: {.note} 56 Due to hardening on the systemd unit the socket can never be created world readable/writable. 57 ::: 58 ''; 59 apply = value: (builtins.fromTOML "v=0o${value}").v; 60 }; 61 62 log-level = mkOption { 63 type = types.enum [ 64 "debug" 65 "info" 66 "warn" 67 "error" 68 ]; 69 default = "info"; 70 example = "warn"; 71 description = '' 72 Log level 73 ''; 74 }; 75 76 prefetch = mkOption { 77 type = types.bool; 78 default = true; 79 example = false; 80 description = '' 81 Whether to prefetch DNS records when the TTL of a cached record is about to expire. 82 ''; 83 }; 84 85 cache-file = mkOption { 86 type = types.path; 87 default = "/var/cache/postfix-tlspol/cache.db"; 88 readOnly = true; 89 description = '' 90 Path to the cache file. 91 ''; 92 }; 93 }; 94 95 dns = { 96 address = mkOption { 97 type = with types; nullOr str; 98 default = null; 99 example = "127.0.0.1:53"; 100 description = '' 101 IP and port to your DNS resolver. 102 103 Uses resolvers from /etc/resolv.conf if unset. 104 105 ::: {.note} 106 The configured DNS resolver must validate DNSSEC signatures. 107 ::: 108 ''; 109 }; 110 }; 111 }; 112 }; 113 114 default = { }; 115 description = '' 116 The postfix-tlspol configuration file as a Nix attribute set. 117 118 See the reference documentation for possible options. 119 <https://github.com/Zuplu/postfix-tlspol/blob/main/configs/config.default.yaml> 120 ''; 121 }; 122 123 configurePostfix = mkOption { 124 type = types.bool; 125 default = true; 126 description = '' 127 Whether to configure the required settings to use postfix-tlspol in the local Postfix instance. 128 ''; 129 }; 130 }; 131 132 config = mkMerge [ 133 (mkIf (cfg.enable && config.services.postfix.enable && cfg.configurePostfix) { 134 # https://github.com/Zuplu/postfix-tlspol#postfix-configuration 135 services.postfix.settings.main = { 136 smtp_dns_support_level = "dnssec"; 137 smtp_tls_security_level = "dane"; 138 smtp_tls_policy_maps = 139 let 140 address = 141 if (hasPrefix "unix:" cfg.settings.server.address) then 142 cfg.settings.server.address 143 else 144 "inet:${cfg.settings.server.address}"; 145 in 146 [ "socketmap:${address}:QUERYwithTLSRPT" ]; 147 }; 148 149 systemd.services.postfix = { 150 wants = [ "postfix-tlspol.service" ]; 151 after = [ "postfix-tlspol.service" ]; 152 }; 153 154 users.users.postfix.extraGroups = [ "postfix-tlspol" ]; 155 }) 156 157 (mkIf cfg.enable { 158 environment.etc."postfix-tlspol/config.yaml".source = configFile; 159 160 environment.systemPackages = [ cfg.package ]; 161 162 users.users.postfix-tlspol = { 163 isSystemUser = true; 164 group = "postfix-tlspol"; 165 }; 166 users.groups.postfix-tlspol = { }; 167 168 systemd.services.postfix-tlspol = { 169 after = [ 170 "nss-lookup.target" 171 "network-online.target" 172 ]; 173 wants = [ 174 "nss-lookup.target" 175 "network-online.target" 176 ]; 177 wantedBy = [ "multi-user.target" ]; 178 179 description = "Postfix DANE/MTA-STS TLS policy socketmap service"; 180 documentation = [ "https://github.com/Zuplu/postfix-tlspol" ]; 181 182 restartTriggers = [ configFile ]; 183 184 # https://github.com/Zuplu/postfix-tlspol/blob/main/init/postfix-tlspol.service 185 serviceConfig = { 186 ExecStart = toString [ 187 (lib.getExe cfg.package) 188 "-config" 189 "/etc/postfix-tlspol/config.yaml" 190 ]; 191 ExecReload = "${lib.getExe' pkgs.util-linux "kill"} -HUP $MAINPID"; 192 Restart = "always"; 193 RestartSec = 5; 194 195 User = "postfix-tlspol"; 196 Group = "postfix-tlspol"; 197 198 CacheDirectory = "postfix-tlspol"; 199 CapabilityBoundingSet = [ "" ]; 200 LockPersonality = true; 201 MemoryDenyWriteExecute = true; 202 NoNewPrivileges = true; 203 PrivateDevices = true; 204 PrivateTmp = true; 205 PrivateUsers = true; 206 ProcSubset = "pid"; 207 ProtectClock = true; 208 ProtectControlGroups = true; 209 ProtectHome = true; 210 ProtectHostname = true; 211 ProtectKernelLogs = true; 212 ProtectKernelModules = true; 213 ProtectKernelTunables = true; 214 ProtectProc = "invisible"; 215 ProtectSystem = "strict"; 216 ReadOnlyPaths = [ "/etc/postfix-tlspol/config.yaml" ]; 217 RemoveIPC = true; 218 RestrictAddressFamilies = [ 219 "AF_INET" 220 "AF_INET6" 221 ] 222 ++ lib.optionals (lib.hasPrefix "unix:" cfg.settings.server.address) [ 223 "AF_UNIX" 224 ]; 225 RestrictNamespaces = true; 226 RestrictRealtime = true; 227 RestrictSUIDSGID = true; 228 SystemCallArchitectures = "native"; 229 SystemCallFilter = [ 230 "@system-service" 231 "~@privileged @resources" 232 ]; 233 SystemCallErrorNumber = "EPERM"; 234 SecureBits = [ 235 "noroot" 236 "noroot-locked" 237 ]; 238 RuntimeDirectory = "postfix-tlspol"; 239 RuntimeDirectoryMode = "1750"; 240 WorkingDirectory = "/var/cache/postfix-tlspol"; 241 UMask = "0117"; 242 }; 243 }; 244 }) 245 ]; 246}