Gero Posmyk-Leinemann e9aae6e497
[docker-up] Auto-login dockerd if GITPOD_IMAGE_AUTH is set (#20586)
* [docker-up] Minor fixes and add tests

Tool: gitpod/catfood.gitpod.cloud

* [image-builder-bob] Tests&fixes from an experimental PR

Tool: gitpod/catfood.gitpod.cloud

* [supervisor, ws-manager] Write docker credentials into client config file if passed into workspace

Tool: gitpod/catfood.gitpod.cloud

* [server] Introduce project.settings.enableDockerdAuthentication and expose it on the API

Tool: gitpod/catfood.gitpod.cloud

* [dashboard] Add "Docker registry authentication" toggle under projects/env vars

Tool: gitpod/catfood.gitpod.cloud

* [server] Guard project.settings.enableDockerdAuthentication by org write_settings permission

Tool: gitpod/catfood.gitpod.cloud

* review comments with cleanup + small fixes

Tool: gitpod/catfood.gitpod.cloud
2025-02-12 05:16:48 -05:00

149 lines
3.6 KiB
Go

// Copyright (c) 2025 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License.AGPL.txt in the project root for license information.
package dockerd
import (
"bufio"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
const (
gitpodUserId = 33333
)
type ConvertUserArg func(arg string, value interface{}) ([]string, error)
var allowedDockerArgs = map[string]ConvertUserArg{
"remap-user": convertRemapUser,
// TODO(gpl): Why this allow-list instead of a converter lookup only?
"proxies": nil,
"http-proxy": nil,
"https-proxy": nil,
}
func ParseUserArgs(log *logrus.Entry, userArgs string) ([]string, error) {
if userArgs == "" {
return nil, nil
}
var providedDockerArgs map[string]interface{}
if err := json.Unmarshal([]byte(userArgs), &providedDockerArgs); err != nil {
return nil, xerrors.Errorf("unable to deserialize docker args: %w", err)
}
return mapUserArgs(log, providedDockerArgs)
}
func mapUserArgs(log *logrus.Entry, jsonObj map[string]interface{}) ([]string, error) {
args := []string{}
for userArg, userValue := range jsonObj {
converter, exists := allowedDockerArgs[userArg]
if !exists {
// TODO(gpl): Why this allow-list instead of a converter lookup only?
continue
}
if converter != nil {
cargs, err := converter(userArg, userValue)
if err != nil {
return nil, xerrors.Errorf("could not convert %v - %v: %w", userArg, userValue, err)
}
args = append(args, cargs...)
continue
}
strValue, ok := (userValue).(string)
if ok {
args = append(args, fmt.Sprintf("--%s=%s", userArg, strValue))
continue
}
bValue, ok := (userValue).(bool)
if ok {
args = append(args, fmt.Sprintf("--%s=%t", userArg, bValue))
continue
}
obj, ok := (userValue).(map[string]interface{})
if ok {
nestedArgs, err := mapUserArgs(log, obj)
if err != nil {
return nil, xerrors.Errorf("could not convert nested arg %v - %v: %w", userArg, userValue, err)
}
args = append(args, nestedArgs...)
continue
}
log.WithField("arg", userArg).WithField("value", userValue).Warn("could not map userArg to dockerd argument, skipping.")
}
return args, nil
}
func convertRemapUser(arg string, value interface{}) ([]string, error) {
v, ok := (value).(string)
if !ok {
return nil, xerrors.Errorf("userns-remap expects a string argument")
}
id, err := strconv.Atoi(v)
if err != nil {
return nil, err
}
for _, f := range []string{"/etc/subuid", "/etc/subgid"} {
err := adaptSubid(f, id)
if err != nil {
return nil, xerrors.Errorf("could not adapt subid files: %w", err)
}
}
return []string{"--userns-remap", "gitpod"}, nil
}
func adaptSubid(oldfile string, id int) error {
uid, err := os.Open(oldfile)
if err != nil {
return err
}
newfile, err := os.Create(oldfile + ".new")
if err != nil {
return err
}
mappingFmt := func(username string, id int, size int) string { return fmt.Sprintf("%s:%d:%d\n", username, id, size) }
if id != 0 {
newfile.WriteString(mappingFmt("gitpod", 1, id))
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
} else {
newfile.WriteString(mappingFmt("gitpod", gitpodUserId, 1))
newfile.WriteString(mappingFmt("gitpod", 1, gitpodUserId-1))
newfile.WriteString(mappingFmt("gitpod", gitpodUserId+1, 32200)) // map rest of user ids in the user namespace
}
uidScanner := bufio.NewScanner(uid)
for uidScanner.Scan() {
l := uidScanner.Text()
if !strings.HasPrefix(l, "gitpod") {
newfile.WriteString(l + "\n")
}
}
if err = os.Rename(newfile.Name(), oldfile); err != nil {
return err
}
return nil
}