diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index ee85385630c9..4f64702bfee1 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -294,6 +294,30 @@ Deployments running the server and worker in the same network namespace must also set at least the worker `AUTHENTIK_LISTEN__HTTP` address so that the server and worker do not bind to the same address. +- `podman` has been updated to Podman 6. + - BoltDB support was dropped. Podman has been automatically migrating to SQLite since 5.8.0 on system reboots. + - `iptables` support was dropped, Podman now unconditionally uses its `nftables` backend. + This has the side effect that DNS in custom networks no longer works for rootful containers without a corresponding firewall rule when using the `iptables` or `nftables` firewall backend. + The `firewalld` backend is not affected. + `virtualisation.podman.dns` has been introduced to allow DNS traffic configuration. + Network interfaces whose name starts with `podman` are allowed by default. + If you have custom podman networks with non-default interface names, add them to `virtualisation.podman.dns.interfaces`. + + - `netavark` is now the only supported networking backend. + - `slirp4netns` is no longer supported for rootless networking, `pasta` is now the default and only available option. + Existing containers using `slirp4netns` must be migrated using `podman system migrate`. + - Configuration was rewritten and now follows the [UAPI.6 Configuration Files Specification](https://uapi-group.org/specifications/specs/configuration_files_specification/). + + See https://github.com/podman-container-tools/podman/blob/v6.0.0/contrib/design-docs/config-file-parsing.md. + + Configuration files are now split into common, rootful and rootless versions. + `virtualisation.containers` was updated to support the new configuration format. + + Configurations which set `graphroot` or `runroot` must move these from the common configuration to the rootful config + because rootless podman reads this configuration now as well. + - Upgrading with `nixos-rebuild boot` and rebooting is recommended, to complete the BoltDB migration and drop stale iptables rules. + - See upstream release notes: https://github.com/podman-container-tools/podman/blob/v6.1.2/RELEASE_NOTES.md#600 + - `qt.platformTheme` no longer supports the `gtk2` option as GTK 2 had reached end-of-life and will be removed in Nixpkgs. - `pkgs.php` now points to PHP 8.5 diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix index 4b0c59846d2c..41d609b6380b 100644 --- a/nixos/modules/virtualisation/containers.nix +++ b/nixos/modules/virtualisation/containers.nix @@ -9,21 +9,65 @@ let inherit (lib) literalExpression mkOption types; - oldRegistriesOptionsUsed = lib.any (x: x != [ ]) ( - with cfg.registries; - [ - search - insecure - block - ] - ); - toml = pkgs.formats.toml { }; + json = pkgs.formats.json { }; + mkOptions = file: { + rootless.settings = mkOption { + type = toml.type; + default = { }; + description = "rootless ${file} configuration"; + }; + rootful.settings = mkOption { + type = toml.type; + default = { }; + description = "rootful ${file} configuration"; + }; + settings = mkOption { + type = toml.type; + default = { }; + description = "base ${file} configuration"; + }; + }; in { meta = { teams = [ lib.teams.podman ]; }; + imports = [ + (lib.mkRemovedOptionModule [ + "virtualisation" + "containers" + "containersConf" + "cniPlugins" + ] "Podman 6.0 removed support for CNI networking.") + (lib.mkRemovedOptionModule + [ + "virtualisation" + "containers" + "registries" + "search" + ] + "Podman 6.0 dropped support for the deprecated V1 registry.conf format. Migrate to virtualisation.containers.registries.settings." + ) + (lib.mkRemovedOptionModule + [ + "virtualisation" + "containers" + "registries" + "insecure" + ] + "Podman 6.0 dropped support for the deprecated V1 registry.conf format. Migrate to virtualisation.containers.registries.settings." + ) + (lib.mkRemovedOptionModule + [ + "virtualisation" + "containers" + "registries" + "block" + ] + "Podman 6.0 dropped support for the deprecated V1 registry.conf format. Migrate to virtualisation.containers.registries.settings." + ) + ]; options.virtualisation.containers = { @@ -41,90 +85,33 @@ in description = "Enable the OCI seccomp BPF hook"; }; - containersConf.settings = mkOption { + shortnames.enable = mkOption { + type = types.bool; + default = true; + description = '' + Enable the usage of a collection of common aliases for fully qualified images. + See https://github.com/containers/shortnames. + ''; + }; + + containersConf = mkOptions "containers.conf"; + storage = mkOptions "storage.conf"; + + registries.settings = mkOption { type = toml.type; default = { }; - description = "containers.conf configuration"; - }; - - containersConf.cniPlugins = mkOption { - type = types.listOf types.package; - defaultText = literalExpression '' - [ - pkgs.cni-plugins - ] - ''; - example = literalExpression '' - [ - pkgs.cniPlugins.dnsname - ] - ''; description = '' - CNI plugins to install on the system. + registries.conf configuration. + + Examine [containers-registries.conf(5)] for more information about the format. + + [containers-registries.conf(5)]: https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md ''; }; - storage.settings = mkOption { - type = toml.type; - description = "storage.conf configuration"; - }; - - registries = { - # TODO: remove those options in 26.11 - search = mkOption { - visible = false; - type = types.listOf types.str; - default = [ ]; - description = '' - List of repositories to search. - - Deprecated, examine {option}`virtualisation.containers.registries.settings` instead. - ''; - }; - - insecure = mkOption { - default = [ ]; - visible = false; - type = types.listOf types.str; - description = '' - List of insecure repositories. - - Deprecated, examine {option}`virtualisation.containers.registries.settings` instead. - ''; - }; - - block = mkOption { - default = [ ]; - visible = false; - type = types.listOf types.str; - description = '' - List of blocked repositories. - - Deprecated, examine {option}`virtualisation.containers.registries.settings` instead. - ''; - }; - - settings = mkOption { - type = toml.type; - default = { - registry = [ - { location = "docker.io"; } - { location = "quay.io"; } - ]; - }; - description = '' - repositories.conf configuration. - - Examine [containers-registries.conf(5)] for more information about the format. - - [containers-registries.conf(5)]: https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md - ''; - }; - }; - policy = mkOption { default = { }; - type = types.attrs; + type = json.type; example = literalExpression '' { default = [ { type = "insecureAcceptAnything"; } ]; @@ -144,49 +131,83 @@ in }; - config = lib.mkIf cfg.enable { - warnings = lib.optional oldRegistriesOptionsUsed "the options virtualisation.containers.registries.search / insecure / block are deprecated. See virtualisation.containers.registries.settings instead."; + config = + let + dropIn = + name: scope: settings: + let + path = "${name}${lib.optionalString (scope != "") ".${scope}"}.conf"; + filename = if scope == "" then "00-nixos.conf" else "10-nixos-${scope}.conf"; + in + lib.optionalAttrs (settings != { }) { + "containers/${path}.d/${filename}".source = toml.generate path settings; + }; + in + lib.mkIf cfg.enable { + assertions = + let + attr = name: default: lib.attrByPath [ "network" name ] default cfg.containersConf.settings; + in + [ + { + assertion = (attr "default_rootless_network_cmd" "pasta") == "pasta"; + message = "Podman 6.0 only supports pasta for rootless networking."; + } + { + assertion = (attr "firewall_driver" "nftables") != "iptables"; + message = "Podman 6.0 no longer supports iptables as a firewall driver. Use nftables instead."; + } + { + assertion = (attr "network_backend" "netavark") == "netavark"; + message = "Podman 6.0 no longer supports network backends other than netavark."; + } + ]; - virtualisation.containers.registries.settings = lib.mkIf oldRegistriesOptionsUsed { - registries = { - block.registries = cfg.registries.block; - insecure.registries = cfg.registries.insecure; - search.registries = cfg.registries.search; + warnings = + lib.optional (lib.hasAttrByPath [ "storage" "graphroot" ] cfg.storage.settings) '' + virtualisation.containers.storage.settings.storage.graphroot now applies to both rootful and rootless podman and will likely not work as intended. + Use virtualisation.containers.storage.{rootful,rootless}.settings.storage.graphroot instead. + '' + ++ lib.optional (lib.hasAttrByPath [ "storage" "runroot" ] cfg.storage.settings) '' + virtualisation.containers.storage.settings.storage.runroot now applies to both rootful and rootless podman and will likely not work as intended. + Use virtualisation.containers.storage.{rootful,rootless}.settings.storage.runroot instead. + ''; + + virtualisation.containers = { + containersConf.settings = { + engine = lib.mkIf cfg.ociSeccompBpfHook.enable { + hooks_dir = [ config.boot.kernelPackages.oci-seccomp-bpf-hook ]; + }; + containers = { + default_sysctls = lib.mkDefault [ "net.ipv4.ping_group_range=0 0" ]; + }; + }; + storage = { + settings.storage = { + driver = lib.mkDefault "overlay"; + options.overlay.mountopt = lib.mkDefault "nodev"; + }; + }; }; + + environment.etc = lib.mkMerge [ + (dropIn "containers" "" cfg.containersConf.settings) + (dropIn "containers" "rootful" cfg.containersConf.rootful.settings) + (dropIn "containers" "rootless" cfg.containersConf.rootless.settings) + (dropIn "storage" "" cfg.storage.settings) + (dropIn "storage" "rootful" cfg.storage.rootful.settings) + (dropIn "storage" "rootless" cfg.storage.rootless.settings) + (dropIn "registries" "" cfg.registries.settings) + { + "containers/policy.json".source = + if cfg.policy != { } then + json.generate "policy.json" cfg.policy + else + "${pkgs.skopeo.policy}/default-policy.json"; + } + (lib.mkIf cfg.shortnames.enable { + "containers/registries.conf.d/000-shortnames.conf".source = pkgs.containers-shortnames.shortnames; + }) + ]; }; - - 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 ]; - }; - }; - - virtualisation.containers.storage.settings.storage = { - driver = lib.mkDefault "overlay"; - graphroot = lib.mkDefault "/var/lib/containers/storage"; - runroot = lib.mkDefault "/run/containers/storage"; - }; - - environment.etc = { - "containers/containers.conf".source = toml.generate "containers.conf" cfg.containersConf.settings; - - "containers/storage.conf".source = toml.generate "storage.conf" cfg.storage.settings; - - "containers/registries.conf".source = toml.generate "registries.conf" cfg.registries.settings; - - "containers/policy.json".source = - if cfg.policy != { } then - pkgs.writeText "policy.json" (builtins.toJSON cfg.policy) - else - "${pkgs.skopeo.policy}/default-policy.json"; - }; - - }; - } diff --git a/nixos/modules/virtualisation/podman/default.nix b/nixos/modules/virtualisation/podman/default.nix index 248d89af41b4..203b619067e4 100644 --- a/nixos/modules/virtualisation/podman/default.nix +++ b/nixos/modules/virtualisation/podman/default.nix @@ -183,20 +183,7 @@ in config.systemd.package # To allow systemd-based container healthchecks ] ++ lib.optional (config.boot.supportedFilesystems.zfs or false) config.boot.zfs.package; - extraRuntimes = - cfg.extraRuntimes - ++ - lib.optionals - ( - config.virtualisation.containers.containersConf.settings.network.default_rootless_network_cmd or "" - == "slirp4netns" - ) - ( - with pkgs; - [ - slirp4netns - ] - ); + inherit (cfg) extraRuntimes; }; }; @@ -209,6 +196,28 @@ in ''; }; + dns = { + openFirewall = lib.mkOption { + type = lib.types.bool; + default = config.networking.firewall.backend != "firewalld"; + defaultText = lib.literalExpression '' + config.networking.firewall.backend != "firewalld" + ''; + description = '' + Whether to allow DNS (port 53) from all network interfaces configured in {option}`virtualisation.podman.dns.interfaces` to aardvark-dns. + ''; + }; + interfaces = lib.mkOption { + type = lib.types.listOf lib.types.nonEmptyStr; + default = [ ]; + description = '' + List of network interfaces which should have DNS (port 53) traffic allowed. + Auto-named interfaces (`podman0`, `podman1`, ...) are allowed by default. + Add custom podman networks with an explicitly set interface name here. + ''; + }; + }; + }; config = @@ -235,6 +244,8 @@ in // cfg.defaultNetwork.settings ); inherit (networkConfig) dns_enabled network_interface; + isFirewallD = config.networking.firewall.backend == "firewalld"; + isNftables = config.networking.firewall.backend == "nftables"; in lib.mkIf cfg.enable { warnings = lib.optionals cfg.enableNvidia [ @@ -251,19 +262,22 @@ in }; # containers cannot reach aardvark-dns otherwise - networking.firewall = lib.mkIf (config.networking.firewall.backend != "firewalld") { - interfaces.${network_interface}.allowedUDPPorts = lib.mkIf dns_enabled [ 53 ]; - }; + networking.firewall.interfaces = lib.mkIf cfg.dns.openFirewall ( + lib.genAttrs cfg.dns.interfaces (_: { + allowedUDPPorts = [ 53 ]; + allowedTCPPorts = [ 53 ]; + }) + ); - virtualisation.containers = { - enable = true; # Enable common /etc/containers configuration - containersConf.settings = { - network = { - network_backend = "netavark"; - firewall_driver = lib.mkIf config.networking.nftables.enable "nftables"; - }; - }; - }; + virtualisation.podman.dns.interfaces = + lib.mkIf (!isFirewallD) + # Add `network_interface` if it doesn't match the wildcard. + ( + lib.optional (dns_enabled && (!lib.hasPrefix "podman" network_interface)) network_interface + ++ lib.singleton (if isNftables then "podman*" else "podman+") + ); + + virtualisation.containers.enable = true; systemd.packages = [ cfg.package ]; @@ -339,6 +353,13 @@ in The options virtualisation.podman.dockerSocket.enable and virtualisation.docker.enable conflict, because only one can serve the socket. ''; } + { + assertion = cfg.dns.openFirewall -> !isFirewallD; + message = '' + The option `virtualisation.podman.dns.openFirewall` cannot be used with the `firewalld` firewall backend. + The `firewalld` backend is not affected by the DNS firewall issue the option is built for. + ''; + } ]; }; } diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index f29d10608d4c..fb1f3cad637c 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1487,6 +1487,7 @@ in plotinus = runTest ./plotinus.nix; pocket-id = runTest ./pocket-id.nix; podman = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./podman/default.nix { }; + podman-dns = runTest ./podman/dns.nix; podman-tls-ghostunnel = handleTestOn [ "aarch64-linux" "x86_64-linux" diff --git a/nixos/tests/podman/default.nix b/nixos/tests/podman/default.nix index 64ca81833af9..6000318c6e0f 100644 --- a/nixos/tests/podman/default.nix +++ b/nixos/tests/podman/default.nix @@ -47,13 +47,6 @@ import ../make-test-python.nix ( isNormalUser = true; }; }; - dns = - { pkgs, ... }: - { - virtualisation.podman.enable = true; - - virtualisation.podman.defaultNetwork.settings.dns_enabled = true; - }; docker = { pkgs, ... }: { @@ -74,6 +67,14 @@ import ../make-test-python.nix ( isNormalUser = true; }; }; + docker_compat = + { pkgs, ... }: + { + virtualisation.podman = { + enable = true; + dockerCompat = true; + }; + }; }; testScript = '' @@ -88,8 +89,8 @@ import ../make-test-python.nix ( rootful.wait_for_unit("sockets.target") rootful_norunc.wait_for_unit("sockets.target") rootless.wait_for_unit("sockets.target") - dns.wait_for_unit("sockets.target") docker.wait_for_unit("sockets.target") + docker_compat.wait_for_unit("sockets.target") start_all() with subtest("Run container as root with runc"): @@ -212,18 +213,6 @@ import ../make-test-python.nix ( ) rootful.succeed("podman rm journald") - with subtest("aardvark-dns"): - dns.succeed("tar cv --files-from /dev/null | podman import - scratchimg") - dns.succeed( - "podman run -d --name=webserver -v /nix/store:/nix/store -v /run/current-system/sw/bin:/bin -w ${pkgs.writeTextDir "index.html" "