From f0fb1ba5f3074da592fadccf8b90da2deeb608e9 Mon Sep 17 00:00:00 2001 From: OlivierChiasson Date: Thu, 11 Jun 2026 22:28:48 -0300 Subject: [PATCH] Add Docker registry authentication support to NixOS configurations --- modules/system/default.nix | 1 + modules/system/docker-registry-auth.nix | 79 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 modules/system/docker-registry-auth.nix diff --git a/modules/system/default.nix b/modules/system/default.nix index b24e860..5c4d0f9 100644 --- a/modules/system/default.nix +++ b/modules/system/default.nix @@ -12,6 +12,7 @@ self.nixosModules.systemSpotify self.nixosModules.systemPackagesDefaults self.nixosModules.systemDocker + self.nixosModules.systemDockerRegistryAuth self.nixosModules.systemFlatpak self.nixosModules.systemAudio self.nixosModules.systemIdeapadMrubyOverlay diff --git a/modules/system/docker-registry-auth.nix b/modules/system/docker-registry-auth.nix new file mode 100644 index 0000000..3b1f4c4 --- /dev/null +++ b/modules/system/docker-registry-auth.nix @@ -0,0 +1,79 @@ +# 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; + }; + }; +}