diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index 3adf4530aeec7..61593dc234ab7 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -295,28 +295,19 @@ let ''; }; - capAdd = mkOption { + capabilities = mkOption { type = with types; lazyAttrsOf (nullOr bool); default = { }; description = '' - Capabilities to add to container - ''; - example = literalExpression '' - { - SYS_ADMIN = true; - { - ''; - }; - - capDrop = mkOption { - type = with types; lazyAttrsOf (nullOr bool); - default = { }; - description = '' - Capabilities to drop from container + Capabilities to configure for the container. + When set to true, capability is added to the container. + When set to false, capability is dropped from the container. + When null, default runtime settings apply. ''; example = literalExpression '' { SYS_ADMIN = true; + SYS_WRITE = false; { ''; }; @@ -441,10 +432,10 @@ let ++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}" ++ optional (container.privileged) "--privileged" ++ mapAttrsToList (k: _: "--cap-add=${escapeShellArg k}") ( - filterAttrs (_: v: v == true) container.capAdd + filterAttrs (_: v: v == true) container.capabilities ) ++ mapAttrsToList (k: _: "--cap-drop=${escapeShellArg k}") ( - filterAttrs (_: v: v == true) container.capDrop + filterAttrs (_: v: v == false) container.capabilities ) ++ map (d: "--device=${escapeShellArg d}") container.devices ++ map (n: "--network=${escapeShellArg n}") container.networks diff --git a/nixos/tests/oci-containers.nix b/nixos/tests/oci-containers.nix index 09075c20d79bd..ed83446b44b9c 100644 --- a/nixos/tests/oci-containers.nix +++ b/nixos/tests/oci-containers.nix @@ -1,64 +1,70 @@ -{ system ? builtins.currentSystem -, config ? {} -, pkgs ? import ../.. { inherit system config; } -, lib ? pkgs.lib +{ + system ? builtins.currentSystem, + config ? { }, + pkgs ? import ../.. { inherit system config; }, + lib ? pkgs.lib, }: let inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; - mkOCITest = backend: makeTest { - name = "oci-containers-${backend}"; + mkOCITest = + backend: + makeTest { + name = "oci-containers-${backend}"; - meta.maintainers = lib.teams.serokell.members - ++ (with lib.maintainers; [ benley ]); + meta.maintainers = lib.teams.serokell.members ++ (with lib.maintainers; [ benley ]); - nodes = { - ${backend} = { pkgs, ... }: { - virtualisation.oci-containers = { - inherit backend; - containers.nginx = { - image = "nginx-container"; - imageStream = pkgs.dockerTools.examples.nginxStream; - ports = ["8181:80"]; - capAdd = { - CAP_AUDIT_READ = true; + nodes = { + ${backend} = + { pkgs, ... }: + { + virtualisation.oci-containers = { + inherit backend; + containers.nginx = { + image = "nginx-container"; + imageStream = pkgs.dockerTools.examples.nginxStream; + ports = [ "8181:80" ]; + capabilities = { + CAP_AUDIT_READ = true; + CAP_AUDIT_WRITE = false; + }; + privileged = false; + devices = [ + "/dev/random:/dev/random" + ]; + }; }; - capDrop = { - CAP_AUDIT_WRITE = true; - }; - privileged = false; - devices = [ - "/dev/random:/dev/random" - ]; - }; - }; - # Stop systemd from killing remaining processes if ExecStop script - # doesn't work, so that proper stopping can be tested. - systemd.services."${backend}-nginx".serviceConfig.KillSignal = "SIGCONT"; + # Stop systemd from killing remaining processes if ExecStop script + # doesn't work, so that proper stopping can be tested. + systemd.services."${backend}-nginx".serviceConfig.KillSignal = "SIGCONT"; + }; }; - }; - testScript = '' - import json + testScript = '' + import json - start_all() - ${backend}.wait_for_unit("${backend}-nginx.service") - ${backend}.wait_for_open_port(8181) - ${backend}.wait_until_succeeds("curl -f http://localhost:8181 | grep Hello") - output = json.loads(${backend}.succeed("${backend} inspect nginx --format json").strip())[0] - ${backend}.succeed("systemctl stop ${backend}-nginx.service", timeout=10) - assert output['HostConfig']['CapAdd'] == ["CAP_AUDIT_READ"] - assert output['HostConfig']['CapDrop'] == ${if backend == "docker" then "[\"CAP_AUDIT_WRITE\"]" else "[]"} # Rootless podman runs with no capabilities so it cannot drop them - assert output['HostConfig']['Privileged'] == False - assert output['HostConfig']['Devices'] == [{'PathOnHost': '/dev/random', 'PathInContainer': '/dev/random', 'CgroupPermissions': '${if backend == "docker" then "rwm" else ""}'}] - ''; - }; + start_all() + ${backend}.wait_for_unit("${backend}-nginx.service") + ${backend}.wait_for_open_port(8181) + ${backend}.wait_until_succeeds("curl -f http://localhost:8181 | grep Hello") + output = json.loads(${backend}.succeed("${backend} inspect nginx --format json").strip())[0] + ${backend}.succeed("systemctl stop ${backend}-nginx.service", timeout=10) + assert output['HostConfig']['CapAdd'] == ["CAP_AUDIT_READ"] + assert output['HostConfig']['CapDrop'] == ${ + if backend == "docker" then "[\"CAP_AUDIT_WRITE\"]" else "[]" + } # Rootless podman runs with no capabilities so it cannot drop them + assert output['HostConfig']['Privileged'] == False + assert output['HostConfig']['Devices'] == [{'PathOnHost': '/dev/random', 'PathInContainer': '/dev/random', 'CgroupPermissions': '${ + if backend == "docker" then "rwm" else "" + }'}] + ''; + }; in -lib.foldl' (attrs: backend: attrs // { ${backend} = mkOCITest backend; }) {} [ +lib.foldl' (attrs: backend: attrs // { ${backend} = mkOCITest backend; }) { } [ "docker" "podman" ]