summary refs log tree commit diff
path: root/nixos/modules/services/misc/geoipupdate.nix
blob: 3211d4d88e4d9dd565447cc78f7cfb648a3a6e0a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
{ config, lib, pkgs, ... }:

let
  cfg = config.services.geoipupdate;
in
{
  imports = [
    (lib.mkRemovedOptionModule [ "services" "geoip-updater" ] "services.geoip-updater has been removed, use services.geoipupdate instead.")
  ];

  options = {
    services.geoipupdate = {
      enable = lib.mkEnableOption ''
        periodic downloading of GeoIP databases using
        <productname>geoipupdate</productname>.
      '';

      interval = lib.mkOption {
        type = lib.types.str;
        default = "weekly";
        description = ''
          Update the GeoIP databases at this time / interval.
          The format is described in
          <citerefentry><refentrytitle>systemd.time</refentrytitle>
          <manvolnum>7</manvolnum></citerefentry>.
        '';
      };

      settings = lib.mkOption {
        description = ''
          <productname>geoipupdate</productname> configuration
          options. See
          <link xlink:href="https://github.com/maxmind/geoipupdate/blob/main/doc/GeoIP.conf.md" />
          for a full list of available options.
        '';
        type = lib.types.submodule {
          freeformType =
            with lib.types;
            let
              type = oneOf [str int bool];
            in
              attrsOf (either type (listOf type));

          options = {

            AccountID = lib.mkOption {
              type = lib.types.int;
              description = ''
                Your MaxMind account ID.
              '';
            };

            EditionIDs = lib.mkOption {
              type = with lib.types; listOf (either str int);
              example = [
                "GeoLite2-ASN"
                "GeoLite2-City"
                "GeoLite2-Country"
              ];
              description = ''
                List of database edition IDs. This includes new string
                IDs like <literal>GeoIP2-City</literal> and old
                numeric IDs like <literal>106</literal>.
              '';
            };

            LicenseKey = lib.mkOption {
              type = lib.types.path;
              description = ''
                A file containing the <productname>MaxMind</productname>
                license key.
              '';
            };

            DatabaseDirectory = lib.mkOption {
              type = lib.types.path;
              default = "/var/lib/GeoIP";
              example = "/run/GeoIP";
              description = ''
                The directory to store the database files in. The
                directory will be automatically created, the owner
                changed to <literal>geoip</literal> and permissions
                set to world readable. This applies if the directory
                already exists as well, so don't use a directory with
                sensitive contents.
              '';
            };

          };
        };
      };
    };

  };

  config = lib.mkIf cfg.enable {

    services.geoipupdate.settings = {
      LockFile = "/run/geoipupdate/.lock";
    };

    systemd.services.geoipupdate-create-db-dir = {
      serviceConfig.Type = "oneshot";
      script = ''
        mkdir -p ${cfg.settings.DatabaseDirectory}
        chmod 0755 ${cfg.settings.DatabaseDirectory}
      '';
    };

    systemd.services.geoipupdate = {
      description = "GeoIP Updater";
      requires = [ "geoipupdate-create-db-dir.service" ];
      after = [
        "geoipupdate-create-db-dir.service"
        "network-online.target"
        "nss-lookup.target"
      ];
      wants = [ "network-online.target" ];
      startAt = cfg.interval;
      serviceConfig = {
        ExecStartPre =
          let
            geoipupdateKeyValue = lib.generators.toKeyValue {
              mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " " rec {
                mkValueString = v: with builtins;
                  if isInt           v then toString v
                  else if isString   v then v
                  else if true  ==   v then "1"
                  else if false ==   v then "0"
                  else if isList     v then lib.concatMapStringsSep " " mkValueString v
                  else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
              };
            };

            geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings);

            script = ''
              chown geoip "${cfg.settings.DatabaseDirectory}"

              cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf
              ${pkgs.replace-secret}/bin/replace-secret '${cfg.settings.LicenseKey}' \
                                                        '${cfg.settings.LicenseKey}' \
                                                        /run/geoipupdate/GeoIP.conf
            '';
          in
            "+${pkgs.writeShellScript "start-pre-full-privileges" script}";
        ExecStart = "${pkgs.geoipupdate}/bin/geoipupdate -f /run/geoipupdate/GeoIP.conf";
        User = "geoip";
        DynamicUser = true;
        ReadWritePaths = cfg.settings.DatabaseDirectory;
        RuntimeDirectory = "geoipupdate";
        RuntimeDirectoryMode = 0700;
        CapabilityBoundingSet = "";
        PrivateDevices = true;
        PrivateMounts = true;
        PrivateUsers = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        ProcSubset = "pid";
        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
        RestrictRealtime = true;
        RestrictNamespaces = true;
        MemoryDenyWriteExecute = true;
        LockPersonality = true;
        SystemCallArchitectures = "native";
      };
    };

    systemd.timers.geoipupdate-initial-run = {
      wantedBy = [ "timers.target" ];
      unitConfig.ConditionPathExists = "!${cfg.settings.DatabaseDirectory}";
      timerConfig = {
        Unit = "geoipupdate.service";
        OnActiveSec = 0;
      };
    };
  };

  meta.maintainers = [ lib.maintainers.talyz ];
}