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,
"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 {
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))
}
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
}