fix: share an explicit DOCKER_CONFIG between docker login and compose so private pulls work under ProtectHome - #80
Open
eav93 wants to merge 1 commit into
Open
Conversation
docker login persisted credentials to $HOME/.docker, but the bundled systemd unit runs the agent with ProtectHome=true, which makes $HOME (/root) unwritable inside the service's mount namespace. The credentials were therefore never stored, and the subsequent `docker compose` pull — run with a clean environment — fell back to anonymous access and failed for private registries with "unauthorized". The login failure was only logged at debug level, so it was invisible. Create an ephemeral DOCKER_CONFIG directory under stacksDir (already writable via the unit's ReadWritePaths) for up/pull operations and share it between `docker login` and the compose command through a small dockerEnv helper. The helper also gives `docker login` the same minimal, clean environment the compose command already uses, so host vars like DOCKER_CONTEXT/DOCKER_TLS_VERIFY cannot redirect it. Failed logins are now surfaced at warn level. No systemd unit change is required.
eav93
force-pushed
the
fix/registry-login-protecthome
branch
from
June 26, 2026 23:47
7f28b17 to
b373ab7
Compare
|
Hit this exact bug independently on a self-hosted Dockhand/Hawser setup (agent v0.2.46, Ubuntu, systemd unit with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Deploying a stack that pulls a private image fails with:
even when the registry credentials are correct.
ComposeClient.Executerunsdocker login(vialoginToRegistries) and thendocker compose up/pull.docker loginpersists credentials to$HOME/.docker/config.json, but the bundled systemd unit (scripts/hawser.service) hardens the agent withProtectHome=true, which makes$HOME(/root) unwritable inside the service's mount namespace. As a result:docker logincannot persist the credentials (and the error was only logged at debug level, so it was invisible);unauthorized.This affects every private registry on a default install, not just GHCR. It reproduces reliably with the shipped unit;
docker login/docker composesucceed only when run by hand outside the sandbox (where/root/.dockeris visible).Fix
Give
docker loginand the compose command a shared, writableDOCKER_CONFIG:DOCKER_CONFIGdirectory understacksDir(already writable per the unit'sReadWritePaths) forup/pulloperations that carry registry credentials, andRemoveAllit when the operation completes;DOCKER_CONFIGto bothdocker loginand the compose command through a smalldockerEnvhelper. The helper also givesdocker loginthe same minimal, clean environment the compose command already uses, so host vars such asDOCKER_CONTEXT/DOCKER_TLS_VERIFYcannot redirect it;docker loginfrom debug to warn, since it is the usual root cause of a later unauthorized pull.No systemd unit change is required.
Notes / tradeoffs
0700dir owned by root, and is removed viadefer. On a hard crash/kill a.docker-*dir could linger; left as an accepted tradeoff to keep this change small (a sweep of stale dirs could be a follow-up).stacksDirisMkdirAll'd before use so custom installs without a pre-created stacks dir still work.stacksDir, not on$HOME, so it also works for non-systemd deployments (e.g. the Docker image), including containers with a read-only root filesystem, where$HOME/.dockerwould be unwritable too. WhenstacksDiris unset the behaviour is unchanged.Testing
go build ./...,go vet ./...,go test ./...pass.ProtectHome=true: a clean env with an unwritable$HOMEmakesdocker compose pullof a private image fail with the exactunauthorizederror; runningdocker loginwith an explicit sharedDOCKER_CONFIGand then compose with the sameDOCKER_CONFIGpulls successfully.