oci-containers: consolidate capabilities interface (#363574)

* oci-containers: consolidate capabilities interface

* Update nixos/modules/virtualisation/oci-containers.nix

Improved wording

Co-authored-by: Benjamin Staffin <benley@zoiks.net>

---------

Co-authored-by: Benjamin Staffin <benley@zoiks.net>
This commit is contained in:
Yethal 2024-12-12 17:50:13 +01:00 committed by GitHub
parent d011002802
commit 91bb1c6d75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 64 deletions

View File

@ -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

View File

@ -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
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 ""
}'}]
'';
};
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 ""}'}]
'';
};
in
lib.foldl' (attrs: backend: attrs // { ${backend} = mkOCITest backend; }) {} [
lib.foldl' (attrs: backend: attrs // { ${backend} = mkOCITest backend; }) { } [
"docker"
"podman"
]