1{ config, lib, pkgs, ... }:
2
3let
4 cfg = config.services.lokinet;
5 dataDir = "/var/lib/lokinet";
6 settingsFormat = pkgs.formats.ini { listsAsDuplicateKeys = true; };
7 configFile = settingsFormat.generate "lokinet.ini" (lib.filterAttrsRecursive (n: v: v != null) cfg.settings);
8in with lib; {
9 options.services.lokinet = {
10 enable = mkEnableOption "Lokinet daemon";
11
12 package = mkPackageOption pkgs "lokinet" { };
13
14 useLocally = mkOption {
15 type = types.bool;
16 default = false;
17 example = true;
18 description = "Whether to use Lokinet locally.";
19 };
20
21 settings = mkOption {
22 type = with types;
23 submodule {
24 freeformType = settingsFormat.type;
25
26 options = {
27 dns = {
28 bind = mkOption {
29 type = str;
30 default = "127.3.2.1";
31 description = "Address to bind to for handling DNS requests.";
32 };
33
34 upstream = mkOption {
35 type = listOf str;
36 default = [ "9.9.9.10" ];
37 example = [ "1.1.1.1" "8.8.8.8" ];
38 description = ''
39 Upstream resolver(s) to use as fallback for non-loki addresses.
40 Multiple values accepted.
41 '';
42 };
43 };
44
45 network = {
46 exit = mkOption {
47 type = bool;
48 default = false;
49 description = ''
50 Whether to act as an exit node. Beware that this
51 increases demand on the server and may pose liability concerns.
52 Enable at your own risk.
53 '';
54 };
55
56 exit-node = mkOption {
57 type = nullOr (listOf str);
58 default = null;
59 example = ''
60 exit-node = [ "example.loki" ]; # maps all exit traffic to example.loki
61 exit-node = [ "example.loki:100.0.0.0/24" ]; # maps 100.0.0.0/24 to example.loki
62 '';
63 description = ''
64 Specify a `.loki` address and an optional ip range to use as an exit broker.
65 See <http://probably.loki/wiki/index.php?title=Exit_Nodes> for
66 a list of exit nodes.
67 '';
68 };
69
70 keyfile = mkOption {
71 type = nullOr str;
72 default = null;
73 example = "snappkey.private";
74 description = ''
75 The private key to persist address with. If not specified the address will be ephemeral.
76 This keyfile is generated automatically if the specified file doesn't exist.
77 '';
78 };
79 };
80 };
81 };
82 default = { };
83 example = literalExpression ''
84 {
85 dns = {
86 bind = "127.3.2.1";
87 upstream = [ "1.1.1.1" "8.8.8.8" ];
88 };
89
90 network.exit-node = [ "example.loki" "example2.loki" ];
91 }
92 '';
93 description = ''
94 Configuration for Lokinet.
95 Currently, the best way to view the available settings is by
96 generating a config file using `lokinet -g`.
97 '';
98 };
99 };
100
101 config = mkIf cfg.enable {
102 networking.resolvconf.extraConfig = mkIf cfg.useLocally ''
103 name_servers="${cfg.settings.dns.bind}"
104 '';
105
106 systemd.services.lokinet = {
107 description = "Lokinet";
108 after = [ "network-online.target" "network.target" ];
109 wants = [ "network-online.target" "network.target" ];
110 wantedBy = [ "multi-user.target" ];
111
112 preStart = ''
113 ln -sf ${cfg.package}/share/bootstrap.signed ${dataDir}
114 ${pkgs.coreutils}/bin/install -m 600 ${configFile} ${dataDir}/lokinet.ini
115
116 ${optionalString (cfg.settings.network.keyfile != null) ''
117 ${pkgs.crudini}/bin/crudini --set ${dataDir}/lokinet.ini network keyfile "${dataDir}/${cfg.settings.network.keyfile}"
118 ''}
119 '';
120
121 serviceConfig = {
122 DynamicUser = true;
123 StateDirectory = "lokinet";
124 AmbientCapabilities = [ "CAP_NET_ADMIN" "CAP_NET_BIND_SERVICE" ];
125 ExecStart = "${cfg.package}/bin/lokinet ${dataDir}/lokinet.ini";
126 Restart = "always";
127 RestartSec = "5s";
128
129 # hardening
130 LockPersonality = true;
131 MemoryDenyWriteExecute = true;
132 NoNewPrivileges = true;
133 PrivateTmp = true;
134 PrivateMounts = true;
135 ProtectControlGroups = true;
136 ProtectHome = true;
137 ProtectHostname = true;
138 ProtectKernelLogs = true;
139 ProtectKernelModules = true;
140 ProtectKernelTunables = true;
141 ProtectSystem = "strict";
142 ReadWritePaths = "/dev/net/tun";
143 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
144 RestrictNamespaces = true;
145 RestrictRealtime = true;
146 RestrictSUIDSGID = true;
147 };
148 };
149
150 environment.systemPackages = [ cfg.package ];
151 };
152}