Skip to content

Commit

Permalink
fix: add logic in agent to invoke docker credentials helpers
Browse files Browse the repository at this point in the history
When we added in the container_startup_script to run in the
agent's runtime environment (docker), an assumption was made
that the golang client would handle invoking credentials
helpers. Unfortunately, they are invoked by the cli itself.
Because of this, this commit is to invoke the credential
helper if auth wasn't provided on the experiment config.
  • Loading branch information
stoksc authored and rb-determined-ai committed Apr 28, 2020
1 parent dcd4d86 commit 4ddfab4
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
1 change: 1 addition & 0 deletions agent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/determined-ai/determined/master v0.0.0
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.13.1
github.com/docker/docker-credential-helpers v0.6.3
github.com/docker/go-connections v0.4.0
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
github.com/go-ole/go-ole v1.2.4 // indirect
Expand Down
2 changes: 2 additions & 0 deletions agent/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ github.com/docker/distribution v0.0.0-20180720172123-0dae0957e5fe/go.mod h1:J2gT
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v0.0.0-20170502054910-90d35abf7b35/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/engine v1.4.2-0.20191113042239-ea84732a7725 h1:aEPcmLOLK4xi6fKbzrWyiAAM+SN9sN1Pi6WrfLnPDog=
github.com/docker/engine v1.4.2-0.20191113042239-ea84732a7725/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
Expand Down
18 changes: 18 additions & 0 deletions agent/internal/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

type dockerActor struct {
*client.Client
credentialStores map[string]*credentialStore
}

type (
Expand Down Expand Up @@ -65,6 +66,11 @@ func registryToString(reg types.AuthConfig) (string, error) {
func (d *dockerActor) Receive(ctx *actor.Context) error {
switch msg := ctx.Message().(type) {
case actor.PreStart:
stores, err := getAllCredentialStores()
if err != nil {
ctx.Log().Warn(fmt.Sprintf("can't retrieve any credential stores: %v", err))
}
d.credentialStores = stores

case pullImage:
go d.pullImage(ctx, msg)
Expand Down Expand Up @@ -127,6 +133,18 @@ func (d *dockerActor) pullImage(ctx *actor.Context, msg pullImage) {
sendErr(ctx, errors.Wrap(err, "error encoding registry credentials"))
return
}
} else if store, ok := d.credentialStores[reference.Domain(ref)]; ok {
var creds types.AuthConfig
creds, err = store.get()
if err != nil {
sendErr(ctx, errors.Wrap(err, "unable to get credentials from helper"))
return
}
reg, err = registryToString(creds)
if err != nil {
sendErr(ctx, errors.Wrap(err, "error encoding registry credentials from helper"))
return
}
}

opts := types.ImagePullOptions{
Expand Down
80 changes: 80 additions & 0 deletions agent/internal/docker_credential_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package internal

import (
"encoding/json"
"io/ioutil"
"os"

hclient "github.com/docker/docker-credential-helpers/client"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
)

const (
dockerConfigFile = "/root/.docker/config.json"
credentialsHelperPrefix = "docker-credential-"
tokenUsername = "<token>"
)

type credentialStore struct {
registry string
store hclient.ProgramFunc
}

// getAllCredentialStores returns the credential helpers configured in the default docker
// config or an error.
func getAllCredentialStores() (map[string]*credentialStore, error) {
type ConfigFile struct {
CredentialHelpers map[string]string `json:"credHelpers,omitempty"`
}

credentialsStores := map[string]*credentialStore{}
configFile, err := os.Open(dockerConfigFile)
if err != nil {
return credentialsStores, errors.Wrap(err, "can't open docker config")
}

b, err := ioutil.ReadAll(configFile)
if err != nil {
return credentialsStores, errors.Wrap(err, "can't read docker config")
}

var config ConfigFile
err = json.Unmarshal(b, &config)
if err != nil {
return credentialsStores, errors.Wrap(err, "can't parse docker config")
}

if config.CredentialHelpers == nil {
return credentialsStores, nil
}

for hostname, helper := range config.CredentialHelpers {
credentialsStores[hostname] = &credentialStore{
registry: hostname,
store: hclient.NewShellProgramFunc(credentialsHelperPrefix + helper),
}
}

return credentialsStores, nil
}

// get executes the command to get the credentials from the native store.
func (s *credentialStore) get() (types.AuthConfig, error) {
var ret types.AuthConfig

creds, err := hclient.Get(s.store, s.registry)
if err != nil {
return ret, err
}

if creds.Username == tokenUsername {
ret.IdentityToken = creds.Secret
} else {
ret.Password = creds.Secret
ret.Username = creds.Username
}

ret.ServerAddress = s.registry
return ret, nil
}

0 comments on commit 4ddfab4

Please sign in to comment.