1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.microsocks;
10
11 cmd =
12 if cfg.execWrapper != null then
13 "${cfg.execWrapper} ${cfg.package}/bin/microsocks"
14 else
15 "${cfg.package}/bin/microsocks";
16 args =
17 [
18 "-i"
19 cfg.ip
20 "-p"
21 (toString cfg.port)
22 ]
23 ++ lib.optionals (cfg.authOnce) [ "-1" ]
24 ++ lib.optionals (cfg.disableLogging) [ "-q" ]
25 ++ lib.optionals (cfg.outgoingBindIp != null) [
26 "-b"
27 cfg.outgoingBindIp
28 ]
29 ++ lib.optionals (cfg.authUsername != null) [
30 "-u"
31 cfg.authUsername
32 ];
33in
34{
35 options.services.microsocks = {
36 enable = lib.mkEnableOption "Tiny, portable SOCKS5 server with very moderate resource usage";
37 user = lib.mkOption {
38 default = "microsocks";
39 description = "User microsocks runs as.";
40 type = lib.types.str;
41 };
42 group = lib.mkOption {
43 default = "microsocks";
44 description = "Group microsocks runs as.";
45 type = lib.types.str;
46 };
47 package = lib.mkPackageOption pkgs "microsocks" { };
48 ip = lib.mkOption {
49 type = lib.types.str;
50 default = "127.0.0.1";
51 description = ''
52 IP on which microsocks should listen. Defaults to 127.0.0.1 for
53 security reasons.
54 '';
55 };
56 port = lib.mkOption {
57 type = lib.types.port;
58 default = 1080;
59 description = "Port on which microsocks should listen.";
60 };
61 disableLogging = lib.mkOption {
62 type = lib.types.bool;
63 default = false;
64 description = "If true, microsocks will not log any messages to stdout/stderr.";
65 };
66 authOnce = lib.mkOption {
67 type = lib.types.bool;
68 default = false;
69 description = ''
70 If true, once a specific ip address authed successfully with user/pass,
71 it is added to a whitelist and may use the proxy without auth.
72 '';
73 };
74 outgoingBindIp = lib.mkOption {
75 type = lib.types.nullOr lib.types.str;
76 default = null;
77 description = "Specifies which ip outgoing connections are bound to";
78 };
79 authUsername = lib.mkOption {
80 type = lib.types.nullOr lib.types.str;
81 default = null;
82 example = "alice";
83 description = "Optional username to use for authentication.";
84 };
85 authPasswordFile = lib.mkOption {
86 type = lib.types.nullOr lib.types.path;
87 default = null;
88 example = "/run/secrets/microsocks-password";
89 description = "Path to a file containing the password for authentication.";
90 };
91 execWrapper = lib.mkOption {
92 type = lib.types.nullOr lib.types.str;
93 default = null;
94 example = ''
95 ''${pkgs.mullvad-vpn}/bin/mullvad-exclude
96 '';
97 description = ''
98 An optional command to prepend to the microsocks command (such as proxychains, or a VPN exclude command).
99 '';
100 };
101 };
102 config = lib.mkIf cfg.enable {
103 assertions = [
104 {
105 assertion = (cfg.authUsername != null) == (cfg.authPasswordFile != null);
106 message = "Need to set both authUsername and authPasswordFile for microsocks";
107 }
108 ];
109 users = {
110 users = lib.mkIf (cfg.user == "microsocks") {
111 microsocks = {
112 group = cfg.group;
113 isSystemUser = true;
114 };
115 };
116 groups = lib.mkIf (cfg.group == "microsocks") {
117 microsocks = { };
118 };
119 };
120 systemd.services.microsocks = {
121 enable = true;
122 description = "a tiny socks server";
123 after = [ "network.target" ];
124 wantedBy = [ "multi-user.target" ];
125 serviceConfig = {
126 User = cfg.user;
127 Group = cfg.group;
128 Restart = "on-failure";
129 RestartSec = 10;
130 LoadCredential = lib.optionalString (
131 cfg.authPasswordFile != null
132 ) "MICROSOCKS_PASSWORD_FILE:${cfg.authPasswordFile}";
133 MemoryDenyWriteExecute = true;
134 SystemCallArchitectures = "native";
135 PrivateTmp = true;
136 NoNewPrivileges = true;
137 ProtectSystem = "strict";
138 ProtectHome = true;
139 ProtectKernelModules = true;
140 ProtectKernelTunables = true;
141 PrivateDevices = true;
142 RestrictSUIDSGID = true;
143 RestrictNamespaces = [
144 "cgroup"
145 "ipc"
146 "pid"
147 "user"
148 "uts"
149 ];
150 };
151 script =
152 if cfg.authPasswordFile != null then
153 ''
154 PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/MICROSOCKS_PASSWORD_FILE")
155 ${cmd} ${lib.escapeShellArgs args} -P "$PASSWORD"
156 ''
157 else
158 ''
159 ${cmd} ${lib.escapeShellArgs args}
160 '';
161 };
162 };
163}