# oci-containers runs `docker login` in ExecStartPre, which stores plaintext credentials # in /root/.docker/config.json. For any container with `login` set, use in-memory # DOCKER_AUTH_CONFIG instead (no per-service list to maintain). { ... }: { flake.nixosModules.systemDockerRegistryAuth = { config, lib, pkgs, ... }: let cfg = config.chiasson.system.docker.ephemeralRegistryAuth; ociCfg = lib.attrByPath [ "virtualisation" "oci-containers" ] null config; backend = if ociCfg == null then "docker" else ociCfg.backend or "docker"; containerBackend = if backend == "podman" then lib.attrByPath [ "virtualisation" "podman" "package" ] pkgs.podman config else lib.attrByPath [ "virtualisation" "docker" "package" ] pkgs.docker config; isValidLogin = login: login.registry != null && login.username != null && login.passwordFile != null; mkRegistryAuthPreStart = login: lib.mkBefore '' export DOCKER_AUTH_CONFIG="$(${pkgs.jq}/bin/jq -cn \ --arg registry ${lib.escapeShellArg login.registry} \ --arg user ${lib.escapeShellArg login.username} \ --arg pass "$(${pkgs.coreutils}/bin/tr -d '\n\r' < ${login.passwordFile})" \ '{auths: {($registry): {username: $user, password: $pass, auth: (($user + ":" + $pass) | @base64)}}}')" ''; mkPreStartWithoutLogin = name: container: pkgs.writeShellScript "oci-pre-start-${name}" '' ${containerBackend}/bin/${backend} rm -f ${lib.escapeShellArg name} || true ${ lib.optionalString (container.imageFile != null) '' ${containerBackend}/bin/${backend} load -i ${container.imageFile} '' } ${ lib.optionalString (container.imageStream != null) '' ${container.imageStream} | ${containerBackend}/bin/${backend} load '' } ''; in { options.chiasson.system.docker.ephemeralRegistryAuth = { enable = lib.mkOption { type = lib.types.bool; default = true; description = '' Authenticate oci-containers registry pulls via in-memory `DOCKER_AUTH_CONFIG` instead of `docker login`. Applies automatically to every container that sets `virtualisation.oci-containers.containers..login`. ''; }; }; # Read merged container config only when defining systemd overrides — never write back # to `virtualisation.oci-containers.containers` (that causes infinite recursion). config = let containers = lib.attrByPath [ "virtualisation" "oci-containers" "containers" ] { } config; containersWithLogin = lib.filterAttrs (_: c: isValidLogin (c.login or { })) containers; in lib.mkIf (cfg.enable && containersWithLogin != { }) { systemd.services = lib.mapAttrs' ( name: container: lib.nameValuePair (container.serviceName or "${backend}-${name}") { preStart = mkRegistryAuthPreStart container.login; serviceConfig.ExecStartPre = lib.mkForce [ "${mkPreStartWithoutLogin name container}" ]; } ) containersWithLogin; }; }; }