at 25.11-pre 5.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 cfg = config.services.opendkim; 10 11 defaultSock = "local:/run/opendkim/opendkim.sock"; 12 13 args = 14 [ 15 "-f" 16 "-l" 17 "-p" 18 cfg.socket 19 "-d" 20 cfg.domains 21 "-k" 22 "${cfg.keyPath}/${cfg.selector}.private" 23 "-s" 24 cfg.selector 25 ] 26 ++ lib.optionals (cfg.configFile != null) [ 27 "-x" 28 cfg.configFile 29 ]; 30 31 configFile = pkgs.writeText "opendkim.conf" ( 32 lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") cfg.settings) 33 ); 34in 35{ 36 imports = [ 37 (lib.mkRenamedOptionModule [ "services" "opendkim" "keyFile" ] [ "services" "opendkim" "keyPath" ]) 38 ]; 39 40 options = { 41 services.opendkim = { 42 enable = lib.mkEnableOption "OpenDKIM sender authentication system"; 43 44 socket = lib.mkOption { 45 type = lib.types.str; 46 default = defaultSock; 47 description = "Socket which is used for communication with OpenDKIM."; 48 }; 49 50 user = lib.mkOption { 51 type = lib.types.str; 52 default = "opendkim"; 53 description = "User for the daemon."; 54 }; 55 56 group = lib.mkOption { 57 type = lib.types.str; 58 default = "opendkim"; 59 description = "Group for the daemon."; 60 }; 61 62 domains = lib.mkOption { 63 type = lib.types.str; 64 default = "csl:${config.networking.hostName}"; 65 defaultText = lib.literalExpression ''"csl:''${config.networking.hostName}"''; 66 example = "csl:example.com,mydomain.net"; 67 description = '' 68 Local domains set (see {manpage}`opendkim(8)` for more information on datasets). 69 Messages from them are signed, not verified. 70 ''; 71 }; 72 73 keyPath = lib.mkOption { 74 type = lib.types.path; 75 description = '' 76 The path that opendkim should put its generated private keys into. 77 The DNS settings will be found in this directory with the name selector.txt. 78 ''; 79 default = "/var/lib/opendkim/keys"; 80 }; 81 82 selector = lib.mkOption { 83 type = lib.types.str; 84 description = "Selector to use when signing."; 85 }; 86 87 # TODO: deprecate this? 88 configFile = lib.mkOption { 89 type = lib.types.nullOr lib.types.path; 90 default = null; 91 description = "Additional opendkim configuration as a file."; 92 }; 93 94 settings = lib.mkOption { 95 type = 96 with lib.types; 97 submodule { 98 freeformType = attrsOf str; 99 }; 100 default = { }; 101 description = "Additional opendkim configuration"; 102 }; 103 }; 104 }; 105 106 config = lib.mkIf cfg.enable { 107 users.users = lib.optionalAttrs (cfg.user == "opendkim") { 108 opendkim = { 109 group = cfg.group; 110 uid = config.ids.uids.opendkim; 111 }; 112 }; 113 114 users.groups = lib.optionalAttrs (cfg.group == "opendkim") { 115 opendkim.gid = config.ids.gids.opendkim; 116 }; 117 118 environment = { 119 etc = lib.mkIf (cfg.settings != { }) { 120 "opendkim/opendkim.conf".source = configFile; 121 }; 122 systemPackages = [ pkgs.opendkim ]; 123 }; 124 125 services.opendkim.configFile = lib.mkIf (cfg.settings != { }) configFile; 126 127 systemd.tmpfiles.rules = [ 128 "d '${cfg.keyPath}' - ${cfg.user} ${cfg.group} - -" 129 ]; 130 131 systemd.services.opendkim = { 132 description = "OpenDKIM signing and verification daemon"; 133 after = [ "network.target" ]; 134 wantedBy = [ "multi-user.target" ]; 135 136 preStart = '' 137 cd "${cfg.keyPath}" 138 if ! test -f ${cfg.selector}.private; then 139 ${pkgs.opendkim}/bin/opendkim-genkey -s ${cfg.selector} -d all-domains-generic-key 140 echo "Generated OpenDKIM key! Please update your DNS settings:\n" 141 echo "-------------------------------------------------------------" 142 cat ${cfg.selector}.txt 143 echo "-------------------------------------------------------------" 144 fi 145 ''; 146 147 serviceConfig = { 148 ExecStart = "${pkgs.opendkim}/bin/opendkim ${lib.escapeShellArgs args}"; 149 User = cfg.user; 150 Group = cfg.group; 151 RuntimeDirectory = lib.optional (cfg.socket == defaultSock) "opendkim"; 152 StateDirectory = "opendkim"; 153 StateDirectoryMode = "0700"; 154 ReadWritePaths = [ cfg.keyPath ]; 155 156 AmbientCapabilities = [ ]; 157 CapabilityBoundingSet = ""; 158 DevicePolicy = "closed"; 159 LockPersonality = true; 160 MemoryDenyWriteExecute = true; 161 NoNewPrivileges = true; 162 PrivateDevices = true; 163 PrivateMounts = true; 164 PrivateTmp = true; 165 PrivateUsers = true; 166 ProtectClock = true; 167 ProtectControlGroups = true; 168 ProtectHome = true; 169 ProtectHostname = true; 170 ProtectKernelLogs = true; 171 ProtectKernelModules = true; 172 ProtectKernelTunables = true; 173 ProtectSystem = "strict"; 174 RemoveIPC = true; 175 RestrictAddressFamilies = [ 176 "AF_INET" 177 "AF_INET6 AF_UNIX" 178 ]; 179 RestrictNamespaces = true; 180 RestrictRealtime = true; 181 RestrictSUIDSGID = true; 182 SystemCallArchitectures = "native"; 183 SystemCallFilter = [ 184 "@system-service" 185 "~@privileged @resources" 186 ]; 187 UMask = "0077"; 188 }; 189 }; 190 }; 191}