Skip to content

Commit 37cb858

Browse files
committed
Make linux user configurable for Git operations
1 parent ff2966a commit 37cb858

File tree

5 files changed

+54
-21
lines changed

5 files changed

+54
-21
lines changed

components/content-service/pkg/executor/executor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func Prepare(req *csapi.WorkspaceInitializer, urls map[string]string) ([]byte, e
4444

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

6565
rs = &storage.NamedURLDownloader{URLs: cfg.URLs}
6666
ilr, err = initializer.NewFromRequest(ctx, destination, rs, &req, initializer.NewFromRequestOpts{
67-
ForceGitpodUserForGit: forceGitUser,
67+
RunAs: runAs,
6868
})
6969
if err != nil {
7070
return "", err

components/content-service/pkg/git/git.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os/exec"
1414
"path/filepath"
1515
"strings"
16+
"syscall"
1617

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

96-
// if true will run git command as gitpod user (should be executed as root that has access to sudo in this case)
97-
RunAsGitpodUser bool
97+
// RunAs runs the Git commands as a particular user - if not nil
98+
RunAs *User
99+
}
100+
101+
type User struct {
102+
UID, GID uint32
98103
}
99104

100105
// Status describes the status of a Git repo/working copy akin to "git status"
@@ -201,13 +206,19 @@ func (c *Client) GitWithOutput(ctx context.Context, ignoreErr *string, subcomman
201206
span.LogKV("args", fullArgs)
202207

203208
cmdName := "git"
204-
if c.RunAsGitpodUser {
205-
cmdName = "sudo"
206-
fullArgs = append([]string{"-u", "gitpod", "git"}, fullArgs...)
207-
}
208209
cmd := exec.Command(cmdName, fullArgs...)
209210
cmd.Dir = c.Location
210211
cmd.Env = env
212+
if c.RunAs != nil {
213+
if cmd.SysProcAttr == nil {
214+
cmd.SysProcAttr = &syscall.SysProcAttr{}
215+
}
216+
if cmd.SysProcAttr.Credential == nil {
217+
cmd.SysProcAttr.Credential = &syscall.Credential{}
218+
}
219+
cmd.SysProcAttr.Credential.Uid = c.RunAs.UID
220+
cmd.SysProcAttr.Credential.Gid = c.RunAs.UID
221+
}
211222

212223
res, err := cmd.CombinedOutput()
213224
if err != nil {

components/content-service/pkg/initializer/git.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ func (ws *GitInitializer) Run(ctx context.Context, mappings []archive.IDMapping)
9595

9696
// make sure that folder itself is owned by gitpod user prior to doing git clone
9797
// this is needed as otherwise git clone will fail if the folder is owned by root
98-
if ws.RunAsGitpodUser {
99-
args := []string{"gitpod", ws.Location}
98+
if ws.RunAs != nil {
99+
args := []string{fmt.Sprintf("%d:%d", ws.RunAs.UID, ws.RunAs.GID), ws.Location}
100100
cmd := exec.Command("chown", args...)
101101
res, cerr := cmd.CombinedOutput()
102102
if cerr != nil && !process.IsNotChildProcess(cerr) {

components/content-service/pkg/initializer/initializer.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,13 @@ func (e CompositeInitializer) Run(ctx context.Context, mappings []archive.IDMapp
9999

100100
// NewFromRequestOpts configures the initializer produced from a content init request
101101
type NewFromRequestOpts struct {
102-
// ForceGitpodUserForGit forces gitpod:gitpod ownership on all files produced by the Git initializer.
103-
// For FWB workspaces the content init is run from supervisor which runs as UID 0. Using this flag, the
104-
// Git content is forced to the Gitpod user. All other content (backup, prebuild, snapshot) will already
105-
// have the correct user.
106-
ForceGitpodUserForGit bool
102+
// RunAs - if not nil - decides the user with which the initiallisation will be executed
103+
RunAs *User
104+
}
105+
106+
type User struct {
107+
UID uint32
108+
GID uint32
107109
}
108110

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

135-
initializer, err = newGitInitializer(ctx, loc, ir.Git, opts.ForceGitpodUserForGit)
137+
initializer, err = newGitInitializer(ctx, loc, ir.Git, opts.RunAs)
136138
} else if ir, ok := spec.(*csapi.WorkspaceInitializer_Prebuild); ok {
137139
if ir.Prebuild == nil {
138140
return nil, status.Error(codes.InvalidArgument, "missing prebuild initializer spec")
@@ -146,7 +148,7 @@ func NewFromRequest(ctx context.Context, loc string, rs storage.DirectDownloader
146148
}
147149
var gits []*GitInitializer
148150
for _, gi := range ir.Prebuild.Git {
149-
gitinit, err := newGitInitializer(ctx, loc, gi, opts.ForceGitpodUserForGit)
151+
gitinit, err := newGitInitializer(ctx, loc, gi, opts.RunAs)
150152
if err != nil {
151153
return nil, err
152154
}
@@ -249,7 +251,7 @@ func (bi *fromBackupInitializer) Run(ctx context.Context, mappings []archive.IDM
249251

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

299+
var user *git.User
300+
if runAs != nil {
301+
user = &git.User{UID: runAs.UID, GID: runAs.GID}
302+
}
303+
297304
log.WithField("location", loc).Debug("using Git initializer")
298305
return &GitInitializer{
299306
Client: git.Client{
@@ -303,7 +310,7 @@ func newGitInitializer(ctx context.Context, loc string, req *csapi.GitInitialize
303310
Config: req.Config.CustomConfig,
304311
AuthMethod: authMethod,
305312
AuthProvider: authProvider,
306-
RunAsGitpodUser: forceGitpodUser,
313+
RunAs: user,
307314
},
308315
TargetMode: targetMode,
309316
CloneTarget: req.CloneTaget,

components/supervisor/pkg/supervisor/supervisor.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import (
5454
csapi "github.com/gitpod-io/gitpod/content-service/api"
5555
"github.com/gitpod-io/gitpod/content-service/pkg/executor"
5656
"github.com/gitpod-io/gitpod/content-service/pkg/git"
57+
"github.com/gitpod-io/gitpod/content-service/pkg/initializer"
5758
gitpod "github.com/gitpod-io/gitpod/gitpod-protocol"
5859
"github.com/gitpod-io/gitpod/supervisor/api"
5960
"github.com/gitpod-io/gitpod/supervisor/pkg/config"
@@ -1537,8 +1538,22 @@ func startContentInit(ctx context.Context, cfg *Config, wg *sync.WaitGroup, cst
15371538
}
15381539

15391540
log.Info("supervisor: running content service executor with content descriptor")
1540-
var src csapi.WorkspaceInitSource
1541-
src, err = executor.Execute(ctx, "/workspace", bytes.NewReader(contentFile), true)
1541+
var (
1542+
src csapi.WorkspaceInitSource
1543+
user *initializer.User
1544+
)
1545+
if cfg.WorkspaceRuntime == WorkspaceRuntimeNextgen {
1546+
user = &initializer.User{
1547+
UID: cfg.WorkspaceLinuxUID,
1548+
GID: cfg.WorkspaceLinuxGID,
1549+
}
1550+
} else {
1551+
user = &initializer.User{
1552+
UID: legacyGitpodUID,
1553+
GID: legacyGitpodGID,
1554+
}
1555+
}
1556+
src, err = executor.Execute(ctx, "/workspace", bytes.NewReader(contentFile), user)
15421557
if err != nil {
15431558
return
15441559
}

0 commit comments

Comments
 (0)