at 23.11-pre 13 kB view raw
1{ config, lib, pkgs, ... }: 2with lib; 3let 4 pkg = pkgs._3proxy; 5 cfg = config.services._3proxy; 6 optionalList = list: if list == [ ] then "*" else concatMapStringsSep "," toString list; 7in { 8 options.services._3proxy = { 9 enable = mkEnableOption (lib.mdDoc "3proxy"); 10 confFile = mkOption { 11 type = types.path; 12 example = "/var/lib/3proxy/3proxy.conf"; 13 description = lib.mdDoc '' 14 Ignore all other 3proxy options and load configuration from this file. 15 ''; 16 }; 17 usersFile = mkOption { 18 type = types.nullOr types.path; 19 default = null; 20 example = "/var/lib/3proxy/3proxy.passwd"; 21 description = lib.mdDoc '' 22 Load users and passwords from this file. 23 24 Example users file with plain-text passwords: 25 26 ``` 27 test1:CL:password1 28 test2:CL:password2 29 ``` 30 31 Example users file with md5-crypted passwords: 32 33 ``` 34 test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1 35 test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME. 36 ``` 37 38 You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/ 39 Note that htpasswd tool generates incompatible md5-crypted passwords. 40 Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/How-To-%28incomplete%29#USERS) for more information. 41 ''; 42 }; 43 services = mkOption { 44 type = types.listOf (types.submodule { 45 options = { 46 type = mkOption { 47 type = types.enum [ 48 "proxy" 49 "socks" 50 "pop3p" 51 "ftppr" 52 "admin" 53 "dnspr" 54 "tcppm" 55 "udppm" 56 ]; 57 example = "proxy"; 58 description = lib.mdDoc '' 59 Service type. The following values are valid: 60 61 - `"proxy"`: HTTP/HTTPS proxy (default port 3128). 62 - `"socks"`: SOCKS 4/4.5/5 proxy (default port 1080). 63 - `"pop3p"`: POP3 proxy (default port 110). 64 - `"ftppr"`: FTP proxy (default port 21). 65 - `"admin"`: Web interface (default port 80). 66 - `"dnspr"`: Caching DNS proxy (default port 53). 67 - `"tcppm"`: TCP portmapper. 68 - `"udppm"`: UDP portmapper. 69 ''; 70 }; 71 bindAddress = mkOption { 72 type = types.str; 73 default = "[::]"; 74 example = "127.0.0.1"; 75 description = lib.mdDoc '' 76 Address used for service. 77 ''; 78 }; 79 bindPort = mkOption { 80 type = types.nullOr types.int; 81 default = null; 82 example = 3128; 83 description = lib.mdDoc '' 84 Override default port used for service. 85 ''; 86 }; 87 maxConnections = mkOption { 88 type = types.int; 89 default = 100; 90 example = 1000; 91 description = lib.mdDoc '' 92 Maximum number of simulationeous connections to this service. 93 ''; 94 }; 95 auth = mkOption { 96 type = types.listOf (types.enum [ "none" "iponly" "strong" ]); 97 example = [ "iponly" "strong" ]; 98 description = lib.mdDoc '' 99 Authentication type. The following values are valid: 100 101 - `"none"`: disables both authentication and authorization. You can not use ACLs. 102 - `"iponly"`: specifies no authentication. ACLs authorization is used. 103 - `"strong"`: authentication by username/password. If user is not registered their access is denied regardless of ACLs. 104 105 Double authentication is possible, e.g. 106 107 ``` 108 { 109 auth = [ "iponly" "strong" ]; 110 acl = [ 111 { 112 rule = "allow"; 113 targets = [ "192.168.0.0/16" ]; 114 } 115 { 116 rule = "allow" 117 users = [ "user1" "user2" ]; 118 } 119 ]; 120 } 121 ``` 122 In this example strong username authentication is not required to access 192.168.0.0/16. 123 ''; 124 }; 125 acl = mkOption { 126 type = types.listOf (types.submodule { 127 options = { 128 rule = mkOption { 129 type = types.enum [ "allow" "deny" ]; 130 example = "allow"; 131 description = lib.mdDoc '' 132 ACL rule. The following values are valid: 133 134 - `"allow"`: connections allowed. 135 - `"deny"`: connections not allowed. 136 ''; 137 }; 138 users = mkOption { 139 type = types.listOf types.str; 140 default = [ ]; 141 example = [ "user1" "user2" "user3" ]; 142 description = lib.mdDoc '' 143 List of users, use empty list for any. 144 ''; 145 }; 146 sources = mkOption { 147 type = types.listOf types.str; 148 default = [ ]; 149 example = [ "127.0.0.1" "192.168.1.0/24" ]; 150 description = lib.mdDoc '' 151 List of source IP range, use empty list for any. 152 ''; 153 }; 154 targets = mkOption { 155 type = types.listOf types.str; 156 default = [ ]; 157 example = [ "127.0.0.1" "192.168.1.0/24" ]; 158 description = lib.mdDoc '' 159 List of target IP ranges, use empty list for any. 160 May also contain host names instead of addresses. 161 It's possible to use wildmask in the beginning and in the the end of hostname, e.g. `*badsite.com` or `*badcontent*`. 162 Hostname is only checked if hostname presents in request. 163 ''; 164 }; 165 targetPorts = mkOption { 166 type = types.listOf types.int; 167 default = [ ]; 168 example = [ 80 443 ]; 169 description = lib.mdDoc '' 170 List of target ports, use empty list for any. 171 ''; 172 }; 173 }; 174 }); 175 default = [ ]; 176 example = literalExpression '' 177 [ 178 { 179 rule = "allow"; 180 users = [ "user1" ]; 181 } 182 { 183 rule = "allow"; 184 sources = [ "192.168.1.0/24" ]; 185 } 186 { 187 rule = "deny"; 188 } 189 ] 190 ''; 191 description = lib.mdDoc '' 192 Use this option to limit user access to resources. 193 ''; 194 }; 195 extraArguments = mkOption { 196 type = types.nullOr types.str; 197 default = null; 198 example = "-46"; 199 description = lib.mdDoc '' 200 Extra arguments for service. 201 Consult "Options" section in [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available arguments. 202 ''; 203 }; 204 extraConfig = mkOption { 205 type = types.nullOr types.lines; 206 default = null; 207 description = lib.mdDoc '' 208 Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection. 209 Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available options. 210 ''; 211 }; 212 }; 213 }); 214 default = [ ]; 215 example = literalExpression '' 216 [ 217 { 218 type = "proxy"; 219 bindAddress = "192.168.1.24"; 220 bindPort = 3128; 221 auth = [ "none" ]; 222 } 223 { 224 type = "proxy"; 225 bindAddress = "10.10.1.20"; 226 bindPort = 3128; 227 auth = [ "iponly" ]; 228 } 229 { 230 type = "socks"; 231 bindAddress = "172.17.0.1"; 232 bindPort = 1080; 233 auth = [ "strong" ]; 234 } 235 ] 236 ''; 237 description = lib.mdDoc '' 238 Use this option to define 3proxy services. 239 ''; 240 }; 241 denyPrivate = mkOption { 242 type = types.bool; 243 default = true; 244 description = lib.mdDoc '' 245 Whether to deny access to private IP ranges including loopback. 246 ''; 247 }; 248 privateRanges = mkOption { 249 type = types.listOf types.str; 250 default = [ 251 "0.0.0.0/8" 252 "127.0.0.0/8" 253 "10.0.0.0/8" 254 "100.64.0.0/10" 255 "172.16.0.0/12" 256 "192.168.0.0/16" 257 "::" 258 "::1" 259 "fc00::/7" 260 ]; 261 description = lib.mdDoc '' 262 What IP ranges to deny access when denyPrivate is set tu true. 263 ''; 264 }; 265 resolution = mkOption { 266 type = types.submodule { 267 options = { 268 nserver = mkOption { 269 type = types.listOf types.str; 270 default = [ ]; 271 example = [ "127.0.0.53" "192.168.1.3:5353/tcp" ]; 272 description = lib.mdDoc '' 273 List of nameservers to use. 274 275 Up to 5 nservers may be specified. If no nserver is configured, 276 default system name resolution functions are used. 277 ''; 278 }; 279 nscache = mkOption { 280 type = types.int; 281 default = 65535; 282 description = lib.mdDoc "Set name cache size for IPv4."; 283 }; 284 nscache6 = mkOption { 285 type = types.int; 286 default = 65535; 287 description = lib.mdDoc "Set name cache size for IPv6."; 288 }; 289 nsrecord = mkOption { 290 type = types.attrsOf types.str; 291 default = { }; 292 example = literalExpression '' 293 { 294 "files.local" = "192.168.1.12"; 295 "site.local" = "192.168.1.43"; 296 } 297 ''; 298 description = lib.mdDoc "Adds static nsrecords."; 299 }; 300 }; 301 }; 302 default = { }; 303 description = lib.mdDoc '' 304 Use this option to configure name resolution and DNS caching. 305 ''; 306 }; 307 extraConfig = mkOption { 308 type = types.nullOr types.lines; 309 default = null; 310 description = lib.mdDoc '' 311 Extra configuration, appended to the 3proxy configuration file. 312 Consult [documentation](https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg) for available options. 313 ''; 314 }; 315 }; 316 317 config = mkIf cfg.enable { 318 services._3proxy.confFile = mkDefault (pkgs.writeText "3proxy.conf" '' 319 # log to stdout 320 log 321 322 ${concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver} 323 324 nscache ${toString cfg.resolution.nscache} 325 nscache6 ${toString cfg.resolution.nscache6} 326 327 ${concatMapStringsSep "\n" (x: "nsrecord " + x) 328 (mapAttrsToList (name: value: "${name} ${value}") 329 cfg.resolution.nsrecord)} 330 331 ${optionalString (cfg.usersFile != null) 332 ''users $"${cfg.usersFile}"'' 333 } 334 335 ${concatMapStringsSep "\n" (service: '' 336 auth ${concatStringsSep " " service.auth} 337 338 ${optionalString (cfg.denyPrivate) 339 "deny * * ${optionalList cfg.privateRanges}"} 340 341 ${concatMapStringsSep "\n" (acl: 342 "${acl.rule} ${ 343 concatMapStringsSep " " optionalList [ 344 acl.users 345 acl.sources 346 acl.targets 347 acl.targetPorts 348 ] 349 }") service.acl} 350 351 maxconn ${toString service.maxConnections} 352 353 ${optionalString (service.extraConfig != null) service.extraConfig} 354 355 ${service.type} -i${toString service.bindAddress} ${ 356 optionalString (service.bindPort != null) 357 "-p${toString service.bindPort}" 358 } ${ 359 optionalString (service.extraArguments != null) service.extraArguments 360 } 361 362 flush 363 '') cfg.services} 364 ${optionalString (cfg.extraConfig != null) cfg.extraConfig} 365 ''); 366 systemd.services."3proxy" = { 367 description = "Tiny free proxy server"; 368 documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ]; 369 after = [ "network.target" ]; 370 wantedBy = [ "multi-user.target" ]; 371 serviceConfig = { 372 DynamicUser = true; 373 StateDirectory = "3proxy"; 374 ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}"; 375 Restart = "on-failure"; 376 }; 377 }; 378 }; 379 380 meta.maintainers = with maintainers; [ misuzu ]; 381}