Skip to content

Commit 522845a

Browse files
paulfitzjonathanperret
authored andcommitted
reapply avagin's --unprivileged flag to recent gvisor
Attempt to reapply the --unprivileged flag patch given here: google#4371 (comment)
1 parent bd77833 commit 522845a

File tree

7 files changed

+27
-9
lines changed

7 files changed

+27
-9
lines changed

runsc/cmd/do.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (c *Do) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcommand
140140
conf := args[0].(*config.Config)
141141
waitStatus := args[1].(*unix.WaitStatus)
142142

143-
if conf.Rootless {
143+
if conf.Rootless && !conf.Unprivileged {
144144
if err := specutils.MaybeRunAsRoot(); err != nil {
145145
return util.Errorf("Error executing inside namespace: %v", err)
146146
}

runsc/cmd/gofer.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
153153
g.syncFDs.syncNVProxy()
154154
g.syncFDs.syncUsernsForRootless()
155155

156+
root := "/"
157+
if !conf.Unprivileged {
156158
if g.setUpRoot {
157159
if err := g.setupRootFS(spec, conf); err != nil {
158160
util.Fatalf("Error setting up root FS: %v", err)
@@ -170,11 +172,13 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
170172
util.Fatalf("setCapsAndCallSelf(%v, %v): %v", args, goferCaps, setCapsAndCallSelf(args, goferCaps))
171173
panic("unreachable")
172174
}
175+
}
173176

174177
// Start profiling. This will be a noop if no profiling arguments were passed.
175178
profileOpts := g.profileFDs.ToOpts()
176179
g.stopProfiling = profile.Start(profileOpts)
177180

181+
if !conf.Unprivileged {
178182
// At this point we won't re-execute, so it's safe to limit via rlimits. Any
179183
// limit >= 0 works. If the limit is lower than the current number of open
180184
// files, then Setrlimit will succeed, and the next open will fail.
@@ -193,10 +197,11 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
193197
}
194198

195199
// Find what path is going to be served by this gofer.
196-
root := spec.Root.Path
200+
root = spec.Root.Path
197201
if !conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
198202
root = "/root"
199203
}
204+
}
200205

201206
// Resolve mount points paths, then replace mounts from our spec and send the
202207
// mount list over to the sandbox, so they are both in sync.
@@ -231,13 +236,15 @@ func (g *Gofer) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomm
231236
// procfs isn't needed anymore.
232237
g.syncFDs.unmountProcfs()
233238

239+
if root != "/" {
234240
if err := unix.Chroot(root); err != nil {
235241
util.Fatalf("failed to chroot to %q: %v", root, err)
236242
}
237243
if err := unix.Chdir("/"); err != nil {
238244
util.Fatalf("changing working dir: %v", err)
239245
}
240246
log.Infof("Process chroot'd to %q", root)
247+
}
241248

242249
// Initialize filters.
243250
opts := filter.Options{

runsc/cmd/run.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ func (r *Run) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomman
8484
return util.Errorf("sandbox network isn't supported with --rootless, use --network=none or --network=host")
8585
}
8686

87+
if !conf.Unprivileged {
8788
if err := specutils.MaybeRunAsRoot(); err != nil {
8889
return util.Errorf("Error executing inside namespace: %v", err)
8990
}
91+
}
9092
// Execution will continue here if no more capabilities are needed...
9193
}
9294

runsc/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ type Config struct {
260260
// should not have a symlink.
261261
Rootless bool `flag:"rootless"`
262262

263+
Unprivileged bool `flag:"unprivileged"`
264+
263265
// AlsoLogToStderr allows to send log messages to stderr.
264266
AlsoLogToStderr bool `flag:"alsologtostderr"`
265267

runsc/config/flags.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func RegisterFlags(flagSet *flag.FlagSet) {
8585
flagSet.String("profile-mutex", "", "collects a mutex profile to this file path for the duration of the container execution. Requires -profile=true.")
8686
flagSet.String("trace", "", "collects a Go runtime execution trace to this file path for the duration of the container execution.")
8787
flagSet.Bool("rootless", false, "it allows the sandbox to be started with a user that is not root. Sandbox and Gofer processes may run with same privileges as current user.")
88+
flagSet.Bool("unprivileged", false, "it allows the sandbox to be started with a user that is not root and doesn't have privileges to create a new user namespace. Sandbox and Gofer processes may run with same privileges as current user.")
8889
flagSet.Var(leakModePtr(refs.NoLeakChecking), "ref-leak-mode", "sets reference leak check mode: disabled (default), log-names, log-traces.")
8990
flagSet.Bool("cpu-num-from-quota", false, "set cpu number to cpu quota (least integer greater or equal to quota value, but not less than 2)")
9091
flagSet.Bool("oci-seccomp", false, "Enables loading OCI seccomp filters inside the sandbox.")

runsc/container/container.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,15 +1322,17 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
13221322

13231323
// Enter new namespaces to isolate from the rest of the system. Don't unshare
13241324
// cgroup because gofer is added to a cgroup in the caller's namespace.
1325-
nss := []specs.LinuxNamespace{
1325+
nss := []specs.LinuxNamespace{}
1326+
rootlessEUID := unix.Geteuid() != 0
1327+
if !conf.Unprivileged {
1328+
nss = []specs.LinuxNamespace{
13261329
{Type: specs.IPCNamespace},
13271330
{Type: specs.MountNamespace},
13281331
{Type: specs.NetworkNamespace},
13291332
{Type: specs.PIDNamespace},
13301333
{Type: specs.UTSNamespace},
1331-
}
1334+
}
13321335

1333-
rootlessEUID := unix.Geteuid() != 0
13341336
// Setup any uid/gid mappings, and create or join the configured user
13351337
// namespace so the gofer's view of the filesystem aligns with the
13361338
// users in the sandbox.
@@ -1353,6 +1355,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
13531355
}
13541356
defer syncFile.Close()
13551357
}
1358+
}
13561359

13571360
nvProxySetup, err := nvproxySetupAfterGoferUserns(spec, conf, cmd, &donations)
13581361
if err != nil {
@@ -1372,7 +1375,7 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
13721375
c.goferIsChild = true
13731376

13741377
// Set up and synchronize rootless mode userns mappings.
1375-
if rootlessEUID {
1378+
if rootlessEUID && !conf.Unprivileged {
13761379
if err := sandbox.SetUserMappings(spec, cmd.Process.Pid); err != nil {
13771380
return nil, nil, nil, err
13781381
}

runsc/sandbox/sandbox.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -958,8 +958,11 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
958958
// are virtualized inside the sandbox. Be paranoid and run inside an empty
959959
// namespace for these. Don't unshare cgroup because sandbox is added to a
960960
// cgroup in the caller's namespace.
961+
nss := []specs.LinuxNamespace{}
962+
setUserMappings := false
963+
if !conf.Unprivileged {
961964
log.Infof("Sandbox will be started in new mount, IPC and UTS namespaces")
962-
nss := []specs.LinuxNamespace{
965+
nss = []specs.LinuxNamespace{
963966
{Type: specs.IPCNamespace},
964967
{Type: specs.MountNamespace},
965968
{Type: specs.UTSNamespace},
@@ -997,7 +1000,6 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
9971000
// namespace specified in the spec or the current namespace if none is
9981001
// configured.
9991002
rootlessEUID := unix.Geteuid() != 0
1000-
setUserMappings := false
10011003
if conf.Network == config.NetworkHost || conf.DirectFS {
10021004
if userns, ok := specutils.GetNS(specs.UserNamespace, args.Spec); ok {
10031005
log.Infof("Sandbox will be started in container's user namespace: %+v", userns)
@@ -1088,6 +1090,7 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
10881090
return fmt.Errorf("can't run sandbox process as user nobody since we don't have CAP_SETUID or CAP_SETGID")
10891091
}
10901092
}
1093+
}
10911094

10921095
// The current process' stdio must be passed to the application via the
10931096
// --stdio-fds flag. The stdio of the sandbox process itself must not
@@ -1696,7 +1699,7 @@ func (s *Sandbox) waitForStopped() error {
16961699
// configureStdios change stdios ownership to give access to the sandbox
16971700
// process. This may be skipped depending on the configuration.
16981701
func (s *Sandbox) configureStdios(conf *config.Config, stdios []*os.File) error {
1699-
if conf.Rootless || conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
1702+
if conf.Unprivileged || conf.Rootless || conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
17001703
// Cannot change ownership without CAP_CHOWN.
17011704
return nil
17021705
}

0 commit comments

Comments
 (0)