summaryrefslogtreecommitdiff
path: root/modules/nixos/shadowsocks.nix
blob: 69688da265357c01fc706ad7003fe23a19348016 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
{
  config,
  inputs,
  lib,
  pkgs,
  ...
}:
with lib; let
  cfg = config.nixfiles.modules.shadowsocks;
in {
  options.nixfiles.modules.shadowsocks = {
    enable = mkEnableOption "Shadowsocks";

    port = mkOption {
      type = with types; port;
      default = 8388;
      description = "Port.";
    };
  };

  config = mkIf cfg.enable {
    secrets.shadowsocks-json.file = "${inputs.self}/secrets/shadowsocks-json";

    services.fail2ban.jails.shadowsocks = {
      enabled = true;
      settings = {
        filter = "shadowsocks";
        inherit (cfg) port;
      };
    };

    systemd.services.shadowsocks = {
      description = "Shadowsocks";
      after = ["network.target"];
      wantedBy = ["multi-user.target"];
      serviceConfig = {
        DynamicUser = true;
        RuntimeDirectory = "shadowsocks";
        LoadCredential = "secret.json:${config.secrets.shadowsocks-json.path}";
        ExecStartPre = let
          mergeJson = let
            configFile = pkgs.writeText "config.json" (generators.toJSON {} {
              server = "::";
              server_port = cfg.port;
              # Can't really use AEAD-2022[1] just yet because it's not
              # supported by some[2] clients.
              #
              # [1]: https://shadowsocks.org/doc/sip022.html
              # [2]: https://github.com/shadowsocks/ShadowsocksX-NG/issues/1480
              # [2]: https://github.com/shadowsocks/shadowsocks-windows/issues/3448
              # method = "2022-blake3-chacha20-poly1305";
              method = "chacha20-ietf-poly1305";
              password = null; # Must be set as a secret.
              users = null; # Muse be set as a secret.
              fast_open = true;
              acl = pkgs.writeText "block-internal-access.acl" ''
                [outbound_block_list]
                0.0.0.0/8
                10.0.0.0/8
                100.64.0.0/10
                127.0.0.0/8
                169.254.0.0/16
                172.16.0.0/12
                192.0.0.0/24
                192.0.2.0/24
                192.88.99.0/24
                192.168.0.0/16
                198.18.0.0/15
                198.51.100.0/24
                203.0.113.0/24
                224.0.0.0/4
                240.0.0.0/4
                255.255.255.255/32
                ::1/128
                ::ffff:127.0.0.1/104
                fc00::/7
                fe80::/10
              '';
            });
          in
            pkgs.writeShellScript "meregeJson" ''
              ${getExe pkgs.jq} \
                -s '.[0] * .[1]' \
                ${configFile} \
                $CREDENTIALS_DIRECTORY/secret.json \
                >$RUNTIME_DIRECTORY/config.json
            '';
        in
          mergeJson;
        ExecStart = "${pkgs.shadowsocks-rust}/bin/ssserver --config \${RUNTIME_DIRECTORY}/config.json";
      };
    };

    environment.etc = mkIf config.nixfiles.modules.fail2ban.enable {
      "fail2ban/filter.d/shadowsocks.conf".text = ''
        [Definition]
        failregex = ^.*tcp handshake failed.*\[::ffff:<ADDR>\].*$
        ignoreregex =
        journalmatch = _SYSTEMD_UNIT=shadowsocks.service
      '';
    };

    networking.firewall.allowedTCPPorts = [cfg.port];

    # https://github.com/shadowsocks/shadowsocks/wiki/Optimizing-Shadowsocks
    boot.kernel.sysctl = {
      "net.core.rmem_max" = mkOverride 100 (pow 2 26);
      "net.core.wmem_max" = mkOverride 100 (pow 2 26);
      "net.core.netdev_max_backlog" = pow 2 18;
      "net.core.somaxconn" = pow 2 12;
      "net.ipv4.tcp_syncookies" = 1;
      "net.ipv4.tcp_tw_reuse" = mkOverride 100 1;
      "net.ipv4.tcp_tw_recycle" = mkOverride 100 0;
      "net.ipv4.tcp_fin_timeout" = mkOverride 100 30;
      "net.ipv4.tcp_keepalive_time" = 60 * 20;
      "net.ipv4.ip_local_port_range" = "10000 65000";
      "net.ipv4.tcp_max_syn_backlog" = pow 2 13;
      "net.ipv4.tcp_max_tw_buckets" = pow 2 12;
      "net.ipv4.tcp_fastopen" = mkOverride 100 3;
      "net.ipv4.tcp_mem" = mkOverride 100 (mkTcpMem 15 16 17);
      "net.ipv4.tcp_rmem" = mkOverride 100 (mkTcpMem 12 16 26);
      "net.ipv4.tcp_wmem" = mkOverride 100 (mkTcpMem 12 16 26);
      "net.ipv4.tcp_mtu_probing" = 1;
      "net.ipv4.tcp_congestion_control" = "hybla";
    };
  };
}