snebel29
snebel29

Reputation: 199

Unable to pull from GCR registry using docker engine-api and long lived JSON file

In the advanced authentication methods documentation for google cloud container registry explains a method for login to the registry using a JSON Key file with the docker cli, this just works fine

$ docker login -u _json_key -p "$(cat keyfile.json)" https://gcr.io

However I'm trying to use that same keyfile.json file to login to the registry using the golang docker/engine-api libraries, I have some working code that this seems to be authenticating fine into other registryies, but always providing a file with the following structure

{
  "auths": {
    "cr.whatever.com": {
      "password": "PASSWORD",
      "username": "registry"
    }
  }
}

By passing that Unmarshal file into ImageBuildOptions function here to then be consumed here

However is not working when using the keyfile.json or a working config.json ...

The docker documentation states that a JSON base64 encoded object with username and password should be used as described here into the Header Parameters section.

I've tried multiple option to produce a file that can be successfully consumed into the docker X-Registry-Config header without much luck...

Any help/hint would be much appreciated.

Thanks!

Upvotes: 1

Views: 597

Answers (2)

snebel29
snebel29

Reputation: 199

Thank you for your help jsand, I finally draft a working code function as below

func (d *DockerEngineClient) BuildImage(archive, modelId string, authConfigs map[string]types.AuthConfig) (types.ImageBuildResponse, error) {
    buildContext, err := os.Open(archive)
    defer buildContext.Close()

    c, err := ioutil.ReadFile(os.Getenv("GOOGLE_APPLICATION_CREDENTIALS_FILE"))
    if err != nil {
        return err
    }

    var authConfigs2 map[string]types.AuthConfig
    authConfigs2 = make(map[string]types.AuthConfig)

    authConfigs2["gcr.io"] = types.AuthConfig{
        Username: "_json_key",
        Password: string(c),
        ServerAddress: fmt.Sprintf("https://%s", d.remoteRegistryPrefix),
    }

    buildOptions := types.ImageBuildOptions{
        Tags:        []string{fmt.Sprintf("%s/%s", d.remoteRegistryPrefix, modelId)},
        AuthConfigs: authConfigs2,
    }

    var buildResponse types.ImageBuildResponse

    buildResponse, err = d.client.ImageBuild(context.TODO(), buildContext, buildOptions)
    if err != nil {
        return buildResponse, err
    }

    b, _ := ioutil.ReadAll(buildResponse.Body)
    fmt.Printf("%s\n", b)
    buildResponse.Body.Close()

    return buildResponse, err
}

Upvotes: 2

jsand
jsand

Reputation: 595

I think the issue is that 'config' is an overloaded term. Looks like X-Registry-Config contains a base64-encoded version of the JSON-encoded value of options.AuthConfigs, rather than the full docker config file. If you only want to authenticate for https://gcr.io, your JSON input should be:

{
  "gcr.io": {
    "username": "_json_key",
    "password": "{contents of keyfile.json}"
  }
}

Or, if you're willing to use Docker's golang libs:

import (
    "encoding/base64"
    "encoding/json"
    "net/http"

    "github.com/docker/engine-api/types"
)

def addDockerAuthsHeader(keyfile_contents string, headers http.Header) error {
    authConfigs := map[string]types.AuthConfig{
        "gcr.io": {
            Username: "_json_key",
            Password: keyfile_contents
        }
    }

    buf, err := json.Marshal(authConfigs)
    if err != nil {
        return err
    }

    headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))

    return nil
}

Generating keyfile_contents is left as an exercise for the reader :)

Upvotes: 1

Related Questions