From 0edccac40c2bf019ab689a1c5affb03d362c0420 Mon Sep 17 00:00:00 2001 From: bad Date: Wed, 12 Jan 2022 00:43:50 +0100 Subject: [PATCH 01/10] virtualisation/podman: init This is mostly a copy of the nixos module with some small changes to make it work for HM. --- modules/modules.nix | 3 + modules/virtualisation/containers.nix | 124 ++++++++++++ .../virtualisation/podman/podman-dnsname.nix | 32 +++ modules/virtualisation/podman/podman.nix | 183 ++++++++++++++++++ tests/default.nix | 1 + 5 files changed, 343 insertions(+) create mode 100644 modules/virtualisation/containers.nix create mode 100644 modules/virtualisation/podman/podman-dnsname.nix create mode 100644 modules/virtualisation/podman/podman.nix diff --git a/modules/modules.nix b/modules/modules.nix index 2b2f7879051a..ab4b33d94107 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -251,6 +251,9 @@ let ./xcursor.nix ./xresources.nix ./xsession.nix + ./virtualisation/containers.nix + ./virtualisation/podman/podman.nix + ./virtualisation/podman/podman-dnsname.nix (pkgs.path + "/nixos/modules/misc/assertions.nix") (pkgs.path + "/nixos/modules/misc/meta.nix") ] ++ optional useNixpkgsModule ./misc/nixpkgs.nix diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix new file mode 100644 index 000000000000..c2f682fae07d --- /dev/null +++ b/modules/virtualisation/containers.nix @@ -0,0 +1,124 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.virtualisation.containers; + + inherit (lib) mkOption types; + + toml = pkgs.formats.toml { }; +in { + options.virtualisation.containers = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + This option enables the common containers configuration module. + ''; + }; + + ociSeccompBpfHook.enable = mkOption { + type = types.bool; + default = false; + description = "Enable the OCI seccomp BPF hook"; + }; + + containersConf.settings = mkOption { + type = toml.type; + default = { }; + description = "containers.conf configuration"; + }; + + containersConf.cniPlugins = mkOption { + type = types.listOf types.package; + defaultText = '' + [ + pkgs.cni-plugins + ] + ''; + example = lib.literalExample '' + [ + pkgs.cniPlugins.dnsname + ] + ''; + description = '' + CNI plugins to install on the system. + ''; + }; + + registries = { + search = mkOption { + type = types.listOf types.str; + default = [ "docker.io" "quay.io" ]; + description = '' + List of repositories to search. + ''; + }; + + insecure = mkOption { + default = [ ]; + type = types.listOf types.str; + description = '' + List of insecure repositories. + ''; + }; + + block = mkOption { + default = [ ]; + type = types.listOf types.str; + description = '' + List of blocked repositories. + ''; + }; + }; + + policy = mkOption { + default = { }; + type = types.attrs; + example = lib.literalExample '' + { + default = [ { type = "insecureAcceptAnything"; } ]; + transports = { + docker-daemon = { + "" = [ { type = "insecureAcceptAnything"; } ]; + }; + }; + } + ''; + description = '' + Signature verification policy file. + If this option is empty the default policy file from + skopeo will be used. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + + virtualisation.containers.containersConf.cniPlugins = [ pkgs.cni-plugins ]; + + virtualisation.containers.containersConf.settings = { + network.cni_plugin_dirs = + map (p: "${lib.getBin p}/bin") cfg.containersConf.cniPlugins; + engine = { + init_path = "${pkgs.catatonit}/bin/catatonit"; + } // lib.optionalAttrs cfg.ociSeccompBpfHook.enable { + hooks_dir = [ config.boot.kernelPackages.oci-seccomp-bpf-hook ]; + }; + }; + + xdg.configFile."containers/containers.conf".source = + toml.generate "containers.conf" cfg.containersConf.settings; + + xdg.configFile."containers/registries.conf".source = + toml.generate "registries.conf" { + registries = lib.mapAttrs (n: v: { registries = v; }) cfg.registries; + }; + + xdg.configFile."containers/policy.json".source = if cfg.policy != { } then + pkgs.writeText "policy.json" (builtins.toJSON cfg.policy) + else + "${pkgs.skopeo.src}/default-policy.json"; + }; + +} diff --git a/modules/virtualisation/podman/podman-dnsname.nix b/modules/virtualisation/podman/podman-dnsname.nix new file mode 100644 index 000000000000..a721a2f676f7 --- /dev/null +++ b/modules/virtualisation/podman/podman-dnsname.nix @@ -0,0 +1,32 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mkOption mkIf types; + + cfg = config.services.podman; + +in { + options = { + services.podman = { + + defaultNetwork.dnsname.enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable DNS resolution in the default podman network. + ''; + }; + + }; + }; + + config = { + virtualisation.containers.containersConf.cniPlugins = + mkIf cfg.defaultNetwork.dnsname.enable [ pkgs.dnsname-cni ]; + services.podman.defaultNetwork.extraPlugins = + lib.optional cfg.defaultNetwork.dnsname.enable { + type = "dnsname"; + domainName = "dns.podman"; + capabilities.aliases = true; + }; + }; +} diff --git a/modules/virtualisation/podman/podman.nix b/modules/virtualisation/podman/podman.nix new file mode 100644 index 000000000000..96c745419f23 --- /dev/null +++ b/modules/virtualisation/podman/podman.nix @@ -0,0 +1,183 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.podman; + toml = pkgs.formats.toml { }; + json = pkgs.formats.json { }; + + inherit (lib) mkOption types; + + podmanPackage = (pkgs.podman.override { inherit (cfg) extraPackages; }); + + # Provides a fake "docker" binary mapping to podman + dockerCompat = pkgs.runCommandNoCC + "${podmanPackage.pname}-docker-compat-${podmanPackage.version}" { + outputs = [ "out" "man" ]; + inherit (podmanPackage) meta; + } '' + mkdir -p $out/bin + ln -s ${podmanPackage}/bin/podman $out/bin/docker + + mkdir -p $man/share/man/man1 + for f in ${podmanPackage.man}/share/man/man1/*; do + basename=$(basename $f | sed s/podman/docker/g) + ln -s $f $man/share/man/man1/$basename + done + ''; + + net-conflist = pkgs.runCommand "87-podman-bridge.conflist" { + nativeBuildInputs = [ pkgs.jq ]; + extraPlugins = builtins.toJSON cfg.defaultNetwork.extraPlugins; + jqScript = '' + . + { "plugins": (.plugins + $extraPlugins) } + ''; + } '' + jq <${cfg.package}/etc/cni/net.d/87-podman-bridge.conflist \ + --argjson extraPlugins "$extraPlugins" \ + "$jqScript" \ + >$out + ''; + +in { + imports = [ + ./podman-dnsname.nix + #./podman-network-socket.nix + (lib.mkRenamedOptionModule [ "virtualisation" "podman" "libpod" ] [ + "virtualisation" + "containers" + "containersConf" + ]) + ]; + + options.services.podman = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + This option enables Podman, a daemonless container engine for + developing, managing, and running OCI Containers on your Linux System. + + It is a drop-in replacement for the docker command. + ''; + }; + + dockerSocket = mkOption { + type = types.bool; + default = false; + description = '' + Make the Podman socket available in place of the Docker socket, so + Docker tools can find the Podman socket. + + Podman implements the Docker API. + ''; + }; + + dockerCompat = mkOption { + type = types.bool; + default = false; + description = '' + Create an alias mapping docker to podman. + ''; + }; + + enableNvidia = mkOption { + type = types.bool; + default = false; + description = '' + Enable use of NVidia GPUs from within podman containers. + ''; + }; + + extraPackages = mkOption { + type = with types; listOf package; + default = [ ]; + example = lib.literalExample '' + [ + pkgs.gvisor + ] + ''; + description = '' + Extra packages to be installed in the Podman wrapper. + ''; + }; + + package = lib.mkOption { + type = types.package; + default = podmanPackage; + internal = true; + description = '' + The final Podman package (including extra packages). + ''; + }; + + defaultNetwork.extraPlugins = lib.mkOption { + type = types.listOf json.type; + default = [ ]; + description = '' + Extra CNI plugin configurations to add to podman's default network. + ''; + }; + + }; + + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + home.packages = [ cfg.package ] + ++ lib.optional cfg.dockerCompat dockerCompat; + + xdg.configFile."cni/net.d/87-podman-bridge.conflist".source = + net-conflist; + + virtualisation.containers = { + enable = true; # Enable common /etc/containers configuration + containersConf.settings = lib.optionalAttrs cfg.enableNvidia { + engine = { + conmon_env_vars = + [ "PATH=${lib.makeBinPath [ pkgs.nvidia-podman ]}" ]; + runtimes.nvidia = + [ "${pkgs.nvidia-podman}/bin/nvidia-container-runtime" ]; + }; + }; + }; + + systemd.user = { + + services.podman = { + Unit = { + Description = "Podman API Service"; + Requires = "podman.socket"; + After = "podman.socket"; + Documentation = "man:podman-system-service(1)"; + StartLimitIntervalSec = 0; + }; + Service = { + Type = "exec"; + KillMode = "process"; + Environment = ''LOGGING=" --log-level=info"''; + ExecStart = + [ "" "${cfg.package}/bin/podman $LOGGING system service" ]; + }; + + Install = { WantedBy = [ "multi-user.target" ]; }; + }; + + sockets.podman = { + Unit = { + Description = "Podman API Socket"; + Documentation = "man:podman-system-service(1)"; + }; + Socket = { + ListenStream = "%t/podman/podman.sock"; + SocketMode = 660; + }; + Install.WantedBy = [ "sockets.target" ]; + }; + + }; + } + (lib.mkIf cfg.dockerSocket { + home.sessionVariables."DOCKER_HOST" = + "unix:///run/user/$UID/podman/podman.sock"; + }) + ]); +} diff --git a/tests/default.nix b/tests/default.nix index 89361a29c31e..299d07c94abd 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -160,6 +160,7 @@ import nmt { ./modules/services/window-managers/sway ./modules/services/wlsunset ./modules/services/xsettingsd + ./modules/virtualisation/podman ./modules/systemd ./modules/targets-linux ]); From 79a0b36eb547efc1b50aa669c3eb56bfdf079c05 Mon Sep 17 00:00:00 2001 From: bad Date: Thu, 13 Jan 2022 17:17:54 +0100 Subject: [PATCH 02/10] virtualisation/podman: add tests Adds basic tests to verify that the podman module generates the right configs --- .../virtualisation/podman/basic-configuration.nix | 12 ++++++++++++ tests/modules/virtualisation/podman/default.nix | 4 ++++ .../virtualisation/podman/docker-compat.nix | 15 +++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 tests/modules/virtualisation/podman/basic-configuration.nix create mode 100644 tests/modules/virtualisation/podman/default.nix create mode 100644 tests/modules/virtualisation/podman/docker-compat.nix diff --git a/tests/modules/virtualisation/podman/basic-configuration.nix b/tests/modules/virtualisation/podman/basic-configuration.nix new file mode 100644 index 000000000000..02110a08e3a2 --- /dev/null +++ b/tests/modules/virtualisation/podman/basic-configuration.nix @@ -0,0 +1,12 @@ +{ config, pkgs, lib, ... }: + +{ + config = { + services.podman.enable = true; + nmt.script = '' + servicePath=home-files/.config/systemd/user + + assertFileExists $servicePath/podman.service $servicePath/podman.socket + ''; + }; +} diff --git a/tests/modules/virtualisation/podman/default.nix b/tests/modules/virtualisation/podman/default.nix new file mode 100644 index 000000000000..bde968325643 --- /dev/null +++ b/tests/modules/virtualisation/podman/default.nix @@ -0,0 +1,4 @@ +{ + podman-basic-configuration = ./basic-configuration.nix; + podman-docker-compat = ./docker-compat.nix; +} diff --git a/tests/modules/virtualisation/podman/docker-compat.nix b/tests/modules/virtualisation/podman/docker-compat.nix new file mode 100644 index 000000000000..b713104db4db --- /dev/null +++ b/tests/modules/virtualisation/podman/docker-compat.nix @@ -0,0 +1,15 @@ +{ config, pkgs, lib, ... }: + +{ + config = { + services.podman = { + enable = true; + dockerCompat = true; + dockerSocket = true; + }; + nmt.script = '' + assertFileIsExecutable home-path/bin/docker + assertFileContains home-path/etc/profile.d/hm-session-vars.sh "DOCKER_HOST" + ''; + }; +} From cf91dce162a0ba581dc4bade2b3c9f554ba9a6fb Mon Sep 17 00:00:00 2001 From: bad Date: Thu, 13 Jan 2022 17:25:11 +0100 Subject: [PATCH 03/10] podman: add myself as the maintainer --- .github/CODEOWNERS | 3 +++ modules/virtualisation/containers.nix | 1 + modules/virtualisation/podman/podman-dnsname.nix | 1 + modules/virtualisation/podman/podman.nix | 1 + 4 files changed, 6 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1241b106cde2..b7190124f3bc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -398,6 +398,9 @@ /modules/services/systembus-notify.nix @asymmetric +/modules/virtualisation/podman.nix @MaeIsBad +/modules/virtualisation/containers.nix @MaeIsBad + /modules/targets/darwin @midchildan /tests/modules/targets-darwin @midchildan diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index c2f682fae07d..ae00a782445b 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -6,6 +6,7 @@ let toml = pkgs.formats.toml { }; in { + meta.maintainers = [ lib.maintainers.MaeIsBad ]; options.virtualisation.containers = { enable = mkOption { diff --git a/modules/virtualisation/podman/podman-dnsname.nix b/modules/virtualisation/podman/podman-dnsname.nix index a721a2f676f7..f9000ed5cbfd 100644 --- a/modules/virtualisation/podman/podman-dnsname.nix +++ b/modules/virtualisation/podman/podman-dnsname.nix @@ -5,6 +5,7 @@ let cfg = config.services.podman; in { + meta.maintainers = [ lib.maintainers.MaeIsBad ]; options = { services.podman = { diff --git a/modules/virtualisation/podman/podman.nix b/modules/virtualisation/podman/podman.nix index 96c745419f23..9a81d3b70007 100644 --- a/modules/virtualisation/podman/podman.nix +++ b/modules/virtualisation/podman/podman.nix @@ -38,6 +38,7 @@ let ''; in { + meta.maintainers = [ lib.maintainers.MaeIsBad ]; imports = [ ./podman-dnsname.nix #./podman-network-socket.nix From e00415d8c60ddc0ae72063091b208c1ca833b23e Mon Sep 17 00:00:00 2001 From: bad Date: Thu, 13 Jan 2022 17:26:07 +0100 Subject: [PATCH 04/10] podman: format code --- tests/modules/virtualisation/podman/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/modules/virtualisation/podman/default.nix b/tests/modules/virtualisation/podman/default.nix index bde968325643..913dfb6ceca7 100644 --- a/tests/modules/virtualisation/podman/default.nix +++ b/tests/modules/virtualisation/podman/default.nix @@ -1,4 +1,4 @@ -{ - podman-basic-configuration = ./basic-configuration.nix; +{ + podman-basic-configuration = ./basic-configuration.nix; podman-docker-compat = ./docker-compat.nix; } From aac05807955f1b1288fc4c91fa6b10f5424f14dd Mon Sep 17 00:00:00 2001 From: bad Date: Thu, 13 Jan 2022 21:44:31 +0100 Subject: [PATCH 05/10] Add maintainers entry for MaeIsBad --- modules/lib/maintainers.nix | 6 ++++++ modules/virtualisation/containers.nix | 2 +- modules/virtualisation/podman/podman-dnsname.nix | 2 +- modules/virtualisation/podman/podman.nix | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix index 105edf07381a..3fdd11147d4f 100644 --- a/modules/lib/maintainers.nix +++ b/modules/lib/maintainers.nix @@ -169,4 +169,10 @@ github = "pltanton"; githubId = 4561823; }; + bad = { + name = "Mae Lib"; + email = "git@badat.dev"; + github = "MaeIsBad"; + githubId = 26093674; + }; } diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index ae00a782445b..b0305fc48c76 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -6,7 +6,7 @@ let toml = pkgs.formats.toml { }; in { - meta.maintainers = [ lib.maintainers.MaeIsBad ]; + meta.maintainers = [ lib.maintainers.bad ]; options.virtualisation.containers = { enable = mkOption { diff --git a/modules/virtualisation/podman/podman-dnsname.nix b/modules/virtualisation/podman/podman-dnsname.nix index f9000ed5cbfd..d3648a8cd699 100644 --- a/modules/virtualisation/podman/podman-dnsname.nix +++ b/modules/virtualisation/podman/podman-dnsname.nix @@ -5,7 +5,7 @@ let cfg = config.services.podman; in { - meta.maintainers = [ lib.maintainers.MaeIsBad ]; + meta.maintainers = [ lib.maintainers.bad ]; options = { services.podman = { diff --git a/modules/virtualisation/podman/podman.nix b/modules/virtualisation/podman/podman.nix index 9a81d3b70007..a3631f42ed40 100644 --- a/modules/virtualisation/podman/podman.nix +++ b/modules/virtualisation/podman/podman.nix @@ -38,7 +38,7 @@ let ''; in { - meta.maintainers = [ lib.maintainers.MaeIsBad ]; + meta.maintainers = [ lib.maintainers.bad ]; imports = [ ./podman-dnsname.nix #./podman-network-socket.nix From 3237e93d4de5afdf7cd0fbc84ecfd426488cb496 Mon Sep 17 00:00:00 2001 From: bad Date: Thu, 13 Jan 2022 21:54:56 +0100 Subject: [PATCH 06/10] Disable the containers module outside of linux --- modules/virtualisation/containers.nix | 5 +++++ modules/virtualisation/podman/podman.nix | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index b0305fc48c76..319ee6ba781b 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -95,6 +95,11 @@ in { }; config = lib.mkIf cfg.enable { + assertions = [ { + # It may be possible to make a similar module for macos but I don't have a mac device to try + assertion = enabled -> pkgs.stdenv.isLinux; + message = "Containers module only works on linux" + } virtualisation.containers.containersConf.cniPlugins = [ pkgs.cni-plugins ]; diff --git a/modules/virtualisation/podman/podman.nix b/modules/virtualisation/podman/podman.nix index a3631f42ed40..d3f362f7d4ed 100644 --- a/modules/virtualisation/podman/podman.nix +++ b/modules/virtualisation/podman/podman.nix @@ -48,7 +48,6 @@ in { "containersConf" ]) ]; - options.services.podman = { enable = mkOption { From 9de86ab1ffa4e211b413f0e77e2d42387bd1869a Mon Sep 17 00:00:00 2001 From: MaeIsBad <26093674+MaeIsBad@users.noreply.github.com> Date: Sat, 12 Mar 2022 23:52:56 +0100 Subject: [PATCH 07/10] Use default.target for the service target Co-authored-by: Nicolas Berbiche --- modules/virtualisation/podman/podman.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/virtualisation/podman/podman.nix b/modules/virtualisation/podman/podman.nix index d3f362f7d4ed..6971cec8ebb9 100644 --- a/modules/virtualisation/podman/podman.nix +++ b/modules/virtualisation/podman/podman.nix @@ -158,7 +158,7 @@ in { [ "" "${cfg.package}/bin/podman $LOGGING system service" ]; }; - Install = { WantedBy = [ "multi-user.target" ]; }; + Install = { WantedBy = [ "default.target" ]; }; }; sockets.podman = { From d4b7326b11efb82be6c5c0e5b2cd9ab78c469ad2 Mon Sep 17 00:00:00 2001 From: MaeIsBad <26093674+MaeIsBad@users.noreply.github.com> Date: Sun, 13 Mar 2022 00:01:15 +0100 Subject: [PATCH 08/10] Use lib.mkEnableOption instead of defining enable options manually Co-authored-by: Nicolas Berbiche --- modules/virtualisation/containers.nix | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index 319ee6ba781b..6984d53e74d3 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -9,19 +9,9 @@ in { meta.maintainers = [ lib.maintainers.bad ]; options.virtualisation.containers = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - This option enables the common containers configuration module. - ''; - }; + enable = lib.mkEnableOption "the common containers configuration module"; - ociSeccompBpfHook.enable = mkOption { - type = types.bool; - default = false; - description = "Enable the OCI seccomp BPF hook"; - }; + ociSeccompBpfHook.enable = lib.mkEnableOption "the OCI seccomp BPF hook"; containersConf.settings = mkOption { type = toml.type; From 906f040dd1704de7e10d902bcc577aaea7e1933a Mon Sep 17 00:00:00 2001 From: MaeIsBad <26093674+MaeIsBad@users.noreply.github.com> Date: Sun, 13 Mar 2022 00:02:11 +0100 Subject: [PATCH 09/10] Change lib.maintainers to lib.hm.maintainers Co-authored-by: Nicolas Berbiche --- modules/virtualisation/containers.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index 6984d53e74d3..3a936400f52e 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -6,7 +6,7 @@ let toml = pkgs.formats.toml { }; in { - meta.maintainers = [ lib.maintainers.bad ]; + meta.maintainers = [ lib.hm.maintainers.bad ]; options.virtualisation.containers = { enable = lib.mkEnableOption "the common containers configuration module"; From 04f424bb147e62c1ed6fb4811f41054d3608ea6e Mon Sep 17 00:00:00 2001 From: MaeIsBad <26093674+MaeIsBad@users.noreply.github.com> Date: Sun, 13 Mar 2022 00:03:32 +0100 Subject: [PATCH 10/10] Use lib.hm.assertions.assertPlatform Co-authored-by: Nicolas Berbiche --- modules/virtualisation/containers.nix | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/virtualisation/containers.nix b/modules/virtualisation/containers.nix index 3a936400f52e..859a75081148 100644 --- a/modules/virtualisation/containers.nix +++ b/modules/virtualisation/containers.nix @@ -85,11 +85,9 @@ in { }; config = lib.mkIf cfg.enable { - assertions = [ { - # It may be possible to make a similar module for macos but I don't have a mac device to try - assertion = enabled -> pkgs.stdenv.isLinux; - message = "Containers module only works on linux" - } + assertions = [ + (lib.hm.assertions.assertPlatform "virtualisation.containers" pkgs lib.platforms.linux) + ]; virtualisation.containers.containersConf.cniPlugins = [ pkgs.cni-plugins ];