1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8
9 cfg = config.services.nylon;
10
11 homeDir = "/var/lib/nylon";
12
13 configFile =
14 cfg:
15 pkgs.writeText "nylon-${cfg.name}.conf" ''
16 [General]
17 No-Simultaneous-Conn=${toString cfg.nrConnections}
18 Log=${if cfg.logging then "1" else "0"}
19 Verbose=${if cfg.verbosity then "1" else "0"}
20
21 [Server]
22 Binding-Interface=${cfg.acceptInterface}
23 Connecting-Interface=${cfg.bindInterface}
24 Port=${toString cfg.port}
25 Allow-IP=${lib.concatStringsSep " " cfg.allowedIPRanges}
26 Deny-IP=${lib.concatStringsSep " " cfg.deniedIPRanges}
27 '';
28
29 nylonOpts =
30 { name, ... }:
31 {
32
33 options = {
34
35 enable = lib.mkOption {
36 type = lib.types.bool;
37 default = false;
38 description = ''
39 Enables nylon as a running service upon activation.
40 '';
41 };
42
43 name = lib.mkOption {
44 type = lib.types.str;
45 default = "";
46 description = "The name of this nylon instance.";
47 };
48
49 nrConnections = lib.mkOption {
50 type = lib.types.int;
51 default = 10;
52 description = ''
53 The number of allowed simultaneous connections to the daemon, default 10.
54 '';
55 };
56
57 logging = lib.mkOption {
58 type = lib.types.bool;
59 default = false;
60 description = ''
61 Enable logging, default is no logging.
62 '';
63 };
64
65 verbosity = lib.mkOption {
66 type = lib.types.bool;
67 default = false;
68 description = ''
69 Enable verbose output, default is to not be verbose.
70 '';
71 };
72
73 acceptInterface = lib.mkOption {
74 type = lib.types.str;
75 default = "lo";
76 description = ''
77 Tell nylon which interface to listen for client requests on, default is "lo".
78 '';
79 };
80
81 bindInterface = lib.mkOption {
82 type = lib.types.str;
83 default = "enp3s0f0";
84 description = ''
85 Tell nylon which interface to use as an uplink, default is "enp3s0f0".
86 '';
87 };
88
89 port = lib.mkOption {
90 type = lib.types.port;
91 default = 1080;
92 description = ''
93 What port to listen for client requests, default is 1080.
94 '';
95 };
96
97 allowedIPRanges = lib.mkOption {
98 type = with lib.types; listOf str;
99 default = [
100 "192.168.0.0/16"
101 "127.0.0.1/8"
102 "172.16.0.1/12"
103 "10.0.0.0/8"
104 ];
105 description = ''
106 Allowed client IP ranges are evaluated first, defaults to ARIN IPv4 private ranges:
107 [ "192.168.0.0/16" "127.0.0.0/8" "172.16.0.0/12" "10.0.0.0/8" ]
108 '';
109 };
110
111 deniedIPRanges = lib.mkOption {
112 type = with lib.types; listOf str;
113 default = [ "0.0.0.0/0" ];
114 description = ''
115 Denied client IP ranges, these gets evaluated after the allowed IP ranges, defaults to all IPv4 addresses:
116 [ "0.0.0.0/0" ]
117 To block all other access than the allowed.
118 '';
119 };
120 };
121 config = {
122 name = lib.mkDefault name;
123 };
124 };
125
126 mkNamedNylon = cfg: {
127 "nylon-${cfg.name}" = {
128 description = "Nylon, a lightweight SOCKS proxy server";
129 after = [ "network.target" ];
130 wantedBy = [ "multi-user.target" ];
131 serviceConfig = {
132 User = "nylon";
133 Group = "nylon";
134 WorkingDirectory = homeDir;
135 ExecStart = "${pkgs.nylon}/bin/nylon -f -c ${configFile cfg}";
136 };
137 };
138 };
139
140 anyNylons = lib.collect (p: p ? enable) cfg;
141 enabledNylons = lib.filter (p: p.enable == true) anyNylons;
142 nylonUnits = map (nylon: mkNamedNylon nylon) enabledNylons;
143
144in
145
146{
147
148 ###### interface
149
150 options = {
151
152 services.nylon = lib.mkOption {
153 default = { };
154 description = "Collection of named nylon instances";
155 type = with lib.types; attrsOf (submodule nylonOpts);
156 internal = true;
157 };
158
159 };
160
161 ###### implementation
162
163 config = lib.mkIf (lib.length (enabledNylons) > 0) {
164
165 users.users.nylon = {
166 group = "nylon";
167 description = "Nylon SOCKS Proxy";
168 home = homeDir;
169 createHome = true;
170 uid = config.ids.uids.nylon;
171 };
172
173 users.groups.nylon.gid = config.ids.gids.nylon;
174
175 systemd.services = lib.foldr (a: b: a // b) { } nylonUnits;
176
177 };
178}