summary refs log tree commit diff
path: root/modules/vpn
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-04-23 20:35:56 +0200
committerMel <mel@rnrd.eu>2026-04-23 22:58:22 +0200
commit97a40c153e1daf8a1de251db825cb6a6020d5e1f (patch)
tree116bcd1f12d7bd1ba74adb2d56d209029c0140d6 /modules/vpn
parente84316120e46345f803c38de53efdda4ab9a8d0f (diff)
downloadnetwork-97a40c153e1daf8a1de251db825cb6a6020d5e1f.tar.zst
network-97a40c153e1daf8a1de251db825cb6a6020d5e1f.zip
Define VPN egress & ingress default MTUs, routes, reverse paths, ...
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'modules/vpn')
-rw-r--r--modules/vpn/egress.nix92
-rw-r--r--modules/vpn/ingress.nix16
2 files changed, 70 insertions, 38 deletions
diff --git a/modules/vpn/egress.nix b/modules/vpn/egress.nix
index fe39880..7858751 100644
--- a/modules/vpn/egress.nix
+++ b/modules/vpn/egress.nix
@@ -20,6 +20,9 @@ let
   # other protocols, if roskomnadzor learns to sniff out vless fully.
   protocol = "vless";
 
+  inboundTag = "vless-in";
+  outboundTag = "direct-out";
+
   definition = import ./definition.nix;
   inherit (definition) paths mask;
 
@@ -27,48 +30,63 @@ let
     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";
-              }
+  xrayConfig = {
+    inbounds = [
+      {
+        inherit port protocol;
+        tag = inboundTag;
+
+        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
             ];
-            decryption = "none";
+            privateKey = "@PRIVATE_KEY@";
+            shortIds = [ path.info.short ];
           };
+        };
+      }
+    ];
 
-          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 = outboundTag;
+      }
+    ];
 
-      # and we're out!
-      outbounds = [
+    routing = {
+      rules = [
         {
-          protocol = "freedom";
-          tag = "direct";
+          type = "field";
+          inboundTag = [ inboundTag ];
+          inherit outboundTag;
         }
       ];
-    }
-  );
+    };
+
+    log = {
+      loglevel = "debug";
+    };
+  };
+
+  config-file = pkgs.writeText "xray.json" (builtins.toJSON xrayConfig);
 in
 {
   networking.firewall.allowedTCPPorts = [ port ];
@@ -92,7 +110,7 @@ in
       };
       script = ''
         mkdir -p /run/xray-configuration
-        cp ${xrayConfig} /run/xray-configuration/xray.json
+        cp ${config-file} /run/xray-configuration/xray.json
 
         egress_key=$(cat ${config.age.secrets.egress-key.path})
 
@@ -104,7 +122,7 @@ in
     xray = {
       requires = [ "generate-xray-config.service" ];
       after = [ "generate-xray-config.service" ];
-      restartTriggers = [ xrayConfig ];
+      restartTriggers = [ config-file ];
     };
   };
 
diff --git a/modules/vpn/ingress.nix b/modules/vpn/ingress.nix
index 2a6a582..2cf1ae5 100644
--- a/modules/vpn/ingress.nix
+++ b/modules/vpn/ingress.nix
@@ -25,7 +25,8 @@ let
 
   ingressName = index: "vpn-ingress${toString index}";
   egressName = "vpn-egress0";
-  egressAddress = "10.123.255.1/30";
+  egressAddress = "10.123.255.1/16"; # /16 encompasses all possible subnet addresses
+  egressMTU = 1400;
 
   egressHost = name: "${name}.rnrd.eu";
 in
@@ -36,6 +37,8 @@ in
 
   networking.firewall = {
     allowedUDPPorts = map (x: x.port) paths;
+    allowedTCPPorts = map (x: x.port) paths;
+    checkReversePath = "loose";
   };
 
   age.secrets.ingress-key = {
@@ -83,9 +86,13 @@ in
           "20-${egressName}" = {
             name = egressName;
             address = [ egressAddress ];
+            networkConfig = {
+              IPv4ReversePathFilter = "loose";
+            };
             linkConfig = {
               ActivationPolicy = "up";
               RequiredForOnline = "no"; # does not count as online
+              MTUBytes = toString egressMTU;
             };
             routes = [
               {
@@ -137,6 +144,7 @@ in
             tag = inboundName;
             interface_name = egressName;
             address = [ egressAddress ];
+            mtu = egressMTU;
             auto_route = false; # we route manually
             strict_route = false;
             endpoint_independent_nat = true;
@@ -146,6 +154,7 @@ in
         outbounds = map (path: {
           type = "vless";
           flow = "xtls-rprx-vision";
+          packet_encoding = "xudp";
 
           server = egressHost path.egress;
           server_port = 443;
@@ -179,6 +188,11 @@ in
 
           auto_detect_interface = true;
         };
+
+        log = {
+          level = "debug";
+          timestamp = true;
+        };
       };
     };
 }