427 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			427 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ config, lib, pkgs, ... }:
 | 
						|
with lib;
 | 
						|
let
 | 
						|
  pkg = pkgs._3proxy;
 | 
						|
  cfg = config.services._3proxy;
 | 
						|
  optionalList = list: if list == [ ] then "*" else concatMapStringsSep "," toString list;
 | 
						|
in {
 | 
						|
  options.services._3proxy = {
 | 
						|
    enable = mkEnableOption "3proxy";
 | 
						|
    confFile = mkOption {
 | 
						|
      type = types.path;
 | 
						|
      example = "/var/lib/3proxy/3proxy.conf";
 | 
						|
      description = ''
 | 
						|
        Ignore all other 3proxy options and load configuration from this file.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    usersFile = mkOption {
 | 
						|
      type = types.nullOr types.path;
 | 
						|
      default = null;
 | 
						|
      example = "/var/lib/3proxy/3proxy.passwd";
 | 
						|
      description = ''
 | 
						|
        Load users and passwords from this file.
 | 
						|
 | 
						|
        Example users file with plain-text passwords:
 | 
						|
 | 
						|
        <literal>
 | 
						|
          test1:CL:password1
 | 
						|
          test2:CL:password2
 | 
						|
        </literal>
 | 
						|
 | 
						|
        Example users file with md5-crypted passwords:
 | 
						|
 | 
						|
        <literal>
 | 
						|
          test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1
 | 
						|
          test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME.
 | 
						|
        </literal>
 | 
						|
 | 
						|
        You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/
 | 
						|
        Note that htpasswd tool generates incompatible md5-crypted passwords.
 | 
						|
        Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/How-To-(incomplete)#USERS">documentation</link> for more information.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    services = mkOption {
 | 
						|
      type = types.listOf (types.submodule {
 | 
						|
        options = {
 | 
						|
          type = mkOption {
 | 
						|
            type = types.enum [
 | 
						|
              "proxy"
 | 
						|
              "socks"
 | 
						|
              "pop3p"
 | 
						|
              "ftppr"
 | 
						|
              "admin"
 | 
						|
              "dnspr"
 | 
						|
              "tcppm"
 | 
						|
              "udppm"
 | 
						|
            ];
 | 
						|
            example = "proxy";
 | 
						|
            description = ''
 | 
						|
              Service type. The following values are valid:
 | 
						|
 | 
						|
              <itemizedlist>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"proxy"</literal>: HTTP/HTTPS proxy (default port 3128).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"socks"</literal>: SOCKS 4/4.5/5 proxy (default port 1080).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"pop3p"</literal>: POP3 proxy (default port 110).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"ftppr"</literal>: FTP proxy (default port 21).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"admin"</literal>: Web interface (default port 80).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"dnspr"</literal>: Caching DNS proxy (default port 53).
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"tcppm"</literal>: TCP portmapper.
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"udppm"</literal>: UDP portmapper.
 | 
						|
                </para></listitem>
 | 
						|
              </itemizedlist>
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          bindAddress = mkOption {
 | 
						|
            type = types.str;
 | 
						|
            default = "[::]";
 | 
						|
            example = "127.0.0.1";
 | 
						|
            description = ''
 | 
						|
              Address used for service.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          bindPort = mkOption {
 | 
						|
            type = types.nullOr types.int;
 | 
						|
            default = null;
 | 
						|
            example = 3128;
 | 
						|
            description = ''
 | 
						|
              Override default port used for service.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          maxConnections = mkOption {
 | 
						|
            type = types.int;
 | 
						|
            default = 100;
 | 
						|
            example = 1000;
 | 
						|
            description = ''
 | 
						|
              Maximum number of simulationeous connections to this service.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          auth = mkOption {
 | 
						|
            type = types.listOf (types.enum [ "none" "iponly" "strong" ]);
 | 
						|
            example = [ "iponly" "strong" ];
 | 
						|
            description = ''
 | 
						|
              Authentication type. The following values are valid:
 | 
						|
 | 
						|
              <itemizedlist>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"none"</literal>: disables both authentication and authorization. You can not use ACLs.
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"iponly"</literal>: specifies no authentication. ACLs authorization is used.
 | 
						|
                </para></listitem>
 | 
						|
                <listitem><para>
 | 
						|
                  <literal>"strong"</literal>: authentication by username/password. If user is not registered their access is denied regardless of ACLs.
 | 
						|
                </para></listitem>
 | 
						|
              </itemizedlist>
 | 
						|
 | 
						|
              Double authentication is possible, e.g.
 | 
						|
 | 
						|
              <literal>
 | 
						|
                {
 | 
						|
                  auth = [ "iponly" "strong" ];
 | 
						|
                  acl = [
 | 
						|
                    {
 | 
						|
                      rule = "allow";
 | 
						|
                      targets = [ "192.168.0.0/16" ];
 | 
						|
                    }
 | 
						|
                    {
 | 
						|
                      rule = "allow"
 | 
						|
                      users = [ "user1" "user2" ];
 | 
						|
                    }
 | 
						|
                  ];
 | 
						|
                }
 | 
						|
              </literal>
 | 
						|
              In this example strong username authentication is not required to access 192.168.0.0/16.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          acl = mkOption {
 | 
						|
            type = types.listOf (types.submodule {
 | 
						|
              options = {
 | 
						|
                rule = mkOption {
 | 
						|
                  type = types.enum [ "allow" "deny" ];
 | 
						|
                  example = "allow";
 | 
						|
                  description = ''
 | 
						|
                    ACL rule. The following values are valid:
 | 
						|
 | 
						|
                    <itemizedlist>
 | 
						|
                      <listitem><para>
 | 
						|
                        <literal>"allow"</literal>: connections allowed.
 | 
						|
                      </para></listitem>
 | 
						|
                      <listitem><para>
 | 
						|
                        <literal>"deny"</literal>: connections not allowed.
 | 
						|
                      </para></listitem>
 | 
						|
                    </itemizedlist>
 | 
						|
                  '';
 | 
						|
                };
 | 
						|
                users = mkOption {
 | 
						|
                  type = types.listOf types.str;
 | 
						|
                  default = [ ];
 | 
						|
                  example = [ "user1" "user2" "user3" ];
 | 
						|
                  description = ''
 | 
						|
                    List of users, use empty list for any.
 | 
						|
                  '';
 | 
						|
                };
 | 
						|
                sources = mkOption {
 | 
						|
                  type = types.listOf types.str;
 | 
						|
                  default = [ ];
 | 
						|
                  example = [ "127.0.0.1" "192.168.1.0/24" ];
 | 
						|
                  description = ''
 | 
						|
                    List of source IP range, use empty list for any.
 | 
						|
                  '';
 | 
						|
                };
 | 
						|
                targets = mkOption {
 | 
						|
                  type = types.listOf types.str;
 | 
						|
                  default = [ ];
 | 
						|
                  example = [ "127.0.0.1" "192.168.1.0/24" ];
 | 
						|
                  description = ''
 | 
						|
                    List of target IP ranges, use empty list for any.
 | 
						|
                    May also contain host names instead of addresses.
 | 
						|
                    It's possible to use wildmask in the begginning and in the the end of hostname, e.g. *badsite.com or *badcontent*.
 | 
						|
                    Hostname is only checked if hostname presents in request.
 | 
						|
                  '';
 | 
						|
                };
 | 
						|
                targetPorts = mkOption {
 | 
						|
                  type = types.listOf types.int;
 | 
						|
                  default = [ ];
 | 
						|
                  example = [ 80 443 ];
 | 
						|
                  description = ''
 | 
						|
                    List of target ports, use empty list for any.
 | 
						|
                  '';
 | 
						|
                };
 | 
						|
              };
 | 
						|
            });
 | 
						|
            default = [ ];
 | 
						|
            example = literalExample ''
 | 
						|
              [
 | 
						|
                {
 | 
						|
                  rule = "allow";
 | 
						|
                  users = [ "user1" ];
 | 
						|
                }
 | 
						|
                {
 | 
						|
                  rule = "allow";
 | 
						|
                  sources = [ "192.168.1.0/24" ];
 | 
						|
                }
 | 
						|
                {
 | 
						|
                  rule = "deny";
 | 
						|
                }
 | 
						|
              ]
 | 
						|
            '';
 | 
						|
            description = ''
 | 
						|
              Use this option to limit user access to resources.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          extraArguments = mkOption {
 | 
						|
            type = types.nullOr types.str;
 | 
						|
            default = null;
 | 
						|
            example = "-46";
 | 
						|
            description = ''
 | 
						|
              Extra arguments for service.
 | 
						|
              Consult "Options" section in <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available arguments.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          extraConfig = mkOption {
 | 
						|
            type = types.nullOr types.lines;
 | 
						|
            default = null;
 | 
						|
            description = ''
 | 
						|
              Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection.
 | 
						|
              Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
      default = [ ];
 | 
						|
      example = literalExample ''
 | 
						|
        [
 | 
						|
          {
 | 
						|
            type = "proxy";
 | 
						|
            bindAddress = "192.168.1.24";
 | 
						|
            bindPort = 3128;
 | 
						|
            auth = [ "none" ];
 | 
						|
          }
 | 
						|
          {
 | 
						|
            type = "proxy";
 | 
						|
            bindAddress = "10.10.1.20";
 | 
						|
            bindPort = 3128;
 | 
						|
            auth = [ "iponly" ];
 | 
						|
          }
 | 
						|
          {
 | 
						|
            type = "socks";
 | 
						|
            bindAddress = "172.17.0.1";
 | 
						|
            bindPort = 1080;
 | 
						|
            auth = [ "strong" ];
 | 
						|
          }
 | 
						|
        ]
 | 
						|
      '';
 | 
						|
      description = ''
 | 
						|
        Use this option to define 3proxy services.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    denyPrivate = mkOption {
 | 
						|
      type = types.bool;
 | 
						|
      default = true;
 | 
						|
      description = ''
 | 
						|
        Whether to deny access to private IP ranges including loopback.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    privateRanges = mkOption {
 | 
						|
      type = types.listOf types.str;
 | 
						|
      default = [
 | 
						|
        "0.0.0.0/8"
 | 
						|
        "127.0.0.0/8"
 | 
						|
        "10.0.0.0/8"
 | 
						|
        "100.64.0.0/10"
 | 
						|
        "172.16.0.0/12"
 | 
						|
        "192.168.0.0/16"
 | 
						|
        "::"
 | 
						|
        "::1"
 | 
						|
        "fc00::/7"
 | 
						|
      ];
 | 
						|
      example = [
 | 
						|
        "0.0.0.0/8"
 | 
						|
        "127.0.0.0/8"
 | 
						|
        "10.0.0.0/8"
 | 
						|
        "100.64.0.0/10"
 | 
						|
        "172.16.0.0/12"
 | 
						|
        "192.168.0.0/16"
 | 
						|
        "::"
 | 
						|
        "::1"
 | 
						|
        "fc00::/7"
 | 
						|
      ];
 | 
						|
      description = ''
 | 
						|
        What IP ranges to deny access when denyPrivate is set tu true.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    resolution = mkOption {
 | 
						|
      type = types.submodule {
 | 
						|
        options = {
 | 
						|
          nserver = mkOption {
 | 
						|
            type = types.listOf types.str;
 | 
						|
            default = [ ];
 | 
						|
            example = [ "127.0.0.53" "192.168.1.3:5353/tcp" ];
 | 
						|
            description = ''
 | 
						|
              List of nameservers to use.
 | 
						|
 | 
						|
              Up to 5 nservers may be specified. If no nserver is configured,
 | 
						|
              default system name resolution functions are used.
 | 
						|
            '';
 | 
						|
          };
 | 
						|
          nscache = mkOption {
 | 
						|
            type = types.int;
 | 
						|
            default = 65535;
 | 
						|
            example = 65535;
 | 
						|
            description = "Set name cache size for IPv4.";
 | 
						|
          };
 | 
						|
          nscache6 = mkOption {
 | 
						|
            type = types.int;
 | 
						|
            default = 65535;
 | 
						|
            example = 65535;
 | 
						|
            description = "Set name cache size for IPv6.";
 | 
						|
          };
 | 
						|
          nsrecord = mkOption {
 | 
						|
            type = types.attrsOf types.str;
 | 
						|
            default = { };
 | 
						|
            example = literalExample ''
 | 
						|
              {
 | 
						|
                "files.local" = "192.168.1.12";
 | 
						|
                "site.local" = "192.168.1.43";
 | 
						|
              }
 | 
						|
            '';
 | 
						|
            description = "Adds static nsrecords.";
 | 
						|
          };
 | 
						|
        };
 | 
						|
      };
 | 
						|
      default = { };
 | 
						|
      description = ''
 | 
						|
        Use this option to configure name resolution and DNS caching.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
    extraConfig = mkOption {
 | 
						|
      type = types.nullOr types.lines;
 | 
						|
      default = null;
 | 
						|
      description = ''
 | 
						|
        Extra configuration, appended to the 3proxy configuration file.
 | 
						|
        Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options.
 | 
						|
      '';
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  config = mkIf cfg.enable {
 | 
						|
    services._3proxy.confFile = mkDefault (pkgs.writeText "3proxy.conf" ''
 | 
						|
      # log to stdout
 | 
						|
      log
 | 
						|
 | 
						|
      ${concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver}
 | 
						|
 | 
						|
      nscache ${toString cfg.resolution.nscache}
 | 
						|
      nscache6 ${toString cfg.resolution.nscache6}
 | 
						|
 | 
						|
      ${concatMapStringsSep "\n" (x: "nsrecord " + x)
 | 
						|
      (mapAttrsToList (name: value: "${name} ${value}")
 | 
						|
        cfg.resolution.nsrecord)}
 | 
						|
 | 
						|
      ${optionalString (cfg.usersFile != null)
 | 
						|
        ''users $"${cfg.usersFile}"''
 | 
						|
      }
 | 
						|
 | 
						|
      ${concatMapStringsSep "\n" (service: ''
 | 
						|
        auth ${concatStringsSep " " service.auth}
 | 
						|
 | 
						|
        ${optionalString (cfg.denyPrivate)
 | 
						|
        "deny * * ${optionalList cfg.privateRanges}"}
 | 
						|
 | 
						|
        ${concatMapStringsSep "\n" (acl:
 | 
						|
          "${acl.rule} ${
 | 
						|
            concatMapStringsSep " " optionalList [
 | 
						|
              acl.users
 | 
						|
              acl.sources
 | 
						|
              acl.targets
 | 
						|
              acl.targetPorts
 | 
						|
            ]
 | 
						|
          }") service.acl}
 | 
						|
 | 
						|
        maxconn ${toString service.maxConnections}
 | 
						|
 | 
						|
        ${optionalString (service.extraConfig != null) service.extraConfig}
 | 
						|
 | 
						|
        ${service.type} -i${toString service.bindAddress} ${
 | 
						|
          optionalString (service.bindPort != null)
 | 
						|
          "-p${toString service.bindPort}"
 | 
						|
        } ${
 | 
						|
          optionalString (service.extraArguments != null) service.extraArguments
 | 
						|
        }
 | 
						|
 | 
						|
        flush
 | 
						|
      '') cfg.services}
 | 
						|
      ${optionalString (cfg.extraConfig != null) cfg.extraConfig}
 | 
						|
    '');
 | 
						|
    systemd.services."3proxy" = {
 | 
						|
      description = "Tiny free proxy server";
 | 
						|
      documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ];
 | 
						|
      after = [ "network.target" ];
 | 
						|
      wantedBy = [ "multi-user.target" ];
 | 
						|
      serviceConfig = {
 | 
						|
        DynamicUser = true;
 | 
						|
        StateDirectory = "3proxy";
 | 
						|
        ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}";
 | 
						|
        Restart = "on-failure";
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  meta.maintainers = with maintainers; [ misuzu ];
 | 
						|
}
 |