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