mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Allow sandbox PID namespace to be used
Identify the sandbox PID namespace path and translate it to the root PID namespace. This is one step towards supporting K8s ephemeral container target. Currently, the target always comes as the sandbox because containerd uses the process PID (in gVisor's case the sandbox PID) to identify the container. Instead of landing in the desired container, the root namespace is targeted to allow all processes to be visible and debugged. PiperOrigin-RevId: 531272178
This commit is contained in:
committed by
gVisor bot
parent
6b52411e67
commit
570973f0be
@@ -801,12 +801,14 @@ func (l *Loader) startSubcontainer(spec *specs.Spec, conf *config.Config, cid st
|
||||
if ns.Path != "" {
|
||||
for _, p := range l.processes {
|
||||
if ns.Path == p.pidnsPath {
|
||||
log.Debugf("Joining PID namespace named %q", ns.Path)
|
||||
pidns = p.tg.PIDNamespace()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if pidns == nil {
|
||||
log.Warningf("PID namespace %q not found, running in new PID namespace", ns.Path)
|
||||
pidns = l.k.RootPIDNamespace().NewChild(l.k.RootUserNamespace())
|
||||
}
|
||||
ep.pidnsPath = ns.Path
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ type Create struct {
|
||||
// userLog is the path to send user-visible logs to. This log is different
|
||||
// from debug logs. The former is meant to be consumed by the users and should
|
||||
// contain only information that is relevant to the person running the
|
||||
// container, e.g. unsuported syscalls, while the later is more verbose and
|
||||
// container, e.g. unsupported syscalls, while the later is more verbose and
|
||||
// consumed by developers.
|
||||
userLog string
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ func TestMultiContainerSanity(t *testing.T) {
|
||||
// TestMultiPIDNS checks that it is possible to run 2 dead-simple containers in
|
||||
// the same sandbox with different pidns.
|
||||
func TestMultiPIDNS(t *testing.T) {
|
||||
for name, conf := range configs(t, false /* noOverlay */) {
|
||||
for name, conf := range configs(t, true /* noOverlay */) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rootDir, cleanup, err := testutil.SetupRootDir()
|
||||
if err != nil {
|
||||
@@ -248,7 +248,7 @@ func TestMultiPIDNS(t *testing.T) {
|
||||
|
||||
// TestMultiPIDNSPath checks the pidns path.
|
||||
func TestMultiPIDNSPath(t *testing.T) {
|
||||
for name, conf := range configs(t, false /* noOverlay */) {
|
||||
for name, conf := range configs(t, true /* noOverlay */) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rootDir, cleanup, err := testutil.SetupRootDir()
|
||||
if err != nil {
|
||||
@@ -366,7 +366,7 @@ func TestMultiPIDNSKill(t *testing.T) {
|
||||
t.Fatal("error finding test_app:", err)
|
||||
}
|
||||
|
||||
for name, conf := range configs(t, false /* noOverlay */) {
|
||||
for name, conf := range configs(t, true /* noOverlay */) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rootDir, cleanup, err := testutil.SetupRootDir()
|
||||
if err != nil {
|
||||
@@ -451,6 +451,93 @@ func TestMultiPIDNSKill(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiPIDNSRoot checks that the sandbox PID namespace can be used to
|
||||
// reference the root PID namespace of the sandbox.
|
||||
func TestMultiPIDNSRoot(t *testing.T) {
|
||||
for name, conf := range configs(t, true /* noOverlay */) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
rootDir, cleanup, err := testutil.SetupRootDir()
|
||||
if err != nil {
|
||||
t.Fatalf("error creating root dir: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
conf.RootDir = rootDir
|
||||
|
||||
// Setup the containers. One in the root PID namespace and another in a
|
||||
// sub-namespace.
|
||||
sleep := []string{"sleep", "100"}
|
||||
testSpecs, ids := createSpecs(sleep, sleep, sleep)
|
||||
testSpecs[1].Linux = &specs.Linux{
|
||||
Namespaces: []specs.LinuxNamespace{
|
||||
{
|
||||
Type: "pid",
|
||||
Path: "/proc/1/ns/pid",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Start 2 containers first, and use the 3rd to join the sandbox pidns.
|
||||
delayedSpec, delayedID := testSpecs[2], ids[2]
|
||||
testSpecs = testSpecs[:2]
|
||||
ids = ids[:2]
|
||||
|
||||
containers, cleanup, err := startContainers(conf, testSpecs, ids)
|
||||
if err != nil {
|
||||
t.Fatalf("error starting containers: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
delayedSpec.Linux = &specs.Linux{
|
||||
Namespaces: []specs.LinuxNamespace{
|
||||
{
|
||||
Type: "pid",
|
||||
Path: fmt.Sprintf("/proc/%d/ns/pid", containers[0].SandboxPid()),
|
||||
},
|
||||
},
|
||||
}
|
||||
delayed, cleanup, err := startContainers(conf, []*specs.Spec{delayedSpec}, []string{delayedID})
|
||||
if err != nil {
|
||||
t.Fatalf("error starting sub-container: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Wait for all container processes to be up and running.
|
||||
expectedPL := []*control.Process{
|
||||
newProcessBuilder().PID(1).PPID(0).Cmd("sleep").Process(),
|
||||
}
|
||||
if err := waitForProcessList(containers[0], expectedPL); err != nil {
|
||||
t.Errorf("failed to wait for sleep to start: %v", err)
|
||||
}
|
||||
expectedPL = []*control.Process{
|
||||
newProcessBuilder().PID(2).PPID(0).Cmd("sleep").Process(),
|
||||
}
|
||||
if err := waitForProcessList(containers[1], expectedPL); err != nil {
|
||||
t.Fatalf("failed to wait for sleep to start: %v", err)
|
||||
}
|
||||
expectedPL = []*control.Process{
|
||||
newProcessBuilder().PID(3).PPID(0).Cmd("sleep").Process(),
|
||||
}
|
||||
if err := waitForProcessList(delayed[0], expectedPL); err != nil {
|
||||
t.Fatalf("failed to wait for sleep to start: %v", err)
|
||||
}
|
||||
|
||||
// Check that delayer container is running in the root PID namespace and
|
||||
// can see all other processes.
|
||||
expectedPL = []*control.Process{
|
||||
newProcessBuilder().PID(1).Cmd("sleep").Process(),
|
||||
newProcessBuilder().PID(2).Cmd("sleep").Process(),
|
||||
newProcessBuilder().PID(3).Cmd("sleep").Process(),
|
||||
newProcessBuilder().Cmd("ps").Process(),
|
||||
}
|
||||
if got, err := execPS(conf, delayed[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if !procListsEqual(got, expectedPL) {
|
||||
t.Fatalf("container got process list: %s, want: %s", procListToString(got), procListToString(expectedPL))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiContainerWait(t *testing.T) {
|
||||
rootDir, cleanup, err := testutil.SetupRootDir()
|
||||
if err != nil {
|
||||
|
||||
@@ -403,6 +403,7 @@ func (s *Sandbox) StartSubcontainer(spec *specs.Spec, conf *config.Config, cid s
|
||||
if err := s.configureStdios(conf, stdios); err != nil {
|
||||
return err
|
||||
}
|
||||
s.fixPidns(spec)
|
||||
|
||||
// The payload contains (in this specific order):
|
||||
// * stdin/stdout/stderr (optional: only present when not using TTY)
|
||||
@@ -1518,3 +1519,29 @@ func (s *Sandbox) CgroupsWriteControlFile(file control.CgroupControlFile, value
|
||||
}
|
||||
return out.Results[0].AsError()
|
||||
}
|
||||
|
||||
// fixPidns looks at the PID namespace path. If that path corresponds to the
|
||||
// sandbox process PID namespace, then change the spec so that the container
|
||||
// joins the sandbox root namespace.
|
||||
func (s *Sandbox) fixPidns(spec *specs.Spec) {
|
||||
pidns, ok := specutils.GetNS(specs.PIDNamespace, spec)
|
||||
if !ok {
|
||||
// pidns was not set, nothing to fix.
|
||||
return
|
||||
}
|
||||
if pidns.Path != fmt.Sprintf("/proc/%d/ns/pid", s.Pid.load()) {
|
||||
// Fix only if the PID namespace corresponds to the sandbox's.
|
||||
return
|
||||
}
|
||||
|
||||
for i := range spec.Linux.Namespaces {
|
||||
if spec.Linux.Namespaces[i].Type == specs.PIDNamespace {
|
||||
// Removing the namespace makes the container join the sandbox root
|
||||
// namespace.
|
||||
log.Infof("Fixing PID namespace in spec from %q to make the container join the sandbox root namespace", pidns.Path)
|
||||
spec.Linux.Namespaces = append(spec.Linux.Namespaces[:i], spec.Linux.Namespaces[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user