summary refs log tree commit diff
path: root/modules/vpn/egress.nix
blob: 699d107183d3a1beab202d6708eb156b3c4ff627 (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
{
  me,
  config,
  pkgs,
  lib,
  ...
}:

let
  inherit (lib) findFirst;

  # this is the https port, we use it to try to trick dpi into thinking
  # we are just serving normal encrypted web traffic, nothing interesting! :)
  # this does mean that our egress servers are unable to support normal www
  # services which we put on machines by default, which is okay.
  port = 443;

  # supposedly the current gold-standard protocol for circumventing dpi!
  # both xray (egress-side) and sing-box (ingress-side) support various
  # other protocols, if roskomnadzor learns to sniff out vless fully.
  protocol = "vless";

  definition = import ./definition.nix;
  inherit (definition) paths mask;

  path = findFirst (
    p: p.egress == me.name
  ) (throw "no egress information found for this server!") paths;

  xrayConfig = pkgs.writeText "xray.json" (
    builtins.toJSON {
      inbounds = [
        {
          inherit port protocol;

          settings = {
            clients = [
              {
                id = path.info.uuid;
                flow = "xtls-rprx-vision";
              }
            ];
            decryption = "none";
          };

          streamSettings = {
            network = "tcp";
            security = "reality";
            realitySettings = {
              show = false;
              dest = "www.${mask}:443";
              serverNames = [
                "www.${mask}"
                mask
              ];
              privateKey = "@PRIVATE_KEY@";
              shortIds = [ path.info.short ];
            };
          };
        }
      ];

      # and we're out!
      outbounds = [
        {
          protocol = "freedom";
          tag = "direct";
        }
      ];
    }
  );
in
{
  networking.firewall.allowedTCPPorts = [ port ];

  age.secrets.egress-key = {
    file = path.info.keySecret;
  };

  # we have to make an xray config on the fly because
  # xray does not like reading secrets from specific files,
  # it wants them in plain-text!
  systemd.services.generate-xray-config = {
    before = [ "xray.service" ];
    requiredBy = [ "xray.service" ];
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;
    };
    script = ''
      mkdir -p /run/xray-configuration
      cp ${xrayConfig} /run/xray-configuration/xray.json

      egress_key=$(cat ${config.age.secrets.egress-key.path})

      # use sd for replacement as a fancy new tool for this
      ${pkgs.sd}/bin/sd "@PRIVATE_KEY@" "$egress_key" /run/xray-configuration/xray.json

      chown root:xray /run/xray-configuration/xray.json
      chmod 640 /run/xray-configuration/xray.json
    '';
  };

  services.xray = {
    enable = true;
    settingsFile = "/run/xray-configuration/xray.json";
  };
}