1 { config, lib, pkgs, ... }:
6 cfg = config.services.shadowsocks;
9 server = cfg.localAddress;
10 server_port = cfg.port;
11 method = cfg.encryptionMethod;
14 fast_open = cfg.fastOpen;
15 } // optionalAttrs (cfg.plugin != null) {
17 plugin_opts = cfg.pluginOpts;
18 } // optionalAttrs (cfg.password != null) {
19 password = cfg.password;
22 configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts);
32 services.shadowsocks = {
38 Whether to run shadowsocks-libev shadowsocks server.
42 localAddress = mkOption {
43 type = types.coercedTo types.str singleton (types.listOf types.str);
44 default = [ "[::0]" "0.0.0.0" ];
46 Local addresses to which the server binds.
54 Port which the server uses.
59 type = types.nullOr types.str;
62 Password for connecting clients.
66 passwordFile = mkOption {
67 type = types.nullOr types.path;
70 Password file with a password for connecting clients.
75 type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ];
76 default = "tcp_and_udp";
90 encryptionMethod = mkOption {
92 default = "chacha20-ietf-poly1305";
94 Encryption method. See <https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers>.
99 type = types.nullOr types.str;
101 example = literalExpression ''"''${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin"'';
103 SIP003 plugin for shadowsocks
107 pluginOpts = mkOption {
110 example = "server;host=example.com";
112 Options to pass to the plugin if one was specified
116 extraConfig = mkOption {
120 nameserver = "8.8.8.8";
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>
136 ###### implementation
138 config = mkIf cfg.enable {
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";
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;
157 ${optionalString (cfg.passwordFile != null) ''
158 cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json
160 exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile}