diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java index ebbfd68a3..13c0f19b9 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java @@ -24,6 +24,7 @@ package org.jenkinsci.plugins.docker.workflow; import com.google.common.base.Optional; +import hudson.util.ArgumentListBuilder; import com.google.inject.Inject; import hudson.AbortException; import hudson.EnvVars; @@ -194,8 +195,9 @@ public Execution() { volumes.put(tmp, tmp); } + final String userId = dockerClient.whoAmI(); String command = launcher.isUnix() ? "cat" : "cmd.exe"; - container = dockerClient.run(env, step.image, step.args, ws, volumes, volumesFromContainers, envReduced, dockerClient.whoAmI(), /* expected to hang until killed */ command); + container = dockerClient.run(env, step.image, step.args, ws, volumes, volumesFromContainers, envReduced, userId, /* expected to hang until killed */ command); final List ps = dockerClient.listProcess(env, container); if (!ps.contains(command)) { listener.error( @@ -207,7 +209,7 @@ public Execution() { ImageAction.add(step.image, run); getContext().newBodyInvoker(). - withContext(BodyInvoker.mergeLauncherDecorators(getContext().get(LauncherDecorator.class), new Decorator(container, envHost, ws, toolName, dockerVersion))). + withContext(BodyInvoker.mergeLauncherDecorators(getContext().get(LauncherDecorator.class), new Decorator(container, envHost, ws, userId, toolName, dockerVersion))). withCallback(new Callback(container, toolName)). start(); return false; @@ -242,17 +244,19 @@ private static class Decorator extends LauncherDecorator implements Serializable private final String container; private final String[] envHost; private final String ws; + private final String user; private final @CheckForNull String toolName; private final boolean hasEnv; private final boolean hasWorkdir; - Decorator(String container, EnvVars envHost, String ws, String toolName, VersionNumber dockerVersion) { + Decorator(String container, EnvVars envHost, String ws, String user, String toolName, VersionNumber dockerVersion) { this.container = container; this.envHost = Util.mapToEnv(envHost); this.ws = ws; this.toolName = toolName; this.hasEnv = dockerVersion != null && dockerVersion.compareTo(new VersionNumber("1.13.0")) >= 0; this.hasWorkdir = dockerVersion != null && dockerVersion.compareTo(new VersionNumber("17.12")) >= 0; + this.user = user; } @Override public Launcher decorate(final Launcher launcher, final Node node) { @@ -264,8 +268,15 @@ private static class Decorator extends LauncherDecorator implements Serializable } catch (InterruptedException x) { throw new IOException(x); } + List prefix = new ArrayList<>(Arrays.asList(executable, "exec")); List masksPrefixList = new ArrayList<>(Arrays.asList(false, false)); + if (user != null) { + prefix.add("-u"); + masksPrefixList.add(false); + prefix.add(user); + masksPrefixList.add(false); + } if (ws != null) { FilePath cwd = starter.pwd(); if (cwd != null) { diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java index 336efa0bb..1b09864a9 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java @@ -110,7 +110,7 @@ public DockerClient(@Nonnull Launcher launcher, @CheckForNull Node node, @CheckF * @param command The command to execute in the image container being run. * @return The container ID. */ - public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map volumes, @Nonnull Collection volumesFromContainers, @Nonnull EnvVars containerEnv, @Nonnull String user, @Nonnull String... command) throws IOException, InterruptedException { + public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map volumes, @Nonnull Collection volumesFromContainers, @Nonnull EnvVars containerEnv, @CheckForNull String user, @Nonnull String... command) throws IOException, InterruptedException { ArgumentListBuilder argb = new ArgumentListBuilder(); argb.add("run", "-t", "-d"); diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index 2de11c25d..c87e90e51 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -30,12 +30,11 @@ public WindowsDockerClient(@Nonnull Launcher launcher, @CheckForNull Node node, } @Override - public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map volumes, @Nonnull Collection volumesFromContainers, @Nonnull EnvVars containerEnv, @Nonnull String user, @Nonnull String... command) throws IOException, InterruptedException { + public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map volumes, @Nonnull Collection volumesFromContainers, @Nonnull EnvVars containerEnv, @CheckForNull String user, @Nonnull String... command) throws IOException, InterruptedException { ArgumentListBuilder argb = new ArgumentListBuilder("docker", "run", "-d", "-t"); if (args != null) { argb.addTokenized(args); } - if (workdir != null) { argb.add("-w", workdir); } diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java index 4c9a97447..612043f92 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java @@ -260,6 +260,52 @@ public class WithContainerStepTest { }); } + @Test public void withInitAndExecAsUser() throws Exception { + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + DockerTestUtil.assumeDocker(); + WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj"); + p.setDefinition(new CpsFlowDefinition( + "node {\n" + + " def gid = sh(script: 'id -g', returnStdout: true).trim()\n" + + " def uid = sh(script: 'id -u', returnStdout: true).trim()\n" + + " def gname = sh(script: 'id -g -n', returnStdout: true).trim()\n" + + " def uname = sh(script: 'id -u -n', returnStdout: true).trim()\n" + + " def args = \"-e BUILDER_UID=${uid} -e BUILDER_GID=${gid} -e BUILDER_USER=${uname} -e BUILDER_GROUP=${gname} -e HOME=${HOME}\"\n" + + " withDockerContainer(args: args, image: 'dockcross/base:latest') {\n" + + " sh 'sleep 2 && touch ${HOME}/test && id'\n" + + " }\n" + + "}", true)); + WorkflowRun b = story.j.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0)); + } + }); + } + + @Test public void withInitAsRootAndExecAsUser() throws Exception { + story.addStep(new Statement() { + @Override + public void evaluate() throws Throwable { + DockerTestUtil.assumeDocker(); + WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj"); + p.setDefinition(new CpsFlowDefinition( + "node {\n" + + " def gid = sh(script: 'id -g', returnStdout: true).trim()\n" + + " def uid = sh(script: 'id -u', returnStdout: true).trim()\n" + + " def gname = sh(script: 'id -g -n', returnStdout: true).trim()\n" + + " def uname = sh(script: 'id -u -n', returnStdout: true).trim()\n" + + " def args = \"-u root:root -e BUILDER_UID=${uid} -e BUILDER_GID=${gid} -e BUILDER_USER=${uname} -e BUILDER_GROUP=${gname} -e HOME=${HOME}\"\n" + + " withDockerContainer(args: args, image: 'dockcross/base:latest') {\n" + + " sh 'sleep 2 && touch ${HOME}/test && id'\n" + + " }\n" + + "}", true)); + WorkflowRun b = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0).get()); + story.j.assertLogContains("uid=1000", b); + } + }); + } + + @Issue("JENKINS-27152") @Test public void configFile() throws Exception { story.addStep(new Statement() {