summary refs log tree commit diff
path: root/modules/vpn
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-04-24 03:10:11 +0200
committerMel <mel@rnrd.eu>2026-04-24 03:18:05 +0200
commitfa38ea010957a98e778c32b23a8f133b14afdef1 (patch)
tree2548538141908ceafa25c5f8ac7371d054b7bd87 /modules/vpn
parent97e935e0ff718cbec86605bf584a5660812bdce9 (diff)
downloadnetwork-fa38ea010957a98e778c32b23a8f133b14afdef1.tar.zst
network-fa38ea010957a98e778c32b23a8f133b14afdef1.zip
Give the VPN its final name 'Tunnel'
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'modules/vpn')
-rw-r--r--modules/vpn/definition.nix75
-rw-r--r--modules/vpn/egress.nix133
-rw-r--r--modules/vpn/ingress.nix201
3 files changed, 0 insertions, 409 deletions
diff --git a/modules/vpn/definition.nix b/modules/vpn/definition.nix
deleted file mode 100644
index 60ea5d0..0000000
--- a/modules/vpn/definition.nix
+++ /dev/null
@@ -1,75 +0,0 @@
-# definition of the network layout which supports our vpn
-# architecture.
-
-{
-  # these are the available paths which a user is allowed to take
-  # to reach a specified egress server.
-  # when a user connects to a port defined here via wireguard,
-  # the primary ingress server (us), will establish a connection with
-  # the user and the backend egress server (this time, not via wireguard,
-  # but with a specific dpi-evading protocol), and route the users packets
-  # through to the egress.
-  paths = [
-    {
-      port = 50501;
-      egress = "taupe";
-
-      info = {
-        uuid = "328c90a0-20ae-4d4c-9e54-97e9ab41c053";
-        short = "b20629b505f39194";
-
-        public = "_837k5niQBE-qmgqpZalH3cS_fAIBwv8dwMoDW1uvgk";
-        keySecret = ../../secrets/vpn/egress-key-taupe.age;
-      };
-    }
-    {
-      port = 50502;
-      egress = "taureau";
-
-      info = {
-        uuid = "826b8598-ed75-4782-9b7e-27e0e16e1141";
-        short = "8f7e9f8a3fa46bf0";
-
-        public = "HvR4iP8URERpPBM4oG1Bjfw3mIfN0MoL2x6MHlt_TUM";
-        keySecret = ../../secrets/vpn/egress-key-taureau.age;
-      };
-    }
-  ];
-
-  # there are our users who are allowed to connect to any of our "paths".
-  # their ip is always a template, with 'X' representing the path index.
-  users = {
-    mel = {
-      key = "vnZoHXapCLLUhZ8A8R5W0iJ8LpWVLve29z41kkoT0BU=";
-      ip = "10.123.X.101";
-    };
-
-    andrei = {
-      key = "qqU4uYImLfUohIwl4KBshPtTINFcs0JVALjbmwpfxRg=";
-      ip = "10.123.X.102";
-    };
-
-    sergo = {
-      key = "qbZGMNIDZFCJC6SHtlyNIlIdGWHELceXClJCcagrj2Y=";
-      ip = "10.123.X.103";
-    };
-
-    fedor = {
-      key = "tEO9r8+jTpu8TBRmZ+/v087IgD/QfmofLUKs249i/F0=";
-      ip = "10.123.X.104";
-    };
-  };
-
-  # we use a website as a "mask" for vless/reality, which will tell our peers
-  # to pretend as if they're a user and a well-known website communicating with
-  # each other, even though they know that the keys don't actually match up,
-  # it's not possible to see that on the outside.
-  mask = "microsoft.com";
-
-  # we don't actually need this to configure the tunnel, but this is
-  # the public key of the ingress interface.
-  # when creating wireguard vpn configurations for the users, this
-  # is the public key of the server peer at `tunnel.rnrd.eu`.
-  # the matching private key of the pair is the secret `vpn/ingress-key`.
-  ingress.public = "s5yyPCJiN0uqW0jzKIbYCF7I9TthymiRzpNt466XeWk=";
-}
diff --git a/modules/vpn/egress.nix b/modules/vpn/egress.nix
deleted file mode 100644
index 7858751..0000000
--- a/modules/vpn/egress.nix
+++ /dev/null
@@ -1,133 +0,0 @@
-{
-  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";
-
-  inboundTag = "vless-in";
-  outboundTag = "direct-out";
-
-  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 = {
-    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
-            ];
-            privateKey = "@PRIVATE_KEY@";
-            shortIds = [ path.info.short ];
-          };
-        };
-      }
-    ];
-
-    # and we're out!
-    outbounds = [
-      {
-        protocol = "freedom";
-        tag = outboundTag;
-      }
-    ];
-
-    routing = {
-      rules = [
-        {
-          type = "field";
-          inboundTag = [ inboundTag ];
-          inherit outboundTag;
-        }
-      ];
-    };
-
-    log = {
-      loglevel = "debug";
-    };
-  };
-
-  config-file = pkgs.writeText "xray.json" (builtins.toJSON xrayConfig);
-in
-{
-  networking.firewall.allowedTCPPorts = [ port ];
-
-  age.secrets.egress-key = {
-    file = path.info.keySecret;
-  };
-
-  systemd.services = {
-    # 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!
-    generate-xray-config = {
-      description = "Generate Xray configuration";
-      wantedBy = [ "multi-user.target" ];
-      before = [ "xray.service" ];
-      partOf = [ "xray.service" ];
-      serviceConfig = {
-        Type = "oneshot";
-        RemainAfterExit = true;
-      };
-      script = ''
-        mkdir -p /run/xray-configuration
-        cp ${config-file} /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
-      '';
-    };
-
-    xray = {
-      requires = [ "generate-xray-config.service" ];
-      after = [ "generate-xray-config.service" ];
-      restartTriggers = [ config-file ];
-    };
-  };
-
-  services.xray = {
-    enable = true;
-    settingsFile = "/run/xray-configuration/xray.json";
-  };
-}
diff --git a/modules/vpn/ingress.nix b/modules/vpn/ingress.nix
deleted file mode 100644
index 6c6a78e..0000000
--- a/modules/vpn/ingress.nix
+++ /dev/null
@@ -1,201 +0,0 @@
-{
-  config,
-  lib,
-  ...
-}:
-
-let
-  inherit (lib)
-    imap0
-    attrValues
-    mergeAttrsList
-    replaceString
-    concatImapStringsSep
-    ;
-
-  definition = import ./definition.nix;
-
-  inherit (definition) paths users mask;
-
-  ownAddress = "10.123.X.1"; # ip of host running the ingress vpn (per-interface)
-
-  addressFromTemplate =
-    index: template: prefix:
-    "${replaceString "X" (toString (index + 1)) template}/${toString prefix}";
-
-  ingressName = index: "vpn-ingress${toString index}";
-  egressName = "vpn-egress0";
-  egressAddress = "10.123.255.1/16"; # /16 encompasses all possible subnet addresses
-  egressMTU = 1400;
-
-  egressHost = name: "${name}.rnrd.eu";
-in
-{
-  boot.kernel.sysctl = {
-    "net.ipv4.ip_forward" = 1; # allow ipv4 forwarding
-  };
-
-  networking.firewall = {
-    allowedUDPPorts = map (x: x.port) paths;
-    allowedTCPPorts = map (x: x.port) paths;
-    checkReversePath = "loose";
-  };
-
-  age.secrets.ingress-key = {
-    file = ../../secrets/vpn/ingress-key.age;
-    owner = "systemd-network";
-  };
-
-  systemd.network =
-    let
-      mkNetdev = index: path: {
-        "10-${ingressName index}" = {
-          netdevConfig = {
-            Kind = "wireguard";
-            Name = ingressName index;
-          };
-          wireguardConfig = {
-            PrivateKeyFile = config.age.secrets.ingress-key.path;
-            ListenPort = path.port;
-          };
-          wireguardPeers = map (user: {
-            PublicKey = user.key;
-            AllowedIPs = [ (addressFromTemplate index user.ip 32) ];
-          }) (attrValues users);
-        };
-      };
-
-      mkNetwork = index: path: {
-        "10-${ingressName index}" = {
-          name = ingressName index;
-          address = [ (addressFromTemplate index ownAddress 24) ];
-          routingPolicyRules = [
-            {
-              IncomingInterface = ingressName index;
-              Table = 100;
-            }
-          ];
-        };
-      };
-
-      ingressNetdevs = imap0 mkNetdev paths;
-
-      ingressNetworks = imap0 mkNetwork paths;
-      egressNetworks = [
-        {
-          "20-${egressName}" = {
-            name = egressName;
-            address = [ egressAddress ];
-            networkConfig = {
-              IPv4ReversePathFilter = "loose";
-            };
-            linkConfig = {
-              ActivationPolicy = "up";
-              RequiredForOnline = "no"; # does not count as online
-              MTUBytes = toString egressMTU;
-            };
-            routes = [
-              {
-                Destination = "0.0.0.0/0";
-                Table = 100;
-                Scope = "link";
-              }
-            ];
-          };
-        }
-      ];
-    in
-    {
-      netdevs = mergeAttrsList ingressNetdevs;
-      networks = mergeAttrsList (ingressNetworks ++ egressNetworks);
-    };
-
-  # allow forwarding packets between egress and ingress, but avoid any snat,
-  # ip should always keep it's origin form, for correct egress routing.
-  # also adapt mss to outgoing mss value, so that we don't shatter packets.
-  networking.nftables.ruleset =
-    let
-      ingressInterfaces = concatImapStringsSep "\", \"" (i: _: ingressName (i - 1)) paths;
-    in
-    ''
-      table inet filter {
-        chain forward {
-          type filter hook forward priority 0; policy drop;
-
-          tcp flags syn tcp option maxseg size set rt mtu
-
-          iifname { "${ingressInterfaces}" } oifname "${egressName}" accept
-          iifname "${egressName}" oifname { "${ingressInterfaces}" } accept
-        }
-      }
-    '';
-
-  # sing-box is a vpn client supporting various protocols which will allow us
-  # to configure it in whichever way we want to avoid russian dpi.
-  # in this case, our communications crossing the borders are relying on vless.
-  services.sing-box =
-    let
-      inboundName = "vpn-in";
-      outboundName = egress: "vpn-out-${egress}";
-    in
-    {
-      enable = true;
-      settings = {
-        inbounds = [
-          {
-            type = "tun";
-            tag = inboundName;
-            interface_name = egressName;
-            address = [ egressAddress ];
-            mtu = egressMTU;
-            stack = "gvisor";
-            auto_route = false; # we route manually
-            strict_route = false;
-            endpoint_independent_nat = true;
-          }
-        ];
-
-        outbounds = map (path: {
-          type = "vless";
-          flow = "xtls-rprx-vision";
-
-          server = egressHost path.egress;
-          server_port = 443;
-
-          tag = outboundName path.egress;
-          uuid = path.info.uuid;
-
-          tls = {
-            enabled = true;
-            server_name = "www.${mask}";
-
-            utls = {
-              enabled = true;
-              fingerprint = "chrome";
-            };
-
-            reality = {
-              enabled = true;
-              public_key = path.info.public;
-              short_id = path.info.short;
-            };
-          };
-        }) paths;
-
-        route = {
-          rules = imap0 (index: path: {
-            inbound = inboundName;
-            source_ip_cidr = [ (addressFromTemplate index "10.123.X.0" 24) ];
-            outbound = outboundName path.egress;
-          }) paths;
-
-          auto_detect_interface = true;
-        };
-
-        log = {
-          level = "debug";
-          timestamp = true;
-        };
-      };
-    };
-}