Skip to content

Revert "Relax assumptions in supervisor" #18857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 2, 2023
Merged
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
4 changes: 2 additions & 2 deletions components/content-service/pkg/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Prepare(req *csapi.WorkspaceInitializer, urls map[string]string) ([]byte, e

// Execute runs an initializer to place content in destination based on the configuration read
// from the cfgin stream.
func Execute(ctx context.Context, destination string, cfgin io.Reader, runAs *initializer.User, opts ...initializer.InitializeOpt) (src csapi.WorkspaceInitSource, err error) {
func Execute(ctx context.Context, destination string, cfgin io.Reader, forceGitUser bool, opts ...initializer.InitializeOpt) (src csapi.WorkspaceInitSource, err error) {
var cfg config
err = json.NewDecoder(cfgin).Decode(&cfg)
if err != nil {
Expand All @@ -64,7 +64,7 @@ func Execute(ctx context.Context, destination string, cfgin io.Reader, runAs *in

rs = &storage.NamedURLDownloader{URLs: cfg.URLs}
ilr, err = initializer.NewFromRequest(ctx, destination, rs, &req, initializer.NewFromRequestOpts{
RunAs: runAs,
ForceGitpodUserForGit: forceGitUser,
})
if err != nil {
return "", err
Expand Down
25 changes: 8 additions & 17 deletions components/content-service/pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"

"github.com/opentracing/opentracing-go"
"golang.org/x/xerrors"
Expand Down Expand Up @@ -94,12 +93,8 @@ type Client struct {
// UpstreamCloneURI is the fork upstream of a repository
UpstreamRemoteURI string

// RunAs runs the Git commands as a particular user - if not nil
RunAs *User
}

type User struct {
UID, GID uint32
// if true will run git command as gitpod user (should be executed as root that has access to sudo in this case)
RunAsGitpodUser bool
}

// Status describes the status of a Git repo/working copy akin to "git status"
Expand Down Expand Up @@ -183,6 +178,8 @@ func (c *Client) GitWithOutput(ctx context.Context, ignoreErr *string, subcomman
env = append(env, fmt.Sprintf("GIT_AUTH_PASSWORD=%s", pwd))
}

env = append(env, "HOME=/home/gitpod")

fullArgs = append(fullArgs, subcommand)
fullArgs = append(fullArgs, args...)

Expand All @@ -204,19 +201,13 @@ func (c *Client) GitWithOutput(ctx context.Context, ignoreErr *string, subcomman
span.LogKV("args", fullArgs)

cmdName := "git"
if c.RunAsGitpodUser {
cmdName = "sudo"
fullArgs = append([]string{"-u", "gitpod", "git"}, fullArgs...)
}
cmd := exec.Command(cmdName, fullArgs...)
cmd.Dir = c.Location
cmd.Env = env
if c.RunAs != nil {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
if cmd.SysProcAttr.Credential == nil {
cmd.SysProcAttr.Credential = &syscall.Credential{}
}
cmd.SysProcAttr.Credential.Uid = c.RunAs.UID
cmd.SysProcAttr.Credential.Gid = c.RunAs.UID
}

res, err := cmd.CombinedOutput()
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions components/content-service/pkg/initializer/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ func (ws *GitInitializer) Run(ctx context.Context, mappings []archive.IDMapping)

// make sure that folder itself is owned by gitpod user prior to doing git clone
// this is needed as otherwise git clone will fail if the folder is owned by root
if ws.RunAs != nil {
args := []string{fmt.Sprintf("%d:%d", ws.RunAs.UID, ws.RunAs.GID), ws.Location}
if ws.RunAsGitpodUser {
args := []string{"gitpod", ws.Location}
cmd := exec.Command("chown", args...)
res, cerr := cmd.CombinedOutput()
if cerr != nil && !process.IsNotChildProcess(cerr) {
Expand Down
25 changes: 9 additions & 16 deletions components/content-service/pkg/initializer/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,11 @@ func (e CompositeInitializer) Run(ctx context.Context, mappings []archive.IDMapp

// NewFromRequestOpts configures the initializer produced from a content init request
type NewFromRequestOpts struct {
// RunAs - if not nil - decides the user with which the initiallisation will be executed
RunAs *User
}

type User struct {
UID uint32
GID uint32
// ForceGitpodUserForGit forces gitpod:gitpod ownership on all files produced by the Git initializer.
// For FWB workspaces the content init is run from supervisor which runs as UID 0. Using this flag, the
// Git content is forced to the Gitpod user. All other content (backup, prebuild, snapshot) will already
// have the correct user.
ForceGitpodUserForGit bool
}

// NewFromRequest picks the initializer from the request but does not execute it.
Expand Down Expand Up @@ -134,7 +132,7 @@ func NewFromRequest(ctx context.Context, loc string, rs storage.DirectDownloader
return nil, status.Error(codes.InvalidArgument, "missing Git initializer spec")
}

initializer, err = newGitInitializer(ctx, loc, ir.Git, opts.RunAs)
initializer, err = newGitInitializer(ctx, loc, ir.Git, opts.ForceGitpodUserForGit)
} else if ir, ok := spec.(*csapi.WorkspaceInitializer_Prebuild); ok {
if ir.Prebuild == nil {
return nil, status.Error(codes.InvalidArgument, "missing prebuild initializer spec")
Expand All @@ -148,7 +146,7 @@ func NewFromRequest(ctx context.Context, loc string, rs storage.DirectDownloader
}
var gits []*GitInitializer
for _, gi := range ir.Prebuild.Git {
gitinit, err := newGitInitializer(ctx, loc, gi, opts.RunAs)
gitinit, err := newGitInitializer(ctx, loc, gi, opts.ForceGitpodUserForGit)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -251,7 +249,7 @@ func (bi *fromBackupInitializer) Run(ctx context.Context, mappings []archive.IDM

// newGitInitializer creates a Git initializer based on the request.
// Returns gRPC errors.
func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitializer, runAs *User) (*GitInitializer, error) {
func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitializer, forceGitpodUser bool) (*GitInitializer, error) {
if req.Config == nil {
return nil, status.Error(codes.InvalidArgument, "Git initializer misses config")
}
Expand Down Expand Up @@ -296,11 +294,6 @@ func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitialize
return
})

var user *git.User
if runAs != nil {
user = &git.User{UID: runAs.UID, GID: runAs.GID}
}

log.WithField("location", loc).Debug("using Git initializer")
return &GitInitializer{
Client: git.Client{
Expand All @@ -310,7 +303,7 @@ func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitialize
Config: req.Config.CustomConfig,
AuthMethod: authMethod,
AuthProvider: authProvider,
RunAs: user,
RunAsGitpodUser: forceGitpodUser,
},
TargetMode: targetMode,
CloneTarget: req.CloneTaget,
Expand Down
38 changes: 0 additions & 38 deletions components/supervisor/cmd/dump-initializer.go

This file was deleted.

32 changes: 2 additions & 30 deletions components/supervisor/pkg/supervisor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math"
"net/http"
"net/url"
Expand Down Expand Up @@ -328,35 +329,6 @@ type WorkspaceConfig struct {

// ConfigcatEnabled controls whether configcat is enabled
ConfigcatEnabled bool `env:"GITPOD_CONFIGCAT_ENABLED"`

WorkspaceLinuxUID uint32 `env:"GITPOD_WORKSPACE_LINUX_UID,default=33333"`
WorkspaceLinuxGID uint32 `env:"GITPOD_WORKSPACE_LINUX_GID,default=33333"`

// ContentInitializer - if set - will run the content initializer instead of waiting for the ready file
ContentInitializer string `env:"SUPERVISOR_CONTENT_INITIALIZER"`

// WorkspaceRuntime configures the runtime supervisor is running in
WorkspaceRuntime WorkspaceRuntime `env:"SUPERVISOR_WORKSPACE_RUNTIME,default=container"`
}

type WorkspaceRuntime string

const (
WorkspaceRuntimeContainer WorkspaceRuntime = "container"
WorkspaceRuntimeNextgen WorkspaceRuntime = "nextgen"
WorkspaceRuntimeRunGP WorkspaceRuntime = "rungp"
)

func (rt *WorkspaceRuntime) UnmarshalEnvironmentValue(data string) error {
switch WorkspaceRuntime(data) {
case WorkspaceRuntimeContainer, WorkspaceRuntimeNextgen, WorkspaceRuntimeRunGP:
// everything's fine
default:
return fmt.Errorf("unknown workspace runtime: %s", data)
}

*rt = WorkspaceRuntime(data)
return nil
}

// WorkspaceGitpodToken is a list of tokens that should be added to supervisor's token service.
Expand Down Expand Up @@ -615,7 +587,7 @@ func loadDesktopIDEs(static *StaticConfig) ([]*IDEConfig, error) {
uniqueDesktopIDEs[desktopIDE.Name] = struct{}{}
}

files, err := os.ReadDir(static.DesktopIDERoot)
files, err := ioutil.ReadDir(static.DesktopIDERoot)
if err != nil {
return nil, err
}
Expand Down
10 changes: 5 additions & 5 deletions components/supervisor/pkg/supervisor/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func socketActivationForDocker(parentCtx context.Context, wg *sync.WaitGroup, te
return
}

logFile, err := openDockerUpLogFile(int(cfg.WorkspaceLinuxUID), int(cfg.WorkspaceLinuxGID))
logFile, err := openDockerUpLogFile()
if err != nil {
log.WithError(err).Error("docker-up: cannot open log file")
} else {
Expand Down Expand Up @@ -164,7 +164,7 @@ func listenToDockerSocket(parentCtx context.Context, term *terminal.Mux, cfg *Co
l.Close()
}()

_ = os.Chown(fn, int(cfg.WorkspaceLinuxUID), int(cfg.WorkspaceLinuxGID))
_ = os.Chown(fn, gitpodUID, gitpodGID)

var lastExitErrorTime time.Time
burstAttempts := 0
Expand Down Expand Up @@ -267,19 +267,19 @@ func listenToDockerSocket(parentCtx context.Context, term *terminal.Mux, cfg *Co
return ctx.Err()
}

func openDockerUpLogFile(uid, gid int) (*os.File, error) {
func openDockerUpLogFile() (*os.File, error) {
if err := os.MkdirAll(logsDir, 0755); err != nil {
return nil, xerrors.Errorf("cannot create logs dir: %w", err)
}
if err := os.Chown(logsDir, uid, gid); err != nil {
if err := os.Chown(logsDir, gitpodUID, gitpodGID); err != nil {
return nil, xerrors.Errorf("cannot chown logs dir: %w", err)
}
logFile, err := os.OpenFile(dockerUpLogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, xerrors.Errorf("cannot open docker-up log file: %w", err)
}

if err := os.Chown(dockerUpLogFilePath, uid, gid); err != nil {
if err := os.Chown(dockerUpLogFilePath, gitpodUID, gitpodGID); err != nil {
_ = logFile.Close()
return nil, xerrors.Errorf("cannot chown docker-up log file: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion components/supervisor/pkg/supervisor/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (p *GitTokenProvider) openAccessControl() error {
return err
}
gpCmd := exec.Command(gpPath, "preview", "--external", p.workspaceConfig.GitpodHost+"/access-control")
runAsUser(gpCmd, p.workspaceConfig.WorkspaceLinuxUID, p.workspaceConfig.WorkspaceLinuxGID)
runAsGitpodUser(gpCmd)
if b, err := gpCmd.CombinedOutput(); err != nil {
log.WithField("Stdout", string(b)).WithError(err).Error("failed to exec gp preview to open access control")
return err
Expand Down
30 changes: 14 additions & 16 deletions components/supervisor/pkg/supervisor/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ func (is *InfoService) WorkspaceInfo(ctx context.Context, req *api.WorkspaceInfo
}
}

resp.UserHome = os.Getenv("HOME")
resp.UserHome = "/home/gitpod"

endpoint, host, err := is.cfg.GitpodAPIEndpoint()
if err != nil {
Expand All @@ -763,8 +763,6 @@ type ControlService struct {
publicKey string
hostKey *api.SSHPublicKey

uid, gid int

api.UnimplementedControlServiceServer
}

Expand All @@ -785,24 +783,24 @@ func (c *ControlService) ExposePort(ctx context.Context, req *api.ExposePortRequ
}

// CreateSSHKeyPair create a ssh key pair for the workspace.
func (c *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateSSHKeyPairRequest) (response *api.CreateSSHKeyPairResponse, err error) {
home := os.Getenv("HOME")
if c.privateKey != "" && c.publicKey != "" {
func (ss *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateSSHKeyPairRequest) (response *api.CreateSSHKeyPairResponse, err error) {
home := "/home/gitpod/"
if ss.privateKey != "" && ss.publicKey != "" {
checkKey := func() error {
data, err := os.ReadFile(filepath.Join(home, ".ssh/authorized_keys"))
if err != nil {
return xerrors.Errorf("cannot read file ~/.ssh/authorized_keys: %w", err)
}
if !bytes.Contains(data, []byte(c.publicKey)) {
if !bytes.Contains(data, []byte(ss.publicKey)) {
return xerrors.Errorf("not found special publickey")
}
return nil
}
err := checkKey()
if err == nil {
return &api.CreateSSHKeyPairResponse{
PrivateKey: c.privateKey,
HostKey: c.hostKey,
PrivateKey: ss.privateKey,
HostKey: ss.hostKey,
}, nil
}
log.WithError(err).Error("check authorized_keys failed, will recreate")
Expand All @@ -813,7 +811,7 @@ func (c *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateSS
if err != nil {
return nil, xerrors.Errorf("cannot create tmpfile: %w", err)
}
err = prepareSSHKey(ctx, filepath.Join(dir, "ssh"), c.uid, c.gid)
err = prepareSSHKey(ctx, filepath.Join(dir, "ssh"))
if err != nil {
return nil, xerrors.Errorf("cannot create ssh key pair: %w", err)
}
Expand All @@ -837,7 +835,7 @@ func (c *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateSS
if err != nil {
return nil, xerrors.Errorf("cannot write file ~.ssh/authorized_keys: %w", err)
}
err = os.Chown(filepath.Join(home, ".ssh/authorized_keys"), c.uid, c.gid)
err = os.Chown(filepath.Join(home, ".ssh/authorized_keys"), gitpodUID, gitpodGID)
if err != nil {
return nil, xerrors.Errorf("cannot chown SSH authorized_keys file: %w", err)
}
Expand All @@ -847,25 +845,25 @@ func (c *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateSS
if err != nil {
return nil, status.Errorf(codes.Internal, "cannot create ssh key pair: %v", err)
}
c.privateKey = string(generated.PrivateKey)
c.publicKey = string(generated.PublicKey)
ss.privateKey = string(generated.PrivateKey)
ss.publicKey = string(generated.PublicKey)

hostKey, err := os.ReadFile("/.supervisor/ssh/sshkey.pub")
if err != nil {
log.WithError(err).Error("faled to read host key")
} else {
hostKeyParts := strings.Split(string(hostKey), " ")
if len(hostKeyParts) >= 2 {
c.hostKey = &api.SSHPublicKey{
ss.hostKey = &api.SSHPublicKey{
Type: hostKeyParts[0],
Value: hostKeyParts[1],
}
}
}

return &api.CreateSSHKeyPairResponse{
PrivateKey: c.privateKey,
HostKey: c.hostKey,
PrivateKey: ss.privateKey,
HostKey: ss.hostKey,
}, err
}

Expand Down
Loading