Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a podman module #2630

Closed
wants to merge 10 commits into from
Closed
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions modules/lib/maintainers.nix
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,10 @@
github = "pltanton";
githubId = 4561823;
};
bad = {
name = "Mae Lib";
email = "git@badat.dev";
github = "MaeIsBad";
githubId = 26093674;
};
}
3 changes: 3 additions & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
118 changes: 118 additions & 0 deletions modules/virtualisation/containers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{ config, lib, pkgs, ... }:
let
cfg = config.virtualisation.containers;

inherit (lib) mkOption types;

toml = pkgs.formats.toml { };
in {
meta.maintainers = [ lib.hm.maintainers.bad ];
options.virtualisation.containers = {

enable = lib.mkEnableOption "the common containers configuration module";

ociSeccompBpfHook.enable = lib.mkEnableOption "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
]
'';
Comment on lines +22 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
containersConf.cniPlugins = mkOption {
type = types.listOf types.package;
defaultText = ''
[
pkgs.cni-plugins
]
'';
containersConf.cniPlugins = mkOption {
type = types.listOf types.package;
defaultText = lib.literalExpression ''
[
pkgs.cni-plugins
]
'';

The default value is missing.

example = lib.literalExample ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
example = lib.literalExample ''
example = lib.literalExpression ''

[
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use pkgs.formats.json for this?
types.attrs does not allow using mkIf and mkMerge.
It does not support merging settings correctly too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should also be applied to the nixpkgs version of this module

example = lib.literalExample ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
example = lib.literalExample ''
example = lib.literalExpression ''

{
default = [ { type = "insecureAcceptAnything"; } ];
transports = {
docker-daemon = {
"" = [ { type = "insecureAcceptAnything"; } ];
};
};
}
'';
description = ''
Signature verification policy file.
If this option is empty the default policy file from
<literal>skopeo</literal> will be used.
'';
};

};

config = lib.mkIf cfg.enable {
assertions = [
(lib.hm.assertions.assertPlatform "virtualisation.containers" pkgs lib.platforms.linux)
];

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will create a hierarchy of registries.<name>.registries = <value>.
Is this the right hierarchy?

};

xdg.configFile."containers/policy.json".source = if cfg.policy != { } then
pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the pkgs.formats.json instance used for the json policy:

Suggested change
pkgs.writeText "policy.json" (builtins.toJSON cfg.policy)
jsonFormat.generate "policy.json" cfg.policy

else
"${pkgs.skopeo.src}/default-policy.json";
};

}
33 changes: 33 additions & 0 deletions modules/virtualisation/podman/podman-dnsname.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption mkIf types;

cfg = config.services.podman;

in {
meta.maintainers = [ lib.maintainers.bad ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
meta.maintainers = [ lib.maintainers.bad ];
meta.maintainers = [ lib.hm.maintainers.bad ];

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;
};
};
}
183 changes: 183 additions & 0 deletions modules/virtualisation/podman/podman.nix
Original file line number Diff line number Diff line change
@@ -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 {
meta.maintainers = [ lib.maintainers.bad ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
meta.maintainers = [ lib.maintainers.bad ];
meta.maintainers = [ lib.hm.maintainers.bad ];

imports = [
./podman-dnsname.nix
#./podman-network-socket.nix
(lib.mkRenamedOptionModule [ "virtualisation" "podman" "libpod" ] [
"virtualisation"
"containers"
"containersConf"
])
Comment on lines +45 to +49
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need an mkRenamedOptionModule on a new module.

];
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 <command>docker</command> 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 <command>docker</command> to <command>podman</command>.
'';
};

enableNvidia = mkOption {
type = types.bool;
default = false;
description = ''
Enable use of NVidia GPUs from within podman containers.
'';
};
Comment on lines +83 to +89
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
enableNvidia = mkOption {
type = types.bool;
default = false;
description = ''
Enable use of NVidia GPUs from within podman containers.
'';
};
enableNvidia = lib.mkEnableOption "use of NVidia GPUs from within Podman containers";


extraPackages = mkOption {
type = with types; listOf package;
default = [ ];
example = lib.literalExample ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
example = lib.literalExample ''
example = lib.literalExpression ''

[
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 = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
systemd.user = {
systemd.user = lib.mkIf (pkgs.stdenv.hostPlatform.isLinux) {


services.podman = {
Unit = {
Description = "Podman API Service";
Requires = "podman.socket";
After = "podman.socket";
Documentation = "man:podman-system-service(1)";
StartLimitIntervalSec = 0;
};
Comment on lines +146 to +152
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Unit = {
Description = "Podman API Service";
Requires = "podman.socket";
After = "podman.socket";
Documentation = "man:podman-system-service(1)";
StartLimitIntervalSec = 0;
};
Unit = {
Description = "Podman API Service";
Requires = ["podman.socket"];
After = ["podman.socket"];
Documentation = "man:podman-system-service(1)";
StartLimitIntervalSec = 0;
};

Should this have RefuseManualStart and RefuseManualStop?

Service = {
Type = "exec";
KillMode = "process";
Environment = ''LOGGING=" --log-level=info"'';
ExecStart =
[ "" "${cfg.package}/bin/podman $LOGGING system service" ];
Comment on lines +156 to +158
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is ExecStart a list with an empty first element?
And why is the logging parameter passed as an environment variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, as I said this module is basically a 1 to 1 adaptation of the nixpkgs module. I could fix this, but I preferred to simply keep as much code the same

https://github.com/NixOS/nixpkgs/blob/nixos-21.11/nixos/modules/virtualisation/podman.nix#L146

};

Install = { WantedBy = [ "default.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";
Copy link
Contributor Author

@MaeIsBad MaeIsBad Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that's not how you're supposed to do this but I'm not sure how I should specify the /run/user/UID path

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$XDG_RUNTIME_DIR is the variable used for this.

Variables in home.sessionVariables are not expanded (AFAIK) so this would need to be added to home.sessionVariablesExtra (which is a string).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable seems to get expanded for me, I'm not sure if that's a bug

})
]);
}
1 change: 1 addition & 0 deletions tests/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
]);
Expand Down
12 changes: 12 additions & 0 deletions tests/modules/virtualisation/podman/basic-configuration.nix
Original file line number Diff line number Diff line change
@@ -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
'';
};
}
4 changes: 4 additions & 0 deletions tests/modules/virtualisation/podman/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
podman-basic-configuration = ./basic-configuration.nix;
podman-docker-compat = ./docker-compat.nix;
}
Loading