about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/networking/microsocks.nix
blob: be79a8495636f6ece7f874a1ae24481cd09f7879 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
{ config,
  lib,
  pkgs,
  ...
}:

let
  cfg = config.services.microsocks;

  cmd =
    if cfg.execWrapper != null
    then "${cfg.execWrapper} ${cfg.package}/bin/microsocks"
    else "${cfg.package}/bin/microsocks";
  args =
    [ "-i" cfg.ip "-p" (toString cfg.port) ]
    ++ lib.optionals (cfg.authOnce) [ "-1" ]
    ++ lib.optionals (cfg.disableLogging) [ "-q" ]
    ++ lib.optionals (cfg.outgoingBindIp != null) [ "-b" cfg.outgoingBindIp ]
    ++ lib.optionals (cfg.authUsername != null) [ "-u" cfg.authUsername ];
in {
  options.services.microsocks = {
    enable = lib.mkEnableOption (lib.mdDoc "Tiny, portable SOCKS5 server with very moderate resource usage");
    user = lib.mkOption {
      default = "microsocks";
      description = lib.mdDoc "User microsocks runs as.";
      type = lib.types.str;
    };
    group = lib.mkOption {
      default = "microsocks";
      description = lib.mdDoc "Group microsocks runs as.";
      type = lib.types.str;
    };
    package = lib.mkPackageOption pkgs "microsocks" {};
    ip = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1";
      description = lib.mdDoc ''
        IP on which microsocks should listen. Defaults to 127.0.0.1 for
        security reasons.
      '';
    };
    port = lib.mkOption {
      type = lib.types.port;
      default = 1080;
      description = lib.mdDoc "Port on which microsocks should listen.";
    };
    disableLogging = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = lib.mdDoc "If true, microsocks will not log any messages to stdout/stderr.";
    };
    authOnce = lib.mkOption {
      type = lib.types.bool;
      default = false;
      description = lib.mdDoc ''
        If true, once a specific ip address authed successfully with user/pass,
        it is added to a whitelist and may use the proxy without auth.
      '';
    };
    outgoingBindIp = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = lib.mdDoc "Specifies which ip outgoing connections are bound to";
    };
    authUsername = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      example = "alice";
      description = lib.mdDoc "Optional username to use for authentication.";
    };
    authPasswordFile = lib.mkOption {
      type = lib.types.nullOr lib.types.path;
      default = null;
      example = "/run/secrets/microsocks-password";
      description = lib.mdDoc "Path to a file containing the password for authentication.";
    };
    execWrapper = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      example = ''
        ''${pkgs.mullvad-vpn}/bin/mullvad-exclude
      '';
      description = lib.mdDoc ''
        An optional command to prepend to the microsocks command (such as proxychains, or a VPN exclude command).
      '';
    };
  };
  config = lib.mkIf cfg.enable {
    assertions = [
      {
        assertion = (cfg.authUsername != null) == (cfg.authPasswordFile != null);
        message = "Need to set both authUsername and authPasswordFile for microsocks";
      }
    ];
    users = {
      users = lib.mkIf (cfg.user == "microsocks") {
        microsocks = {
          group = cfg.group;
          isSystemUser = true;
        };
      };
      groups = lib.mkIf (cfg.group == "microsocks") {
        microsocks = {};
      };
    };
    systemd.services.microsocks = {
      enable = true;
      description = "a tiny socks server";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        Restart = "on-failure";
        RestartSec = 10;
        LoadCredential = lib.optionalString (cfg.authPasswordFile != null) "MICROSOCKS_PASSWORD_FILE:${cfg.authPasswordFile}";
        MemoryDenyWriteExecute = true;
        SystemCallArchitectures = "native";
        PrivateTmp = true;
        NoNewPrivileges = true;
        ProtectSystem = "strict";
        ProtectHome = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        PrivateDevices = true;
        RestrictSUIDSGID = true;
        RestrictNamespaces = [
          "cgroup"
          "ipc"
          "pid"
          "user"
          "uts"
        ];
      };
      script =
        if cfg.authPasswordFile != null
        then ''
          PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/MICROSOCKS_PASSWORD_FILE")
          ${cmd} ${lib.escapeShellArgs args} -P "$PASSWORD"
        ''
        else ''
          ${cmd} ${lib.escapeShellArgs args}
        '';
    };
  };
}