diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 7fe40063771..2507b7f20ba 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -9480,4 +9480,14 @@ github = "yevhenshymotiuk"; githubId = 44244245; }; + hmenke = { + name = "Henri Menke"; + email = "henri@henrimenke.de"; + github = "hmenke"; + githubId = 1903556; + keys = [{ + longkeyid = "rsa4096/0xD65C9AFB4C224DA3"; + fingerprint = "F1C5 760E 45B9 9A44 72E9 6BFB D65C 9AFB 4C22 4DA3"; + }]; + }; } diff --git a/nixos/modules/services/networking/shadowsocks.nix b/nixos/modules/services/networking/shadowsocks.nix index af12db590f0..4fd988297f6 100644 --- a/nixos/modules/services/networking/shadowsocks.nix +++ b/nixos/modules/services/networking/shadowsocks.nix @@ -11,8 +11,13 @@ let method = cfg.encryptionMethod; mode = cfg.mode; user = "nobody"; - fast_open = true; - } // optionalAttrs (cfg.password != null) { password = cfg.password; }; + fast_open = cfg.fastOpen; + } // optionalAttrs (cfg.plugin != null) { + plugin = cfg.plugin; + plugin_opts = cfg.pluginOpts; + } // optionalAttrs (cfg.password != null) { + password = cfg.password; + }; configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts); @@ -74,6 +79,14 @@ in ''; }; + fastOpen = mkOption { + type = types.bool; + default = true; + description = '' + use TCP fast-open + ''; + }; + encryptionMethod = mkOption { type = types.str; default = "chacha20-ietf-poly1305"; @@ -82,6 +95,23 @@ in ''; }; + plugin = mkOption { + type = types.nullOr types.str; + default = null; + example = "\${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin"; + description = '' + SIP003 plugin for shadowsocks + ''; + }; + + pluginOpts = mkOption { + type = types.str; + default = ""; + example = "server;host=example.com"; + description = '' + Options to pass to the plugin if one was specified + ''; + }; }; }; @@ -99,7 +129,7 @@ in description = "shadowsocks-libev Daemon"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq; + path = [ pkgs.shadowsocks-libev cfg.plugin ] ++ optional (cfg.passwordFile != null) pkgs.jq; serviceConfig.PrivateTmp = true; script = '' ${optionalString (cfg.passwordFile != null) '' diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 66fa435da71..b922032e3f6 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -307,6 +307,7 @@ in sanoid = handleTest ./sanoid.nix {}; sddm = handleTest ./sddm.nix {}; service-runner = handleTest ./service-runner.nix {}; + shadowsocks = handleTest ./shadowsocks.nix {}; shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {}; shiori = handleTest ./shiori.nix {}; signal-desktop = handleTest ./signal-desktop.nix {}; diff --git a/nixos/tests/shadowsocks.nix b/nixos/tests/shadowsocks.nix new file mode 100644 index 00000000000..6cb288f7611 --- /dev/null +++ b/nixos/tests/shadowsocks.nix @@ -0,0 +1,80 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "shadowsocks"; + meta = { + maintainers = with lib.maintainers; [ hmenke ]; + }; + + nodes = { + server = { + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; + networking.useDHCP = false; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.1"; prefixLength = 24; } + ]; + networking.firewall.rejectPackets = true; + networking.firewall.allowedTCPPorts = [ 8488 ]; + networking.firewall.allowedUDPPorts = [ 8488 ]; + services.shadowsocks = { + enable = true; + encryptionMethod = "chacha20-ietf-poly1305"; + password = "pa$$w0rd"; + localAddress = [ "0.0.0.0" ]; + port = 8488; + fastOpen = false; + mode = "tcp_and_udp"; + plugin = "${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin"; + pluginOpts = "server;host=nixos.org"; + }; + services.nginx = { + enable = true; + virtualHosts.server = { + locations."/".root = pkgs.writeTextDir "index.html" "It works!"; + }; + }; + }; + + client = { + networking.useDHCP = false; + networking.interfaces.eth1.ipv4.addresses = [ + { address = "192.168.0.2"; prefixLength = 24; } + ]; + systemd.services.shadowsocks-client = { + description = "connect to shadowsocks"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ + shadowsocks-libev + shadowsocks-v2ray-plugin + ]; + script = '' + exec ss-local \ + -s 192.168.0.1 \ + -p 8488 \ + -l 1080 \ + -k 'pa$$w0rd' \ + -m chacha20-ietf-poly1305 \ + -a nobody \ + --plugin "${pkgs.shadowsocks-v2ray-plugin}/bin/v2ray-plugin" \ + --plugin-opts "host=nixos.org" + ''; + }; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("shadowsocks-libev.service") + client.wait_for_unit("shadowsocks-client.service") + + client.fail( + "${pkgs.curl}/bin/curl 192.168.0.1:80" + ) + + msg = client.succeed( + "${pkgs.curl}/bin/curl --socks5 localhost:1080 192.168.0.1:80" + ) + assert msg == "It works!", "Could not connect through shadowsocks" + ''; + } +)