Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/openvpn: add network namespace support #109643

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

ju1m
Copy link
Contributor

@ju1m ju1m commented Jan 17, 2021

This is working enough for me so far but may benefit from reviews and tests before being merged.
I am not sure how to handle the DNS option from DHCP, currently /etc/netns/${netns}/resolv.conf is always set (ip netns bind mounting it onto /etc/resolv.conf) but maybe it should be guarded by a NixOS option. I've also not tested the interaction with updateResolvConf.

Motivation for this change

Add network namespace support to OpenVPN.
This enables to move the OpenVPN interface and selected systemd services in a dedicated network namespace without the physical network interfaces.

Things done
  • Add services.netns module to create network namespaces and have systemd services depend upon it.
  • Add services.netns.${netns}.sysctl to configure sysctls within network namespaces, and inherit defaults from boot.kernel.sysctl.
  • Add services.netns.${netns}.nftables to configure the firewall using nftables within network namespaces, iif. networking.nftables.enable is true.
  • Move to services.openvpn.settings option using freeformType.
  • Add option services.openvpn.server.${server}.netns. Note that this requires script-security = 2 because after ip link has moved openvpn's device the network configuration has to be done from scratch within the network namespace.
  • Alias services.openvpn.server.${server}.{up,down} to services.openvpn.server.${server}.settings.{up,down}, using mkAliasAndWrapDefsWithPriority.
  • Add services.openvpn.server.${server}.enable.
Example

This creates the riseup network namespace, start an openvpn in the initial network namespace, and then move openvpn's interface to that riseup network namespace.

{ pkgs, lib, config, ... }:
let
  netns = "riseup";
  inherit (config.services) openvpn;
  apiUrl = "https://api.black.riseup.net/3/cert";
  ca = pkgs.fetchurl {
    url = "https://black.riseup.net/ca.crt";
    hash = "sha256-Zdvnfz2k7iWlbgmmcUJrpJZ1dp7o0qXeJhP0HWJD7ro=";
  } + "";
  key-cert = "/run/openvpn-${netns}/key+cert.pem";
in
{
services.openvpn.servers.${netns} = {
  inherit netns;
  settings = {
    remote =
      # amsterdam
      ["212.83.182.127" "212.83.165.160" "212.129.4.141"] ++
      # miami
      ["37.218.244.249" "37.218.244.251"] ++
      # montreal
      ["199.58.83.10" "199.58.83.10" "199.58.83.12"] ++
      # new-york
      ["185.220.103.12"] ++
      # paris
      ["212.83.146.228" "212.83.143.67" "163.172.126.44"] ++
      # seattle
      ["198.252.153.28" "198.252.153.28"] ++
      [];
    remote-random = true;
    port = "443";
    proto = "tcp";
    inherit ca;
    key = key-cert;
    cert = key-cert;

    auth = "SHA1";
    cipher = "AES-128-CBC";
    client = true;
    dev = "ov-${netns}";
    dev-type = "tun";
    keepalive = "10 30";
    nobind = true;
    persist-key = true;
    persist-tun = true;
    remote-cert-tls = "server";
    reneg-sec = 0;
    script-security = 2;
    tls-cipher = "DHE-RSA-AES128-SHA";
    tls-client = true;
    tun-ipv6 = true;
    up-restart = true;
    verb = 3;
  };
};
systemd.services."openvpn-${netns}" = {
  preStart = ''
    set -e
    ${pkgs.curl}/bin/curl -X POST --cacert ${ca} -o ${key-cert} -Ls ${apiUrl}
    chmod 700 ${key-cert}
  '';
  serviceConfig = {
    RuntimeDirectory = [ "openvpn-${netns}" ];
    RuntimeDirectoryMode = "0700";
  };
};
networking.nftables.ruleset = ''
  add rule inet filter fw2net meta skuid root tcp dport 443 counter accept comment "OpenVPN Riseup"
'';
services.netns.namespaces.${netns} = {
  nftables = ''
    include "${../../../../networking/nftables.txt}"
    table inet filter {
      chain input {
        type filter hook input priority filter
        policy drop
        iifname lo accept
        jump check-tcp
        ct state { established, related } accept
        jump accept-connectivity-input
        jump check-broadcast
        ct state invalid drop
      }
      chain forward {
        type filter hook forward priority filter
        policy drop
        jump accept-connectivity-forward
      }
      chain output {
        type filter hook output priority filter
        policy drop
        oifname lo accept
        ct state { related, established } accept
        jump accept-connectivity-output
      }
    }
  '';
};
}

This excerpt puts transmission in that riseup network namespace.

let
  netns = "riseup";
  inherit (config.services) transmission;
in
{                         
  systemd.services.transmission = {
    after = [ "netns-${netns}.service" ];
    requires = [ "netns-${netns}.service" ];
    unitConfig.JoinsNamespaceOf = ["netns-${netns}.service"];
    # Alternatively: serviceConfig.NetworkNamespacePath = "/var/run/netns/${netns}";
    serviceConfig.BindReadOnlyPaths = ["/etc/netns/${netns}/resolv.conf:/etc/resolv.conf"];
    serviceConfig.PrivateNetwork = true;
  };
  # Open the firewall within the riseup network namespace.
  services.netns.namespaces.${netns}.nftables = ''
    table inet filter {
      chain input {
        meta l4proto { tcp, udp } th dport ${toString transmission.settings.peer-port} counter accept comment "Transmission"
      }
      chain output {
        meta skuid ${transmission.user} counter accept comment "Transmission"
      }
    }
  '';
}
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

@ju1m
Copy link
Contributor Author

ju1m commented Sep 10, 2021

  • Add support for multiple remotes, using settings.remote = [ ... ].
  • Minor cleanups.

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/585

@ju1m
Copy link
Contributor Author

ju1m commented Sep 16, 2021

  • Alias services.openvpn.server.${server}.{up,down} to services.openvpn.server.${server}.settings.{up,down} using mkAliasDefinitions.
  • Add services.openvpn.server.${server}.enable option, true by default, to disable a server without commenting it out.

@ju1m
Copy link
Contributor Author

ju1m commented Nov 28, 2021

  • Enable the use of PrivateNetwork=true and JoinsNetworkNamespace="netns-${netns}.service" instead of only NetworkNamespacePath="/var/run/netns/${netns}", by letting systemd create the network namespace and then registering it to iproute for convenience.
  • Add documentation, especially about the need to use BindReadOnlyPaths = ["/etc/netns/${netns}/resolv.conf:/etc/resolv.conf"] in each service joining the network namespace.
  • Make sure openvpn's up script only truncates /etc/netns/${netns}/resolv.conf instead of removing it.

@ju1m
Copy link
Contributor Author

ju1m commented Nov 28, 2021

@cladmi, seeing that you've written https://github.com/cladmi/openvpn-netns, you may be interested by this PR :)

@ju1m
Copy link
Contributor Author

ju1m commented Nov 28, 2021

  • Rebase upon a recent Nixpkgs.

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jun 12, 2022
@ju1m ju1m force-pushed the openvpn branch 2 times, most recently from ac97b66 to df65a81 Compare August 21, 2022 08:17
@ju1m
Copy link
Contributor Author

ju1m commented Aug 21, 2022

@ju1m ju1m force-pushed the openvpn branch 2 times, most recently from 4ff2305 to 7cd3789 Compare October 10, 2022 00:28
@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Oct 10, 2022
@ju1m
Copy link
Contributor Author

ju1m commented Oct 10, 2022

  • Rebase upon current master.
  • Fix missing mdDoc now required.

@ju1m
Copy link
Contributor Author

ju1m commented Oct 20, 2022

  • Fix wrong mdDoc in systemd.services."netns-${name}".description.

@ju1m
Copy link
Contributor Author

ju1m commented Jun 8, 2023

Rebased onto master.

@ju1m ju1m marked this pull request as ready for review June 8, 2023 21:25
@ju1m
Copy link
Contributor Author

ju1m commented Feb 1, 2024

  • Fix ip netns exec by using PrivateMounts=false. See inlined comment.

@ju1m
Copy link
Contributor Author

ju1m commented Feb 1, 2024

  • Improve comment about netns persistance and sharing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants