at 22.05-pre 6.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 cfg = config.services.geoipupdate; 5in 6{ 7 imports = [ 8 (lib.mkRemovedOptionModule [ "services" "geoip-updater" ] "services.geoip-updater has been removed, use services.geoipupdate instead.") 9 ]; 10 11 options = { 12 services.geoipupdate = { 13 enable = lib.mkEnableOption '' 14 periodic downloading of GeoIP databases using 15 <productname>geoipupdate</productname>. 16 ''; 17 18 interval = lib.mkOption { 19 type = lib.types.str; 20 default = "weekly"; 21 description = '' 22 Update the GeoIP databases at this time / interval. 23 The format is described in 24 <citerefentry><refentrytitle>systemd.time</refentrytitle> 25 <manvolnum>7</manvolnum></citerefentry>. 26 ''; 27 }; 28 29 settings = lib.mkOption { 30 description = '' 31 <productname>geoipupdate</productname> configuration 32 options. See 33 <link xlink:href="https://github.com/maxmind/geoipupdate/blob/main/doc/GeoIP.conf.md" /> 34 for a full list of available options. 35 ''; 36 type = lib.types.submodule { 37 freeformType = 38 with lib.types; 39 let 40 type = oneOf [str int bool]; 41 in 42 attrsOf (either type (listOf type)); 43 44 options = { 45 46 AccountID = lib.mkOption { 47 type = lib.types.int; 48 description = '' 49 Your MaxMind account ID. 50 ''; 51 }; 52 53 EditionIDs = lib.mkOption { 54 type = with lib.types; listOf (either str int); 55 example = [ 56 "GeoLite2-ASN" 57 "GeoLite2-City" 58 "GeoLite2-Country" 59 ]; 60 description = '' 61 List of database edition IDs. This includes new string 62 IDs like <literal>GeoIP2-City</literal> and old 63 numeric IDs like <literal>106</literal>. 64 ''; 65 }; 66 67 LicenseKey = lib.mkOption { 68 type = lib.types.path; 69 description = '' 70 A file containing the <productname>MaxMind</productname> 71 license key. 72 ''; 73 }; 74 75 DatabaseDirectory = lib.mkOption { 76 type = lib.types.path; 77 default = "/var/lib/GeoIP"; 78 example = "/run/GeoIP"; 79 description = '' 80 The directory to store the database files in. The 81 directory will be automatically created, the owner 82 changed to <literal>geoip</literal> and permissions 83 set to world readable. This applies if the directory 84 already exists as well, so don't use a directory with 85 sensitive contents. 86 ''; 87 }; 88 89 }; 90 }; 91 }; 92 }; 93 94 }; 95 96 config = lib.mkIf cfg.enable { 97 98 services.geoipupdate.settings = { 99 LockFile = "/run/geoipupdate/.lock"; 100 }; 101 102 systemd.services.geoipupdate-create-db-dir = { 103 serviceConfig.Type = "oneshot"; 104 script = '' 105 mkdir -p ${cfg.settings.DatabaseDirectory} 106 chmod 0755 ${cfg.settings.DatabaseDirectory} 107 ''; 108 }; 109 110 systemd.services.geoipupdate = { 111 description = "GeoIP Updater"; 112 requires = [ "geoipupdate-create-db-dir.service" ]; 113 after = [ 114 "geoipupdate-create-db-dir.service" 115 "network-online.target" 116 "nss-lookup.target" 117 ]; 118 wants = [ "network-online.target" ]; 119 startAt = cfg.interval; 120 serviceConfig = { 121 ExecStartPre = 122 let 123 geoipupdateKeyValue = lib.generators.toKeyValue { 124 mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " " rec { 125 mkValueString = v: with builtins; 126 if isInt v then toString v 127 else if isString v then v 128 else if true == v then "1" 129 else if false == v then "0" 130 else if isList v then lib.concatMapStringsSep " " mkValueString v 131 else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}"; 132 }; 133 }; 134 135 geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings); 136 137 script = '' 138 chown geoip "${cfg.settings.DatabaseDirectory}" 139 140 cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf 141 ${pkgs.replace-secret}/bin/replace-secret '${cfg.settings.LicenseKey}' \ 142 '${cfg.settings.LicenseKey}' \ 143 /run/geoipupdate/GeoIP.conf 144 ''; 145 in 146 "+${pkgs.writeShellScript "start-pre-full-privileges" script}"; 147 ExecStart = "${pkgs.geoipupdate}/bin/geoipupdate -f /run/geoipupdate/GeoIP.conf"; 148 User = "geoip"; 149 DynamicUser = true; 150 ReadWritePaths = cfg.settings.DatabaseDirectory; 151 RuntimeDirectory = "geoipupdate"; 152 RuntimeDirectoryMode = 0700; 153 CapabilityBoundingSet = ""; 154 PrivateDevices = true; 155 PrivateMounts = true; 156 PrivateUsers = true; 157 ProtectClock = true; 158 ProtectControlGroups = true; 159 ProtectHome = true; 160 ProtectHostname = true; 161 ProtectKernelLogs = true; 162 ProtectKernelModules = true; 163 ProtectKernelTunables = true; 164 ProtectProc = "invisible"; 165 ProcSubset = "pid"; 166 SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; 167 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 168 RestrictRealtime = true; 169 RestrictNamespaces = true; 170 MemoryDenyWriteExecute = true; 171 LockPersonality = true; 172 SystemCallArchitectures = "native"; 173 }; 174 }; 175 176 systemd.timers.geoipupdate-initial-run = { 177 wantedBy = [ "timers.target" ]; 178 unitConfig.ConditionPathExists = "!${cfg.settings.DatabaseDirectory}"; 179 timerConfig = { 180 Unit = "geoipupdate.service"; 181 OnActiveSec = 0; 182 }; 183 }; 184 }; 185 186 meta.maintainers = [ lib.maintainers.talyz ]; 187}