Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions components/docker-up/docker-up/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ package main
import (
"archive/tar"
"bufio"
"bytes"
"compress/gzip"
"context"
"embed"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -58,6 +60,7 @@ var aptUpdated = false
const (
dockerSocketFN = "/var/run/docker.sock"
gitpodUserId = 33333
gitpodGroupId = 33333
containerIf = "eth0"
)

Expand Down Expand Up @@ -185,13 +188,88 @@ func runWithinNetns() (err error) {
}()
}

if imageAuth, _ := os.LookupEnv("GITPOD_IMAGE_AUTH"); strings.TrimSpace(imageAuth) != "" {
if err := waitUntilSocketPresent(dockerSocketFN); err == nil {
tryAuthenticateForAllHosts(imageAuth)
}
}

err = cmd.Wait()
if err != nil {
return err
}
return nil
}

func waitUntilSocketPresent(dockerSocketFN string) error {
socketTimeout := 30 * time.Second
socketCtx, cancel := context.WithTimeout(context.Background(), socketTimeout)
defer cancel()

ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()

for {
select {
case <-socketCtx.Done():
log.WithError(socketCtx.Err()).Warn("timeout waiting for docker socket")
return socketCtx.Err()
case <-ticker.C:
if _, err := os.Stat(dockerSocketFN); err == nil {
// Socket file exists
return nil
}
}
}
}

func tryAuthenticateForAllHosts(imageAuth string) {
splitHostAndCredentials := func(s string) (string, string) {
parts := strings.SplitN(s, ":", 2)
if len(parts) < 2 {
return "", ""
}
return parts[0], parts[1]
}
splitCredentials := func(host, s string) (string, string, error) {
credentials, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", "", fmt.Errorf("Cannot decode docker credentials for host %s: %w", host, err)
}
parts := strings.SplitN(string(credentials), ":", 2)
if len(parts) < 2 {
return "", "", fmt.Errorf("Credentials in wrong format")
}
return parts[0], parts[1], nil
}

authenticationPerHost := strings.Split(imageAuth, ",")
for _, hostCredentials := range authenticationPerHost {
host, credentials := splitHostAndCredentials(hostCredentials)
if host == "" || credentials == "" {
log.Warnf("Unable to authenticate with host %s, skipping.", host)
continue
}
username, password, decodeErr := splitCredentials(host, credentials)
if decodeErr != nil || username == "" || password == "" {
log.WithError(decodeErr).Warnf("Unable to authenticate with host %s, skipping.", host)
continue
}

loginCmd := exec.Command("docker", "login", "--username", username, "--password-stdin", host)
loginCmd.SysProcAttr = &syscall.SysProcAttr{}
loginCmd.Env = append(loginCmd.Env, "USER=gitpod")
loginCmd.SysProcAttr.Credential = &syscall.Credential{Uid: gitpodUserId, Gid: gitpodGroupId}
loginCmd.Stdin = bytes.NewBufferString(password)
loginErr := loginCmd.Run()
if loginErr != nil {
log.WithError(loginErr).Warnf("Unable to authenticate with host %s, skipping.%s", host)
continue
}
log.Infof("Authenticated with host %s", host)
}
}

type ConvertUserArg func(arg, value string) ([]string, error)

var allowedDockerArgs = map[string]ConvertUserArg{
Expand Down
3 changes: 3 additions & 0 deletions components/supervisor/pkg/supervisor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ type WorkspaceConfig struct {
ConfigcatEnabled bool `env:"GITPOD_CONFIGCAT_ENABLED"`

SSHGatewayCAPublicKey string `env:"GITPOD_SSH_CA_PUBLIC_KEY"`

// Comma-separated list of host:<base64ed user:password> pairs to authenticate against docker registries
GitpodImageAuth string `env:"GITPOD_IMAGE_AUTH"`
}

// WorkspaceGitpodToken is a list of tokens that should be added to supervisor's token service.
Expand Down
8 changes: 7 additions & 1 deletion components/supervisor/pkg/supervisor/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,13 @@ func listenToDockerSocket(parentCtx context.Context, term *terminal.Mux, cfg *Co
}

cmd := exec.CommandContext(ctx, "/usr/bin/docker-up")
cmd.Env = append(os.Environ(), "LISTEN_FDS=1")
// TODO(gpl): Why are we using os.Environ() in the first place, and not childProcEnvvars?
// Might be an oversight from times when we still passed all dockerupEnv vars as Pod dockerupEnv vars... otherwise I don't see why not use [] instead.
// But I'm not sure, so sticking with the slightly more verbose version here.
dockerupEnv := os.Environ()
dockerupEnv = append(dockerupEnv, fmt.Sprintf("GITPOD_IMAGE_AUTH=%s", cfg.GitpodImageAuth))
dockerupEnv = append(dockerupEnv, "LISTEN_FDS=1")
cmd.Env = dockerupEnv
cmd.ExtraFiles = []*os.File{socketFD}
cmd.Stdout = stdout
cmd.Stderr = stderr
Expand Down
3 changes: 2 additions & 1 deletion components/ws-manager-mk2/controllers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,8 @@ func createWorkspaceEnvironment(sctx *startWorkspaceContext) ([]corev1.EnvVar, e
"GITPOD_EXTERNAL_EXTENSIONS",
"GITPOD_WORKSPACE_CLASS_INFO",
"GITPOD_IDE_ALIAS",
"GITPOD_RLIMIT_CORE":
"GITPOD_RLIMIT_CORE",
"GITPOD_IMAGE_AUTH":
// these variables are allowed - don't skip them
default:
if strings.HasPrefix(e.Name, "GITPOD_") {
Expand Down
Loading