1{ config, lib, pkgs, ... }:
2let
3
4 cfg = config.programs.proxychains;
5
6 configFile = ''
7 ${cfg.chain.type}_chain
8 ${lib.optionalString (cfg.chain.type == "random")
9 "chain_len = ${builtins.toString cfg.chain.length}"}
10 ${lib.optionalString cfg.proxyDNS "proxy_dns"}
11 ${lib.optionalString cfg.quietMode "quiet_mode"}
12 remote_dns_subnet ${builtins.toString cfg.remoteDNSSubnet}
13 tcp_read_time_out ${builtins.toString cfg.tcpReadTimeOut}
14 tcp_connect_time_out ${builtins.toString cfg.tcpConnectTimeOut}
15 localnet ${cfg.localnet}
16 [ProxyList]
17 ${builtins.concatStringsSep "\n"
18 (lib.mapAttrsToList (k: v: "${v.type} ${v.host} ${builtins.toString v.port}")
19 (lib.filterAttrs (k: v: v.enable) cfg.proxies))}
20 '';
21
22 proxyOptions = {
23 options = {
24 enable = lib.mkEnableOption "this proxy";
25
26 type = lib.mkOption {
27 type = lib.types.enum [ "http" "socks4" "socks5" ];
28 description = "Proxy type.";
29 };
30
31 host = lib.mkOption {
32 type = lib.types.str;
33 description = "Proxy host or IP address.";
34 };
35
36 port = lib.mkOption {
37 type = lib.types.port;
38 description = "Proxy port";
39 };
40 };
41 };
42
43in {
44
45 ###### interface
46
47 options = {
48
49 programs.proxychains = {
50
51 enable = lib.mkEnableOption "proxychains configuration";
52
53 package = lib.mkPackageOption pkgs "proxychains" {
54 example = "proxychains-ng";
55 };
56
57 chain = {
58 type = lib.mkOption {
59 type = lib.types.enum [ "dynamic" "strict" "random" ];
60 default = "strict";
61 description = ''
62 `dynamic` - Each connection will be done via chained proxies
63 all proxies chained in the order as they appear in the list
64 at least one proxy must be online to play in chain
65 (dead proxies are skipped)
66 otherwise `EINTR` is returned to the app.
67
68 `strict` - Each connection will be done via chained proxies
69 all proxies chained in the order as they appear in the list
70 all proxies must be online to play in chain
71 otherwise `EINTR` is returned to the app.
72
73 `random` - Each connection will be done via random proxy
74 (or proxy chain, see {option}`programs.proxychains.chain.length`) from the list.
75 '';
76 };
77 length = lib.mkOption {
78 type = lib.types.nullOr lib.types.int;
79 default = null;
80 description = ''
81 Chain length for random chain.
82 '';
83 };
84 };
85
86 proxyDNS = lib.mkOption {
87 type = lib.types.bool;
88 default = true;
89 description = "Proxy DNS requests - no leak for DNS data.";
90 };
91
92 quietMode = lib.mkEnableOption "Quiet mode (no output from the library)";
93
94 remoteDNSSubnet = lib.mkOption {
95 type = lib.types.enum [ 10 127 224 ];
96 default = 224;
97 description = ''
98 Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default.
99 '';
100 };
101
102 tcpReadTimeOut = lib.mkOption {
103 type = lib.types.int;
104 default = 15000;
105 description = "Connection read time-out in milliseconds.";
106 };
107
108 tcpConnectTimeOut = lib.mkOption {
109 type = lib.types.int;
110 default = 8000;
111 description = "Connection time-out in milliseconds.";
112 };
113
114 localnet = lib.mkOption {
115 type = lib.types.str;
116 default = "127.0.0.0/255.0.0.0";
117 description = "By default enable localnet for loopback address ranges.";
118 };
119
120 proxies = lib.mkOption {
121 type = lib.types.attrsOf (lib.types.submodule proxyOptions);
122 description = ''
123 Proxies to be used by proxychains.
124 '';
125
126 example = lib.literalExpression ''
127 { myproxy =
128 { type = "socks4";
129 host = "127.0.0.1";
130 port = 1337;
131 };
132 }
133 '';
134 };
135
136 };
137
138 };
139
140 ###### implementation
141
142 meta.maintainers = with lib.maintainers; [ sorki ];
143
144 config = lib.mkIf cfg.enable {
145
146 assertions = lib.singleton {
147 assertion = cfg.chain.type != "random" && cfg.chain.length == null;
148 message = ''
149 Option `programs.proxychains.chain.length`
150 only makes sense with `programs.proxychains.chain.type` = "random".
151 '';
152 };
153
154 programs.proxychains.proxies = lib.mkIf config.services.tor.client.enable
155 {
156 torproxy = lib.mkDefault {
157 enable = true;
158 type = "socks4";
159 host = "127.0.0.1";
160 port = 9050;
161 };
162 };
163
164 environment.etc."proxychains.conf".text = configFile;
165 environment.systemPackages = [ cfg.package ];
166 };
167
168}