Skip to content

Commit c4f56df

Browse files
Furistocsweichel
authored andcommitted
Revert "Revert "Relax assumptions in supervisor (#18782)" (#18857)"
This reverts commit 40c39f5.
1 parent 5e6fa13 commit c4f56df

File tree

14 files changed

+224
-112
lines changed

14 files changed

+224
-112
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 & 8 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"
@@ -178,8 +183,6 @@ func (c *Client) GitWithOutput(ctx context.Context, ignoreErr *string, subcomman
178183
env = append(env, fmt.Sprintf("GIT_AUTH_PASSWORD=%s", pwd))
179184
}
180185

181-
env = append(env, "HOME=/home/gitpod")
182-
183186
fullArgs = append(fullArgs, subcommand)
184187
fullArgs = append(fullArgs, args...)
185188

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

203206
cmdName := "git"
204-
if c.RunAsGitpodUser {
205-
cmdName = "sudo"
206-
fullArgs = append([]string{"-u", "gitpod", "git"}, fullArgs...)
207-
}
208207
cmd := exec.Command(cmdName, fullArgs...)
209208
cmd.Dir = c.Location
210209
cmd.Env = env
210+
if c.RunAs != nil {
211+
if cmd.SysProcAttr == nil {
212+
cmd.SysProcAttr = &syscall.SysProcAttr{}
213+
}
214+
if cmd.SysProcAttr.Credential == nil {
215+
cmd.SysProcAttr.Credential = &syscall.Credential{}
216+
}
217+
cmd.SysProcAttr.Credential.Uid = c.RunAs.UID
218+
cmd.SysProcAttr.Credential.Gid = c.RunAs.UID
219+
}
211220

212221
res, err := cmd.CombinedOutput()
213222
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,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package cmd
6+
7+
import (
8+
"fmt"
9+
10+
csapi "github.com/gitpod-io/gitpod/content-service/api"
11+
"github.com/gitpod-io/gitpod/content-service/pkg/executor"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var dumpInitializer = &cobra.Command{
16+
Use: "dump-init",
17+
Hidden: true, // this is not official user-facing functionality, but just for debugging
18+
Run: func(cmd *cobra.Command, args []string) {
19+
fc, _ := executor.Prepare(&csapi.WorkspaceInitializer{
20+
Spec: &csapi.WorkspaceInitializer_Git{
21+
Git: &csapi.GitInitializer{
22+
RemoteUri: "https://github.com/gitpod-io/gitpod",
23+
CheckoutLocation: "gitpod",
24+
TargetMode: csapi.CloneTargetMode_REMOTE_BRANCH,
25+
CloneTaget: "main",
26+
Config: &csapi.GitConfig{
27+
Authentication: csapi.GitAuthMethod_NO_AUTH,
28+
},
29+
},
30+
},
31+
}, nil)
32+
fmt.Println(string(fc))
33+
},
34+
}
35+
36+
func init() {
37+
rootCmd.AddCommand(dumpInitializer)
38+
}

components/supervisor/pkg/supervisor/config.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"encoding/json"
99
"fmt"
1010
"io"
11-
"io/ioutil"
1211
"math"
1312
"net/http"
1413
"net/url"
@@ -329,6 +328,35 @@ type WorkspaceConfig struct {
329328

330329
// ConfigcatEnabled controls whether configcat is enabled
331330
ConfigcatEnabled bool `env:"GITPOD_CONFIGCAT_ENABLED"`
331+
332+
WorkspaceLinuxUID uint32 `env:"GITPOD_WORKSPACE_LINUX_UID,default=33333"`
333+
WorkspaceLinuxGID uint32 `env:"GITPOD_WORKSPACE_LINUX_GID,default=33333"`
334+
335+
// ContentInitializer - if set - will run the content initializer instead of waiting for the ready file
336+
ContentInitializer string `env:"SUPERVISOR_CONTENT_INITIALIZER"`
337+
338+
// WorkspaceRuntime configures the runtime supervisor is running in
339+
WorkspaceRuntime WorkspaceRuntime `env:"SUPERVISOR_WORKSPACE_RUNTIME,default=container"`
340+
}
341+
342+
type WorkspaceRuntime string
343+
344+
const (
345+
WorkspaceRuntimeContainer WorkspaceRuntime = "container"
346+
WorkspaceRuntimeNextgen WorkspaceRuntime = "nextgen"
347+
WorkspaceRuntimeRunGP WorkspaceRuntime = "rungp"
348+
)
349+
350+
func (rt *WorkspaceRuntime) UnmarshalEnvironmentValue(data string) error {
351+
switch WorkspaceRuntime(data) {
352+
case WorkspaceRuntimeContainer, WorkspaceRuntimeNextgen, WorkspaceRuntimeRunGP:
353+
// everything's fine
354+
default:
355+
return fmt.Errorf("unknown workspace runtime: %s", data)
356+
}
357+
358+
*rt = WorkspaceRuntime(data)
359+
return nil
332360
}
333361

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

590-
files, err := ioutil.ReadDir(static.DesktopIDERoot)
618+
files, err := os.ReadDir(static.DesktopIDERoot)
591619
if err != nil {
592620
return nil, err
593621
}

components/supervisor/pkg/supervisor/docker.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func socketActivationForDocker(parentCtx context.Context, wg *sync.WaitGroup, te
5757
return
5858
}
5959

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

167-
_ = os.Chown(fn, gitpodUID, gitpodGID)
167+
_ = os.Chown(fn, int(cfg.WorkspaceLinuxUID), int(cfg.WorkspaceLinuxGID))
168168

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

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

282-
if err := os.Chown(dockerUpLogFilePath, gitpodUID, gitpodGID); err != nil {
282+
if err := os.Chown(dockerUpLogFilePath, uid, gid); err != nil {
283283
_ = logFile.Close()
284284
return nil, xerrors.Errorf("cannot chown docker-up log file: %w", err)
285285
}

components/supervisor/pkg/supervisor/git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (p *GitTokenProvider) openAccessControl() error {
9292
return err
9393
}
9494
gpCmd := exec.Command(gpPath, "preview", "--external", p.workspaceConfig.GitpodHost+"/access-control")
95-
runAsGitpodUser(gpCmd)
95+
runAsUser(gpCmd, p.workspaceConfig.WorkspaceLinuxUID, p.workspaceConfig.WorkspaceLinuxGID)
9696
if b, err := gpCmd.CombinedOutput(); err != nil {
9797
log.WithField("Stdout", string(b)).WithError(err).Error("failed to exec gp preview to open access control")
9898
return err

components/supervisor/pkg/supervisor/services.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ func (is *InfoService) WorkspaceInfo(ctx context.Context, req *api.WorkspaceInfo
741741
}
742742
}
743743

744-
resp.UserHome = "/home/gitpod"
744+
resp.UserHome = os.Getenv("HOME")
745745

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

766+
uid, gid int
767+
766768
api.UnimplementedControlServiceServer
767769
}
768770

@@ -812,7 +814,7 @@ func (ss *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateS
812814
if err != nil {
813815
return nil, xerrors.Errorf("cannot create tmpfile: %w", err)
814816
}
815-
err = prepareSSHKey(ctx, filepath.Join(dir, "ssh"))
817+
err = prepareSSHKey(ctx, filepath.Join(dir, "ssh"), ss.uid, ss.gid)
816818
if err != nil {
817819
return nil, xerrors.Errorf("cannot create ssh key pair: %w", err)
818820
}
@@ -836,7 +838,7 @@ func (ss *ControlService) CreateSSHKeyPair(ctx context.Context, req *api.CreateS
836838
if err != nil {
837839
return nil, xerrors.Errorf("cannot write file ~.ssh/authorized_keys: %w", err)
838840
}
839-
err = os.Chown(filepath.Join(home, ".ssh/authorized_keys"), gitpodUID, gitpodGID)
841+
err = os.Chown(filepath.Join(home, ".ssh/authorized_keys"), ss.uid, ss.gid)
840842
if err != nil {
841843
return nil, xerrors.Errorf("cannot chown SSH authorized_keys file: %w", err)
842844
}

components/supervisor/pkg/supervisor/ssh.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func newSSHServer(ctx context.Context, cfg *Config, envvars []string) (*sshServe
3030

3131
sshkey := filepath.Join(filepath.Dir(bin), "ssh", "sshkey")
3232
if _, err := os.Stat(sshkey); err != nil {
33-
err := prepareSSHKey(ctx, sshkey)
33+
err := prepareSSHKey(ctx, sshkey, int(cfg.WorkspaceLinuxUID), int(cfg.WorkspaceLinuxGID))
3434
if err != nil {
3535
return nil, xerrors.Errorf("unexpected error creating SSH key: %w", err)
3636
}
@@ -145,7 +145,7 @@ func (s *sshServer) handleConn(ctx context.Context, conn net.Conn) {
145145

146146
log.WithField("args", args).Debug("sshd flags")
147147
cmd := exec.CommandContext(ctx, openssh, args...)
148-
cmd = runAsGitpodUser(cmd)
148+
cmd = runAsUser(cmd, s.cfg.WorkspaceLinuxUID, s.cfg.WorkspaceLinuxGID)
149149
cmd.Env = s.envvars
150150
cmd.ExtraFiles = []*os.File{socketFD}
151151
cmd.Stderr = os.Stderr
@@ -183,7 +183,7 @@ func (s *sshServer) handleConn(ctx context.Context, conn net.Conn) {
183183
}
184184
}
185185

186-
func prepareSSHKey(ctx context.Context, sshkey string) error {
186+
func prepareSSHKey(ctx context.Context, sshkey string, uid, gid int) error {
187187
bin, err := os.Executable()
188188
if err != nil {
189189
return xerrors.Errorf("cannot find executable path: %w", err)
@@ -219,7 +219,7 @@ func prepareSSHKey(ctx context.Context, sshkey string) error {
219219
return xerrors.Errorf("cannot create SSH hostkey file: %w", err)
220220
}
221221

222-
err = os.Chown(sshkey, gitpodUID, gitpodGID)
222+
err = os.Chown(sshkey, uid, gid)
223223
if err != nil {
224224
return xerrors.Errorf("cannot chown SSH hostkey file: %w", err)
225225
}
@@ -235,7 +235,7 @@ func ensureSSHDir(cfg *Config) error {
235235
if err != nil {
236236
return xerrors.Errorf("cannot create $HOME/.ssh: %w", err)
237237
}
238-
_ = exec.Command("chown", "-R", fmt.Sprintf("%d:%d", gitpodUID, gitpodGID), d).Run()
238+
_ = exec.Command("chown", "-R", fmt.Sprintf("%d:%d", cfg.WorkspaceLinuxUID, cfg.WorkspaceLinuxGID), d).Run()
239239

240240
return nil
241241
}

0 commit comments

Comments
 (0)