From 336ef2de99197dd9c07b302685dc9e6282fa5b55 Mon Sep 17 00:00:00 2001 From: Patryk Wychowaniec Date: Fri, 26 Feb 2021 17:14:08 +0100 Subject: [PATCH] nixos/containers: allow containers with long names to create private networks Launching a container with a private network requires creating a dedicated networking interface for it; name of that interface is derived from the container name itself - e.g. a container named `foo` gets attached to an interface named `ve-foo`. An interface name can span up to IFNAMSIZ characters, which means that a container name must contain at most IFNAMSIZ - 3 - 1 = 11 characters; it's a limit that we validate using a build-time assertion. This limit has been upgraded with Linux 5.8, as it allows for an interface to contain a so-called altname, which can be much longer, while remaining treated as a first-class citizen. Since altnames have been supported natively by systemd for a while now, due diligence on our side ends with dropping the name-assertion on newer kernels. This commit closes #38509. systemd/systemd#14467 systemd/systemd#17220 https://lwn.net/Articles/794289/ --- .../virtualisation/nixos-containers.nix | 12 ++++-- nixos/tests/all-tests.nix | 1 + nixos/tests/containers-names.nix | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 nixos/tests/containers-names.nix diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix index f06977f88fc1..3754fe6dac6d 100644 --- a/nixos/modules/virtualisation/nixos-containers.nix +++ b/nixos/modules/virtualisation/nixos-containers.nix @@ -271,8 +271,8 @@ let DeviceAllow = map (d: "${d.node} ${d.modifier}") cfg.allowedDevices; }; - system = config.nixpkgs.localSystem.system; + kernelVersion = config.boot.kernelPackages.kernel.version; bindMountOpts = { name, ... }: { @@ -321,7 +321,6 @@ let }; }; - mkBindFlag = d: let flagPrefix = if d.isReadOnly then " --bind-ro=" else " --bind="; mountstr = if d.hostPath != null then "${d.hostPath}:${d.mountPoint}" else "${d.mountPoint}"; @@ -482,11 +481,16 @@ in networking.useDHCP = false; assertions = [ { - assertion = config.privateNetwork -> stringLength name < 12; + assertion = + (builtins.compareVersions kernelVersion "5.8" <= 0) + -> config.privateNetwork + -> stringLength name <= 11; message = '' Container name `${name}` is too long: When `privateNetwork` is enabled, container names can not be longer than 11 characters, because the container's interface name is derived from it. - This might be fixed in the future. See https://github.com/NixOS/nixpkgs/issues/38509 + You should either make the container name shorter or upgrade to a more recent kernel that + supports interface altnames (i.e. at least Linux 5.8 - please see https://github.com/NixOS/nixpkgs/issues/38509 + for details). ''; } ]; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index bf094dbe9848..02723f88c315 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -72,6 +72,7 @@ in containers-imperative = handleTest ./containers-imperative.nix {}; containers-ip = handleTest ./containers-ip.nix {}; containers-macvlans = handleTest ./containers-macvlans.nix {}; + containers-names = handleTest ./containers-names.nix {}; containers-physical_interfaces = handleTest ./containers-physical_interfaces.nix {}; containers-portforward = handleTest ./containers-portforward.nix {}; containers-reloadable = handleTest ./containers-reloadable.nix {}; diff --git a/nixos/tests/containers-names.nix b/nixos/tests/containers-names.nix new file mode 100644 index 000000000000..9ad2bfb748a8 --- /dev/null +++ b/nixos/tests/containers-names.nix @@ -0,0 +1,37 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "containers-names"; + meta = { + maintainers = with lib.maintainers; [ patryk27 ]; + }; + + machine = { ... }: { + # We're using the newest kernel, so that we can test containers with long names. + # Please see https://github.com/NixOS/nixpkgs/issues/38509 for details. + boot.kernelPackages = pkgs.linuxPackages_latest; + + containers = let + container = subnet: { + autoStart = true; + privateNetwork = true; + hostAddress = "192.168.${subnet}.1"; + localAddress = "192.168.${subnet}.2"; + config = { }; + }; + + in { + first = container "1"; + second = container "2"; + really-long-name = container "3"; + really-long-long-name-2 = container "4"; + }; + }; + + testScript = '' + machine.wait_for_unit("default.target") + + machine.succeed("ip link show | grep ve-first") + machine.succeed("ip link show | grep ve-second") + machine.succeed("ip link show | grep ve-really-lFYWO") + machine.succeed("ip link show | grep ve-really-l3QgY") + ''; +})