python312Packages.aiohomeconnect: 0.10.0 -> 0.11.0 (#374011)
[NixPkgs.git] / nixos / modules / services / networking / shadowsocks.nix
blob2f6f40f2b0f60b2be1f9fc58e6b547244ec6dafa
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.shadowsocks;
8   opts = {
9     server = cfg.localAddress;
10     server_port = cfg.port;
11     method = cfg.encryptionMethod;
12     mode = cfg.mode;
13     user = "nobody";
14     fast_open = cfg.fastOpen;
15   } // optionalAttrs (cfg.plugin != null) {
16     plugin = cfg.plugin;
17     plugin_opts = cfg.pluginOpts;
18   } // optionalAttrs (cfg.password != null) {
19     password = cfg.password;
20   } // cfg.extraConfig;
22   configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
28   ###### interface
30   options = {
32     services.shadowsocks = {
34       enable = mkOption {
35         type = types.bool;
36         default = false;
37         description = ''
38           Whether to run shadowsocks-libev shadowsocks server.
39         '';
40       };
42       localAddress = mkOption {
43         type = types.coercedTo types.str singleton (types.listOf types.str);
44         default = [ "[::0]" "0.0.0.0" ];
45         description = ''
46           Local addresses to which the server binds.
47         '';
48       };
50       port = mkOption {
51         type = types.port;
52         default = 8388;
53         description = ''
54           Port which the server uses.
55         '';
56       };
58       password = mkOption {
59         type = types.nullOr types.str;
60         default = null;
61         description = ''
62           Password for connecting clients.
63         '';
64       };
66       passwordFile = mkOption {
67         type = types.nullOr types.path;
68         default = null;
69         description = ''
70           Password file with a password for connecting clients.
71         '';
72       };
74       mode = mkOption {
75         type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ];
76         default = "tcp_and_udp";
77         description = ''
78           Relay protocols.
79         '';
80       };
82       fastOpen = mkOption {
83         type = types.bool;
84         default = true;
85         description = ''
86           use TCP fast-open
87         '';
88       };
90       encryptionMethod = mkOption {
91         type = types.str;
92         default = "chacha20-ietf-poly1305";
93         description = ''
94           Encryption method. See <https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers>.
95         '';
96       };
98       plugin = mkOption {
99         type = types.nullOr types.str;
100         default = null;
101         example = literalExpression ''"''${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin"'';
102         description = ''
103           SIP003 plugin for shadowsocks
104         '';
105       };
107       pluginOpts = mkOption {
108         type = types.str;
109         default = "";
110         example = "server;host=example.com";
111         description = ''
112           Options to pass to the plugin if one was specified
113         '';
114       };
116       extraConfig = mkOption {
117         type = types.attrs;
118         default = {};
119         example = {
120           nameserver = "8.8.8.8";
121         };
122         description = ''
123           Additional configuration for shadowsocks that is not covered by the
124           provided options. The provided attrset will be serialized to JSON and
125           has to contain valid shadowsocks options. Unfortunately most
126           additional options are undocumented but it's easy to find out what is
127           available by looking into the source code of
128           <https://github.com/shadowsocks/shadowsocks-libev/blob/master/src/jconf.c>
129         '';
130       };
131     };
133   };
136   ###### implementation
138   config = mkIf cfg.enable {
139     assertions = [
140       {
141         # xor, make sure either password or passwordFile be set.
142         # shadowsocks-libev not support plain/none encryption method
143         # which indicated that password must set.
144         assertion = let noPasswd = cfg.password == null; noPasswdFile = cfg.passwordFile == null;
145           in (noPasswd && !noPasswdFile) || (!noPasswd && noPasswdFile);
146         message = "Option `password` or `passwordFile` must be set and cannot be set simultaneously";
147       }
148     ];
150     systemd.services.shadowsocks-libev = {
151       description = "shadowsocks-libev Daemon";
152       after = [ "network.target" ];
153       wantedBy = [ "multi-user.target" ];
154       path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.plugin != null) cfg.plugin ++ optional (cfg.passwordFile != null) pkgs.jq;
155       serviceConfig.PrivateTmp = true;
156       script = ''
157         ${optionalString (cfg.passwordFile != null) ''
158           cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
159         ''}
160         exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}
161       '';
162     };
163   };