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