Unverified Commit f6727c3f authored by Adam C. Stephens's avatar Adam C. Stephens Committed by GitHub
Browse files

lxc: added option for unprivileged containers (#310088)

parents d11c77df 389de87a
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ in
          '';
      };

    unprivilegedContainers = lib.mkEnableOption "support for unprivileged users to launch containers";

    systemConfig =
      lib.mkOption {
        type = lib.types.lines;
@@ -53,6 +55,15 @@ in
            administration access in LXC. See {manpage}`lxc-usernet(5)`.
          '';
      };

      bridgeConfig =
        lib.mkOption {
          type = lib.types.lines;
          default = "";
          description = ''
              This is the config file for override lxc-net bridge default settings.
            '';
        };
  };

  ###### implementation
@@ -62,6 +73,8 @@ in
    environment.etc."lxc/lxc.conf".text = cfg.systemConfig;
    environment.etc."lxc/lxc-usernet".text = cfg.usernetConfig;
    environment.etc."lxc/default.conf".text = cfg.defaultConfig;
    environment.etc."lxc/lxc-net".text = cfg.bridgeConfig;
    environment.pathsToLink = [ "/share/lxc" ];
    systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];

    security.apparmor.packages = [ cfg.package ];
@@ -73,5 +86,30 @@ in
        include ${cfg.package}/etc/apparmor.d/lxc-containers
      '';
    };

    # We don't need the `lxc-user` group, unless the unprivileged containers are enabled.
    users.groups = lib.mkIf cfg.unprivilegedContainers { lxc-user = {}; };

    # `lxc-user-nic` needs suid to attach to bridge for unpriv containers.
    security.wrappers = lib.mkIf cfg.unprivilegedContainers {
      lxcUserNet = {
        source = "${pkgs.lxc}/libexec/lxc/lxc-user-nic";
        setuid = true;
        owner = "root";
        group = "lxc-user";
        program = "lxc-user-nic";
        permissions = "u+rx,g+x,o-rx";
      };
    };

    # Add lxc-net service if unpriv mode is enabled.
    systemd.packages = lib.mkIf cfg.unprivilegedContainers [ pkgs.lxc ];
    systemd.services = lib.mkIf cfg.unprivilegedContainers {
      lxc-net = {
        enable = true;
        wantedBy = [ "multi-user.target" ];
        path = [ pkgs.iproute2 pkgs.iptables pkgs.getent pkgs.dnsmasq ];
      };
    };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -539,6 +539,7 @@ in {
  loki = handleTest ./loki.nix {};
  luks = handleTest ./luks.nix {};
  lvm2 = handleTest ./lvm2 {};
  lxc = handleTest ./lxc {};
  lxd = pkgs.recurseIntoAttrs (handleTest ./lxd { inherit handleTestOn; });
  lxd-image-server = handleTest ./lxd-image-server.nix {};
  #logstash = handleTest ./logstash.nix {};
+3 −3
Original line number Diff line number Diff line
@@ -11,8 +11,8 @@ let
    extra;
  };

  container-image-metadata = releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system};
  container-image-rootfs = releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system};
  container-image-metadata = "${releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}}/tarball/nixos-system-${pkgs.stdenv.hostPlatform.system}.tar.xz";
  container-image-rootfs = "${releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}}/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
in
{
  inherit name;
@@ -61,7 +61,7 @@ in
    machine.succeed("incus admin init --minimal")

    with subtest("Container image can be imported"):
        machine.succeed("incus image import ${container-image-metadata}/*/*.tar.xz ${container-image-rootfs} --alias nixos")
        machine.succeed("incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos")

    with subtest("Container can be launched and managed"):
        machine.succeed("incus launch nixos container")
+7 −3
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@ import ../make-test-python.nix (
      };
    };

    container-image-metadata = releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system};
    container-image-rootfs = releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system};
    container-image-metadata = "${
      releases.incusContainerMeta.${pkgs.stdenv.hostPlatform.system}
    }/tarball/nixos-system-${pkgs.stdenv.hostPlatform.system}.tar.xz";
    container-image-rootfs = "${
      releases.incusContainerImage.${pkgs.stdenv.hostPlatform.system}
    }/nixos-lxc-image-${pkgs.stdenv.hostPlatform.system}.squashfs";
  in
  {
    name = "incusd-options";
@@ -87,7 +91,7 @@ import ../make-test-python.nix (
      machine.wait_for_unit("incus-preseed.service")

      with subtest("Container image can be imported"):
          machine.succeed("incus image import ${container-image-metadata}/*/*.tar.xz ${container-image-rootfs} --alias nixos")
          machine.succeed("incus image import ${container-image-metadata} ${container-image-rootfs} --alias nixos")

      with subtest("Container can be launched and managed"):
          machine.succeed("incus launch nixos container")
+124 −0
Original line number Diff line number Diff line
import ../make-test-python.nix (
  { pkgs, lib, ... }:

  let
    releases = import ../../release.nix {
      configuration = {
        # Building documentation makes the test unnecessarily take a longer time:
        documentation.enable = lib.mkForce false;
      };
    };

    lxc-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system};
    lxc-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system};

  in
  {
    name = "lxc-container-unprivileged";

    meta = {
      maintainers = lib.teams.lxc.members;
    };

    nodes.machine =
      { lib, pkgs, ... }:
      {
        virtualisation = {
          diskSize = 6144;
          cores = 2;
          memorySize = 512;
          writableStore = true;

          lxc = {
            enable = true;
            unprivilegedContainers = true;
            systemConfig = ''
              lxc.lxcpath = /tmp/lxc
            '';
            defaultConfig = ''
              lxc.net.0.type = veth
              lxc.net.0.link = lxcbr0
              lxc.net.0.flags = up
              lxc.net.0.hwaddr = 00:16:3e:xx:xx:xx
              lxc.idmap = u 0 100000 65536
              lxc.idmap = g 0 100000 65536
            '';
            # Permit user alice to connect to bridge
            usernetConfig = ''
              @lxc-user veth lxcbr0 10
            '';
            bridgeConfig = ''
              LXC_IPV6_ADDR=""
              LXC_IPV6_MASK=""
              LXC_IPV6_NETWORK=""
              LXC_IPV6_NAT="false"
            '';
          };
        };

        # Needed for lxc
        environment.systemPackages = with pkgs; [
          pkgs.wget
          pkgs.dnsmasq
        ];

        # Create user for test
        users.users.alice = {
          isNormalUser = true;
          password = "test";
          description = "Lxc unprivileged user with access to lxcbr0";
          extraGroups = [ "lxc-user" ];
          subGidRanges = [
            {
              startGid = 100000;
              count = 65536;
            }
          ];
          subUidRanges = [
            {
              startUid = 100000;
              count = 65536;
            }
          ];
        };

        users.users.bob = {
          isNormalUser = true;
          password = "test";
          description = "Lxc unprivileged user without access to lxcbr0";
          subGidRanges = [
            {
              startGid = 100000;
              count = 65536;
            }
          ];
          subUidRanges = [
            {
              startUid = 100000;
              count = 65536;
            }
          ];
        };
      };

    testScript = ''
      machine.wait_for_unit("lxc-net.service")

      # Copy config files for alice
      machine.execute("su -- alice -c 'mkdir -p ~/.config/lxc'")
      machine.execute("su -- alice -c 'cp /etc/lxc/default.conf ~/.config/lxc/'")
      machine.execute("su -- alice -c 'cp /etc/lxc/lxc.conf ~/.config/lxc/'")

      machine.succeed("su -- alice -c 'lxc-create -t local -n test -- --metadata ${lxc-image-metadata}/*/*.tar.xz --fstree ${lxc-image-rootfs}/*/*.tar.xz'")
      machine.succeed("su -- alice -c 'lxc-start test'")
      machine.succeed("su -- alice -c 'lxc-stop test'")

      # Copy config files for bob
      machine.execute("su -- bob -c 'mkdir -p ~/.config/lxc'")
      machine.execute("su -- bob -c 'cp /etc/lxc/default.conf ~/.config/lxc/'")
      machine.execute("su -- bob -c 'cp /etc/lxc/lxc.conf ~/.config/lxc/'")

      machine.fail("su -- bob -c 'lxc-start test'")
    '';
  }
)
Loading