testcontainers-go icon indicating copy to clipboard operation
testcontainers-go copied to clipboard

Auth problem with private Docker registry when building from Dockerfile

Open rzajac opened this issue 3 years ago • 11 comments

Describe the bug When building image from Dockerfile using private Docker repo I get en error:

req := tc.ContainerRequest{
        FromDockerfile: tc.FromDockerfile{
            Context:       "/my/path",
            Dockerfile:    "Dockerfile",
            PrintBuildLog: true,
        },

dp, _ := tc.NewDockerProvider()
tag, err := dp.BuildImage(ctx, &req) // This returns an error.
Head "http://my.domain.dev:5000/v2/ifp-docker/dki-proftpd/manifests/v0.2.1": no basic auth credentials

Docker info output of the command:

$ docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Build with BuildKit (Docker Inc., v0.6.3-docker)
  scan: Docker Scan (Docker Inc., v0.9.0)

Server:
 Containers: 251
  Running: 0
  Paused: 0
  Stopped: 251
 Images: 1216
 Server Version: 20.10.10
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 5b46e404f6b9f661a205e28d59c982d3634148f8
 runc version: v1.0.2-0-g52b36a2
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: default
 Kernel Version: 5.11.0-41-generic
 Operating System: Ubuntu 21.04
 OSType: linux
 Architecture: x86_64
 CPUs: 12
 Total Memory: 31.26GiB
 Name: thor
 ID: S4XQ:6LAR:AMRS:JRG5:FPSO:NXHA:AD7P:TEZF:Y6FQ:RQ6R:NB46:CUQ7
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  my.domain.dev:5000
  127.0.0.0/8
 Live Restore Enabled: false

Additional context

  • I can pull the image from command line witout problems docker pull my.domain.dev:5000/ifp-docker/dki-proftpd:v0.2.1 and then i do not have this problem anymore.
  • The image I'm trying to build is based on the dki-proftpd:v0.2.1 image (FROM dki-proftpd:v0.2.1 as source).

rzajac avatar Dec 02 '21 13:12 rzajac

After some research I found that https://github.com/testcontainers/testcontainers-go/blob/9dc7dbd18ae46337f80734fd016d276f9fdbb6fd/docker.go#L601 lacks the AuthConfig key.

I cloned the repository ands when I added below code it stared working as expected.

buildOptions := types.ImageBuildOptions{
    BuildArgs:  img.GetBuildArgs(),
    Dockerfile: img.GetDockerfile(),
    Context:    buildContext,
    Tags:       []string{repoTag},
    AuthConfigs: map[string]types.AuthConfig{
        "my.domain.dev:5000": {
            Username: "user",
            Password: "pass",
        },
    },
}

Unfortunately currently there is noway to pass it from my tests. The ImageBuildInfo intrerface would have to be extended.

rzajac avatar Dec 02 '21 14:12 rzajac

Hey @rzajac thanks for opening this issue, it seems we are missing those credentials when pulling the building the image. I wonder if we are missing AuthConfig elsewhere 🤔

Unfortunately currently there is noway to pass it from my tests. The ImageBuildInfo intrerface would have to be extended.

Sorry but I do not get this. Do you mean that you cannot test it because you'll be forced to expose your credentials in the code? If that's the case, have you tried to force a docker login in your build pipeline first?

mdelapenya avatar Dec 03 '21 07:12 mdelapenya

@mdelapenya I meant that with current way BuildImage method is implemented there is no way to set AuthConfigs.

Since yesterday I found a workaround for this problem by pulling the image using Docker API before I use testcontainers.

My function looks like below in case someone wants to know how i did the workaround:

// PullImage uses Docker client to pull the image reference.
func PullImage(ctx context.Context, ref string) error {
	cli, err := client.NewClientWithOpts(
		client.FromEnv,
		client.WithAPIVersionNegotiation(),
	)
	if err != nil {
		return err
	}

	cred, err := RegistryCred(ref, "")
	if err != nil {
		return err
	}

	options := types.ImagePullOptions{
		All:          true,
		RegistryAuth: cred,
	}

	rc, err := cli.ImagePull(ctx, ref, options)
	if err != nil {
		return err
	}
	defer func() { _ = rc.Close() }()

	if _, err = io.ReadAll(rc); err != nil {
		return err
	}

	return nil
}

// RegistryCred returns private registry credentials string for image. The dir
// should be path to the directory with Docker configuration file or empty
// string. The default config (~/.docker/config.json) will be used if the dir
// is set to empty string.
func RegistryCred(image, dir string) (string, error) {
	cf, err := config.Load(dir)
	if err != nil {
		return "", err
	}

	named, err := reference.ParseNormalizedNamed(image)
	if err != nil {
		return "", err
	}

	domain := reference.Domain(named)
	if domain == "docker.io" {
		return "", nil
	}

	ac, err := cf.GetAuthConfig(domain)
	if err != nil {
		return "", err
	}

	encodedJSON, err := json.Marshal(ac)
	if err != nil {
		return "", fmt.Errorf("error when encoding auth: %w", err)
	}
	return base64.URLEncoding.EncodeToString(encodedJSON), nil
}

rzajac avatar Dec 03 '21 15:12 rzajac

I am getting the same error in pipeline "no basic auth credentials". We have uploaded images to AWS ECR and trying to pull images from there.

The thing is we are doing docker login in pipeline before running these tests and still getting this error.

ruchidhore12 avatar Dec 30 '21 06:12 ruchidhore12

I was facing the same problem. But when I looked into docker go pkg. I found how we can give the registry credential for auth docker pull. https://docs.docker.com/engine/api/sdk/examples/#pull-an-image-with-authentication

image

So I gave the above auth str with my encoded registry credentials to RegistryCred as below in ContainerRequest. This worked from in my local as well CI pipeline.

req := testcontainers.ContainerRequest{
		Image:        "<Image Path>",
		ExposedPorts: []string{"1433/tcp"},
		RegistryCred: authStr,
		SkipReaper:   true, // skips ryuk container cleanup
		WaitingFor:   wait.ForLog("database setup completed"),
	}

mehulgohil avatar Feb 17 '22 09:02 mehulgohil

So I gave the above auth str with my encoded registry credentials to RegistryCred as below in ContainerRequest. This worked from in my local as well CI pipeline.

@rzajac could you please check with @mehulgohil's approach? If this is working for you too, I think we could close this issue

mdelapenya avatar Feb 17 '22 09:02 mdelapenya

@mdelapenya the above may work for static creds, but the docker build fails for me where I am using a credential helper to inject the credentials as ENV variables. This is far safer and the recommended approach

matdehaast avatar Mar 04 '22 06:03 matdehaast

@mdelapenya running into a similar issue on simple pull images from ECR (despite being logged in already). Looks like the go implementation for testcontainer does not handle loading the docker config.json when no RegistryCred are passed. The java implementation for testcontainer seems to handle this by default (https://github.com/testcontainers/testcontainers-java/blob/de1324ed2800eff4da326d0c23d281399d006bc0/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java).

Are you open for a contribution to implement a similar behavior in the go implementation?

rvichery avatar Sep 08 '22 18:09 rvichery

@rvichery thanks for pinging us about this issue, your contributions are super welcomed!! So feel free to elaborate a PR describing the use case it solves. Having Java code as a reference is a good thing to follow, so please reference it whenever you need it.

mdelapenya avatar Sep 09 '22 07:09 mdelapenya

@mdelapenya the above may work for static creds, but the docker build fails for me where I am using a credential helper to inject the credentials as ENV variables. This is far safer and the recommended approach

@matdehaast sorry for the radio silence, for some reason this message felt out of my radar, my bad 😞

I know @rvichery is willing to contribute a fix, but if you want to participate too, I wonder if you both would be interested in opening a discussion to collaborate in the fix. wdyt?

mdelapenya avatar Sep 09 '22 07:09 mdelapenya

@mehulgohil nice work around, however, I am using multi-stage docker file with multiple private repositories so another problem is which credential to use at build time...

I added this issue #532 to hopefully support multiple registry creds (@rzajac Can you please check if it make sense?)

szaher avatar Sep 16 '22 16:09 szaher

I think this issue can be closed, as #602 was already merged.

Thank you all for creating such a healthy discussion 👏

mdelapenya avatar Feb 08 '23 10:02 mdelapenya