From 5b999db7f3e670fe5043983e26e9a8a3b78711af Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Fri, 31 Jul 2020 18:28:40 -0600 Subject: [PATCH] Add Docker registry authentication --- config/config_docker.go | 31 +++++++++++++++++++++++++++++++ server/environment_docker.go | 28 +++++++++++++++++++++++++++- server/filesystem.go | 8 ++++---- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/config/config_docker.go b/config/config_docker.go index 40131e4..c220f07 100644 --- a/config/config_docker.go +++ b/config/config_docker.go @@ -1,5 +1,12 @@ package config +import ( + "encoding/base64" + "encoding/json" + "github.com/docker/docker/api/types" + "github.com/pkg/errors" +) + type dockerNetworkInterfaces struct { V4 struct { Subnet string `default:"172.18.0.0/16"` @@ -53,4 +60,28 @@ type DockerConfiguration struct { // Defines the location of the timezone file on the host system that should // be mounted into the created containers so that they all use the same time. TimezonePath string `default:"/etc/timezone" json:"timezone_path" yaml:"timezone_path"` + + // Registries . + Registries map[string]RegistryConfiguration `json:"registries" yaml:"registries"` +} + +// RegistryConfiguration . +type RegistryConfiguration struct { + Username string `yaml:"username"` + Password string `yaml:"password"` +} + +// Base64 . +func (c RegistryConfiguration) Base64() (string, error) { + authConfig := types.AuthConfig{ + Username: c.Username, + Password: c.Password, + } + + b, err := json.Marshal(authConfig) + if err != nil { + return "", errors.Wrap(err, "failed to marshal AuthConfig") + } + + return base64.URLEncoding.EncodeToString(b), nil } diff --git a/server/environment_docker.go b/server/environment_docker.go index fd2dda6..f17f286 100644 --- a/server/environment_docker.go +++ b/server/environment_docker.go @@ -654,7 +654,33 @@ func (d *DockerEnvironment) ensureImageExists() error { ctx, cancel := context.WithTimeout(context.Background(), time.Minute*15) defer cancel() - out, err := d.Client.ImagePull(ctx, d.Image(), types.ImagePullOptions{All: false}) + image := d.Image() + + // Get a registry auth configuration from the config. + var registryAuth *config.RegistryConfiguration + for registry, c := range config.Get().Docker.Registries { + if !strings.HasPrefix(image, registry) { + continue + } + + log.WithField("registry", registry).Debug("using authentication for repository") + registryAuth = &c + break + } + + // Get the ImagePullOptions. + imagePullOptions := types.ImagePullOptions{All: false} + if registryAuth != nil { + b64, err := registryAuth.Base64() + if err != nil { + log.WithError(err).Error("failed to get registry auth credentials") + } + + // b64 is a string so if there is an error it will just be empty, not nil. + imagePullOptions.RegistryAuth = b64 + } + + out, err := d.Client.ImagePull(ctx, image, imagePullOptions) if err != nil { images, ierr := d.Client.ImageList(ctx, types.ImageListOptions{}) if ierr != nil { diff --git a/server/filesystem.go b/server/filesystem.go index 4c1ea69..e09ea73 100644 --- a/server/filesystem.go +++ b/server/filesystem.go @@ -40,8 +40,8 @@ func IsPathResolutionError(err error) bool { } type Filesystem struct { - Server *Server - cacheDiskMu sync.Mutex + Server *Server + cacheDiskMu sync.Mutex } // Returns the root path that contains all of a server's data. @@ -114,10 +114,10 @@ func (fs *Filesystem) SafePath(p string) (string, error) { } // Generate a path to the file by cleaning it up and appending the root server path to it. This -// DOES NOT gaurantee that the file resolves within the server data directory. You'll want to use +// DOES NOT guarantee that the file resolves within the server data directory. You'll want to use // the fs.unsafeIsInDataDirectory(p) function to confirm. func (fs *Filesystem) unsafeFilePath(p string) string { - // Calling filpath.Clean on the joined directory will resolve it to the absolute path, + // Calling filepath.Clean on the joined directory will resolve it to the absolute path, // removing any ../ type of resolution arguments, and leaving us with a direct path link. // // This will also trim the existing root path off the beginning of the path passed to