Skip to content

Commit 057bebe

Browse files
authored
Merge pull request microsoft#1178 from dcantah/exec-username
Rework LCOW username setup/exec behavior
2 parents 3046e94 + b3b21da commit 057bebe

7 files changed

Lines changed: 153 additions & 12 deletions

File tree

internal/guest/runtime/hcsv2/container.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ func (c *Container) ExecProcess(ctx context.Context, process *oci.Process, conSe
6969
// Add in the core rlimit specified on the container in case there was one set. This makes it so that execed processes can also generate
7070
// core dumps.
7171
process.Rlimits = c.spec.Process.Rlimits
72+
73+
// If the client provided a user for the container to run as, we want to have the exec run as this user as well
74+
// unless the exec's spec was explicitly set to a different user. If the Username field is filled in on the containers
75+
// spec, at this point that means the work to find a uid:gid pairing for this username has already been done, so simply
76+
// assign the uid:gid from the container.
77+
if process.User.Username != "" {
78+
// The exec provided a user string of it's own. Grab the uid:gid pairing for the string (if one exists).
79+
if err := setUserStr(&oci.Spec{Root: c.spec.Root, Process: process}, process.User.Username); err != nil {
80+
return -1, err
81+
}
82+
// Runc doesn't care about this, and just to be safe clear it.
83+
process.User.Username = ""
84+
} else if c.spec.Process.User.Username != "" {
85+
process.User = c.spec.Process.User
86+
}
87+
7288
p, err := c.container.ExecProcess(process, stdioSet)
7389
if err != nil {
7490
stdioSet.Close()

internal/guest/runtime/hcsv2/sandbox_container.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,12 @@ func setupSandboxContainerSpec(ctx context.Context, id string, spec *oci.Spec) (
102102
return errors.Wrap(err, "failed to write sandbox resolv.conf")
103103
}
104104

105-
if userstr, ok := spec.Annotations["io.microsoft.lcow.userstr"]; ok {
106-
if err := setUserStr(spec, userstr); err != nil {
105+
// User.Username is generally only used on Windows, but as there's no (easy/fast at least) way to grab
106+
// a uid:gid pairing for a username string on the host, we need to defer this work until we're here in the
107+
// guest. The username field is used as a temporary holding place until we can perform this work here when
108+
// we actually have the rootfs to inspect.
109+
if spec.Process.User.Username != "" {
110+
if err := setUserStr(spec, spec.Process.User.Username); err != nil {
107111
return err
108112
}
109113
}

internal/guest/runtime/hcsv2/spec.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ package hcsv2
55
import (
66
"context"
77
"fmt"
8-
"github.com/Microsoft/hcsshim/internal/log"
9-
"github.com/opencontainers/runc/libcontainer/devices"
108
"path/filepath"
119
"strconv"
1210
"strings"
1311

12+
"github.com/Microsoft/hcsshim/internal/log"
13+
"github.com/opencontainers/runc/libcontainer/devices"
1414
"github.com/opencontainers/runc/libcontainer/user"
1515
oci "github.com/opencontainers/runtime-spec/specs-go"
1616
"github.com/pkg/errors"
@@ -251,11 +251,5 @@ func applyAnnotationsToSpec(ctx context.Context, spec *oci.Spec) error {
251251
}
252252
}
253253

254-
// Check if we need to set non-default user
255-
if userstr, ok := spec.Annotations["io.microsoft.lcow.userstr"]; ok {
256-
if err := setUserStr(spec, userstr); err != nil {
257-
return err
258-
}
259-
}
260254
return nil
261255
}

internal/guest/runtime/hcsv2/workload_container.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ func setupWorkloadContainerSpec(ctx context.Context, sbid, id string, spec *oci.
167167
}
168168
}
169169

170+
// User.Username is generally only used on Windows, but as there's no (easy/fast at least) way to grab
171+
// a uid:gid pairing for a username string on the host, we need to defer this work until we're here in the
172+
// guest. The username field is used as a temporary holding place until we can perform this work here when
173+
// we actually have the rootfs to inspect.
174+
if spec.Process.User.Username != "" {
175+
if err := setUserStr(spec, spec.Process.User.Username); err != nil {
176+
return err
177+
}
178+
}
179+
170180
// Force the parent cgroup into our /containers root
171181
spec.Linux.CgroupsPath = "/containers/" + id
172182

test/cri-containerd/container_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,3 +893,115 @@ func Test_CreateContainer_HugePageMount_LCOW(t *testing.T) {
893893
t.Fatalf("output is supposed to contain pagesize=2M, output: %s", output)
894894
}
895895
}
896+
897+
func Test_RunContainer_ExecUser_LCOW(t *testing.T) {
898+
requireFeatures(t, featureLCOW)
899+
900+
pullRequiredLcowImages(t, []string{imageLcowK8sPause, imageLcowCustomUser})
901+
902+
client := newTestRuntimeClient(t)
903+
ctx, cancel := context.WithCancel(context.Background())
904+
defer cancel()
905+
906+
sandboxRequest := getRunPodSandboxRequest(t, lcowRuntimeHandler, nil)
907+
908+
podID := runPodSandbox(t, client, ctx, sandboxRequest)
909+
defer removePodSandbox(t, client, ctx, podID)
910+
defer stopPodSandbox(t, client, ctx, podID)
911+
912+
cmd := []string{"sh", "-c", "while true; do sleep 1; done"}
913+
request := &runtime.CreateContainerRequest{
914+
PodSandboxId: podID,
915+
Config: &runtime.ContainerConfig{
916+
Metadata: &runtime.ContainerMetadata{
917+
Name: t.Name() + "-Container",
918+
},
919+
Image: &runtime.ImageSpec{
920+
Image: imageLcowCustomUser,
921+
},
922+
Command: cmd,
923+
},
924+
SandboxConfig: sandboxRequest.Config,
925+
}
926+
927+
containerID := createContainer(t, client, ctx, request)
928+
defer removeContainer(t, client, ctx, containerID)
929+
startContainer(t, client, ctx, containerID)
930+
defer stopContainer(t, client, ctx, containerID)
931+
932+
// The `imageLcowCustomUser` image has a user created in the image named test that is set to run the init process as. This tests that
933+
// any execed processes will honor the user set for the container also.
934+
cmd = []string{"whoami"}
935+
containerExecReq := &runtime.ExecSyncRequest{
936+
ContainerId: containerID,
937+
Cmd: cmd,
938+
Timeout: 20,
939+
}
940+
r := execSync(t, client, ctx, containerExecReq)
941+
if r.ExitCode != 0 {
942+
t.Fatalf("failed with exit code %d: %s", r.ExitCode, string(r.Stderr))
943+
}
944+
945+
if !strings.Contains(string(r.Stdout), "test") {
946+
t.Fatalf("expected user for exec to be 'test', got %q", string(r.Stdout))
947+
}
948+
}
949+
950+
func Test_RunContainer_ExecUser_Root_LCOW(t *testing.T) {
951+
requireFeatures(t, featureLCOW)
952+
953+
pullRequiredLcowImages(t, []string{imageLcowK8sPause, imageLcowCustomUser})
954+
955+
client := newTestRuntimeClient(t)
956+
ctx, cancel := context.WithCancel(context.Background())
957+
defer cancel()
958+
959+
sandboxRequest := getRunPodSandboxRequest(t, lcowRuntimeHandler, nil)
960+
961+
podID := runPodSandbox(t, client, ctx, sandboxRequest)
962+
defer removePodSandbox(t, client, ctx, podID)
963+
defer stopPodSandbox(t, client, ctx, podID)
964+
965+
// Overide what user to run the container as and see if the exec also runs as root now.
966+
cmd := []string{"sh", "-c", "while true; do sleep 1; done"}
967+
request := &runtime.CreateContainerRequest{
968+
PodSandboxId: podID,
969+
Config: &runtime.ContainerConfig{
970+
Metadata: &runtime.ContainerMetadata{
971+
Name: t.Name() + "-Container",
972+
},
973+
Image: &runtime.ImageSpec{
974+
Image: imageLcowCustomUser,
975+
},
976+
Command: cmd,
977+
Linux: &runtime.LinuxContainerConfig{
978+
SecurityContext: &runtime.LinuxContainerSecurityContext{
979+
RunAsUsername: "root",
980+
},
981+
},
982+
},
983+
SandboxConfig: sandboxRequest.Config,
984+
}
985+
986+
containerID := createContainer(t, client, ctx, request)
987+
defer removeContainer(t, client, ctx, containerID)
988+
startContainer(t, client, ctx, containerID)
989+
defer stopContainer(t, client, ctx, containerID)
990+
991+
// The `imageLcowCustomUser` image has a user created in the image named test that is set to run the init process as. This tests that
992+
// any execed processes will honor the user set for the container also.
993+
cmd = []string{"whoami"}
994+
containerExecReq := &runtime.ExecSyncRequest{
995+
ContainerId: containerID,
996+
Cmd: cmd,
997+
Timeout: 20,
998+
}
999+
r := execSync(t, client, ctx, containerExecReq)
1000+
if r.ExitCode != 0 {
1001+
t.Fatalf("failed with exit code %d: %s", r.ExitCode, string(r.Stderr))
1002+
}
1003+
1004+
if !strings.Contains(string(r.Stdout), "root") {
1005+
t.Fatalf("expected user for exec to be 'root', got %q", string(r.Stdout))
1006+
}
1007+
}

test/cri-containerd/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const (
5151
imageLcowAlpineCoreDump = "cplatpublic.azurecr.io/stackoverflow-alpine:latest"
5252
imageWindowsProcessDump = "cplatpublic.azurecr.io/crashdump:latest"
5353
imageLcowCosmos = "cosmosarno/spark-master:2.4.1_2019-04-18_8e864ce"
54+
imageLcowCustomUser = "cplatpublic.azurecr.io/linux_custom_user:latest"
5455
imageJobContainerHNS = "cplatpublic.azurecr.io/jobcontainer_hns:latest"
5556
imageJobContainerETW = "cplatpublic.azurecr.io/jobcontainer_etw:latest"
5657
imageJobContainerVHD = "cplatpublic.azurecr.io/jobcontainer_vhd:latest"
@@ -164,7 +165,7 @@ func getWindowsNanoserverImage(build uint16) string {
164165
case osversion.V20H2:
165166
return "mcr.microsoft.com/windows/nanoserver:2009"
166167
default:
167-
return "mcr.microsoft.com/windows/nanoserver:2009"
168+
panic("unsupported build")
168169
}
169170
}
170171

@@ -181,7 +182,7 @@ func getWindowsServerCoreImage(build uint16) string {
181182
case osversion.V20H2:
182183
return "mcr.microsoft.com/windows/servercore:2009"
183184
default:
184-
return "mcr.microsoft.com/windows/nanoserver:2009"
185+
panic("unsupported build")
185186
}
186187
}
187188

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM ubuntu:latest
2+
3+
RUN useradd -ms /bin/bash test
4+
USER test

0 commit comments

Comments
 (0)