Find a file
2026-05-07 19:34:11 +00:00
lib format w/ alejandra (#43) 2026-05-07 19:34:11 +00:00
modules format w/ alejandra (#43) 2026-05-07 19:34:11 +00:00
tests format w/ alejandra (#43) 2026-05-07 19:34:11 +00:00
.gitignore add .gitignore 2024-06-06 01:25:02 +02:00
flake.nix format nix code 2025-10-08 22:33:33 +02:00
LICENSE Initial commit 2024-02-28 20:20:24 +00:00
README.md add DNS leak prevention note to README 2025-12-13 14:02:58 +00:00

    ⛓️ VPN-Confinement ⛓️

    A NixOS module that lets you route traffic from systemd services through a VPN while preventing DNS leaks.


Important

To "prevent" DNS leaks, this module simply makes the NSCD socket inaccessible to a systemd service. This might cause other problems, and leaks have only been tested when resolving DNS over UDP. Other ways of resolving DNS like DoT and DoH have not been tested, and DNS can leak in other ways, so you are always advised to monitor and test for DNS leaks yourself.

Installation

Nix Flake

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    vpn-confinement.url = "github:Maroka-chan/VPN-Confinement";
  };

  outputs = { self, nixpkgs, vpn-confinement, ... }:
  {
    # Change hostname, system, etc. as needed
    nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./configuration.nix
        vpn-confinement.nixosModules.default
      ];
    };
  };
}

Usage

Define VPN network namespace

vpnNamespaces.<name> = { # The name is limited to 7 characters
  enable = true;
  wireguardConfigFile = <path to secret wireguard config file>;
  accessibleFrom = [
    "<ip or subnet>"
  ];
  portMappings = [{
      from = <port on host>;
      to = <port in VPN network namespace>;
      protocol = "<transport protocol>"; # protocol = "tcp"(default), "udp", or "both"
  }];
  openVPNPorts = [{
    port = <port to access through VPN interface>;
    protocol = "<transport protocol>"; # protocol = "tcp"(default), "udp", or "both"
  }];
};

Add systemd service to VPN network namespace

systemd.services.<name>.vpnConfinement = {
  enable = true;
  vpnNamespace = "<network namespace name>";
};

Example

# configuration.nix
{ pkgs, lib, config, ... }:
{
  # Define VPN network namespace
  vpnNamespaces.wg = {
    enable = true;
    wireguardConfigFile = /. + "/secrets/wg0.conf";
    accessibleFrom = [
      "192.168.0.0/24"
    ];
    portMappings = [
      { from = 9091; to = 9091; }
    ];
    openVPNPorts = [{
      port = 60729;
      protocol = "both";
    }];
  };

  # Add systemd service to VPN network namespace
  systemd.services.transmission.vpnConfinement = {
    enable = true;
    vpnNamespace = "wg";
  };

  services.transmission = {
    enable = true;
    settings = {
      "rpc-bind-address" = "192.168.15.1"; # Bind RPC/WebUI to VPN network namespace address

      # RPC-whitelist examples
      "rpc-whitelist" = "192.168.15.5"; # Access from default network namespace
      "rpc-whitelist" = "192.168.1.*";  # Access from other machines on specific subnet
      "rpc-whitelist" = "127.0.0.1";    # Access through loopback within VPN network namespace
    };
  };
}

Note

Access from the default network namespace is done using the VPN network namespace address.
curl 192.168.15.1:9091

See all options and their descriptions in the module file.