Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove user from docker run command #57

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

antoniobeyah
Copy link

Fixes #25 , removes the user from the docker run command. The changes from #25 were merged in and tested against the demo container. I also updated the Dockerfile in the demo in preparation of the release to support the new required minimum version (Docker client 1.7.0). During testing, I found a bug in master with docker client 1.7.0, the bug was introduced in this commit. The bug is that the redirection is causing the exit status to be 1, when it should be 0 in some instances.

To workaround that, I removed the output redirection which restored previous behavior. See the full commit message for the result of each command. Here is the result of testing the return value of the command (inside the demo container):

root@8a07680574ce:/tmp/files# docker --version
Docker version 1.7.0, build 0baf609
root@8a07680574ce:/tmp/files# docker inspect -f . maven:3.3.9-jdk-8
Error: No such image or container: maven:3.3.9-jdk-8
root@8a07680574ce:/tmp/files# echo $?
1
root@8a07680574ce:/tmp/files# docker inspect -f . maven:3.3.9-jdk-8 >&- 2>&-
root@8a07680574ce:/tmp/files# echo $?
1
# run the job to create the container
root@8a07680574ce:/tmp/files# docker inspect -f . maven:3.3.9-jdk-8
.
root@8a07680574ce:/tmp/files# echo $?
0
root@8a07680574ce:/tmp/files# docker inspect -f . maven:3.3.9-jdk-8 >&- 2>&-
root@8a07680574ce:/tmp/files# echo $?
1
root@8a07680574ce:/tmp/files#

This is the error that appeared in the Jenkins job:

[workspace] Running shell script
+ docker pull maven:3.3.9-jdk-8
3.3.9-jdk-8: Pulling from library/maven
Digest: sha256:a5283092002ec9739819c9b392203103346dfc163586ff46c30ead13b60cc436
Status: Image is up to date for maven:3.3.9-jdk-8
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withDockerRegistry
[Pipeline] {
[Pipeline] stage (Build)
Entering stage Build
Proceeding
[Pipeline] sh
[workspace] Running shell script
+ docker inspect -f . maven:3.3.9-jdk-8
[Pipeline] sh
[workspace] Running shell script
+ docker pull localhost/maven:3.3.9-jdk-8
Pulling repository localhost/maven
Error: image maven not found
[Pipeline] }
[Pipeline] // withDockerRegistry
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
ERROR: script returned exit code 1
Finished: FAILURE

@antoniobeyah
Copy link
Author

antoniobeyah commented Aug 18, 2016

Not sure what the process is here for notifying people of pull requests, so apologies in advance if this isn't the right mechanism.

@reviewbybees

cc: @ndeloof @jglick @svanoort

@ghost
Copy link

ghost commented Aug 18, 2016

This pull request originates from a CloudBees employee. At CloudBees, we require that all pull requests be reviewed by other CloudBees employees before we seek to have the change accepted. If you want to learn more about our process please see this explanation.

@antoniobeyah
Copy link
Author

@jglick @ndeloof @svanoort @reviewbybees ping

@ndeloof
Copy link
Contributor

ndeloof commented Aug 23, 2016

Compared to #25 I can't see user set when docker exec is ran, which will cause issues with file permissions on workspace.

@antoniobeyah
Copy link
Author

Good catch, I'll get that added.

@antoniobeyah
Copy link
Author

@ndeloof i added the --user change. i had some troubles verifying that it is actually doing what I expected (I couldn't directly see the docker exec command) but it seems to pass in the demo container and against some of my existing jenkins jobs that use the plugin.

I performed an 'id' in the sh step and it was indeed the jenkins user (in my test instance, in the demo container the jenkins user is root). I tried to figure out how to make the 'docker exec' command it was running print out to the console log like the docker run command but was unsuccessful at figuring out how to do that.

This may be a long-term thing (and probably a mailing list discussion), but I would like to see/determine if the core logic of this plugin and the Docker Custom Build Environment plugin can be merged so the results are consistent between the two. I currently use both and the behavior between these two are similar but different enough to make it confusing.

@ndeloof
Copy link
Contributor

ndeloof commented Aug 24, 2016

LGTM

@antoniobeyah I would have expected the docker custom build environment plugin to provide .inside extension do docker-pipeline, and avoid such code duplication :-
anyway, imho docker+jenkins future is docker-slaves-plugin.

@antoniobeyah
Copy link
Author

@ndeloof thanks for the review, are there any additional required reviewers or can you merge it? I don't have merge rights or else I would based on your approval. I just want to get it off my plate as one less thing to check-in/monitor status of before someone else introduces some conflicting changes to master. ;)

@ndeloof
Copy link
Contributor

ndeloof commented Aug 24, 2016

@antoniobeyah need to wait for plugin maintainer (@jglick)

DockerFingerprints.addRunFacet(dockerClient.getContainerRecord(env, container), run);
ImageAction.add(step.image, run);
getContext().newBodyInvoker().
withContext(BodyInvoker.mergeLauncherDecorators(getContext().get(LauncherDecorator.class), new Decorator(container, envHost, ws))).
withContext(BodyInvoker.mergeLauncherDecorators(getContext().get(LauncherDecorator.class), new Decorator(container, envHost, ws, dockerClient.whoAmI()))).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For safety, run whoAmI before starting the container, since if it fails we cannot clean up.

@jglick
Copy link
Member

jglick commented Aug 30, 2016

So is this purporting to address JENKINS-34289 or not? And is there a self-contained demo case that can be used to prove that this change fixes some concrete bug? What I am missing from all this is a clear description of what problem is being solved.

List<String> prefix = new ArrayList<String>(Arrays.asList("docker", "exec", container, "env"));
List<String> prefix;
if (userId == null) {
prefix = new ArrayList<String>(Arrays.asList("docker", "exec", container, "env"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, this is necessary for compatibility with builds running before the upgrade.

@jglick
Copy link
Member

jglick commented Aug 30, 2016

I found a bug in master with docker client 1.7.0, the bug was introduced in #54. The bug is that the redirection is causing the exit status to be 1, when it should be 0 in some instances.

To workaround that, I removed the output redirection which restored previous behavior.

This sounds like a separate issue that would better be fixed in its own pull request. (If basic testing of this PR requires that fix, just merge in that branch.)

@jglick
Copy link
Member

jglick commented Aug 30, 2016

I performed an 'id' in the sh step and it was indeed the jenkins user (in my test instance, in the demo container the jenkins user is root).

This is an example of something you have apparently tested interactively but which is not reflected in an automated test, or even written manual test instructions.

@jglick
Copy link
Member

jglick commented Aug 30, 2016

I would like to see/determine if the core logic of this plugin and the Docker Custom Build Environment plugin can be merged

Given how fundamentally different Pipeline asynchronous (durable) process execution is from freestyle, I do not think this is practical.

@ndeloof
Copy link
Contributor

ndeloof commented Aug 30, 2016

@jglick both rely on docker exec a very comparable way, I don't see any reason they don't share a common code base.

@antoniobeyah
Copy link
Author

@jglick JENKINS-34289 was marked as resolved so I ignored it for the purposes of this PR. Point taken on the clear explanation on why this is needed, I will try to get a test case written up that shows the failure.

I have a limited amount of time I can spend on this- we are running on a forked version at Target that has the changes in it (not ideal). Are you able to provide a bulleted list of items that you require for a merge? So far I have gathered:

  1. Move the master bug fix to a seperate pr
  2. Provide a test showing the thing this PR is intended to fix
  3. (breaks backwards compability) Remove the 'user' parameter from DockerClient#run
  4. Move dockerClient.whoAmI() to occur before the invocation

Are there any additional changes you would require?

@jglick
Copy link
Member

jglick commented Aug 30, 2016

Move the master bug fix to a seperate pr

Well, desirable practice generally, so that the obvious bug fix can get to users while we are discussing the main part of this PR. Can be skipped if you do not have time.

Provide a test showing the thing this PR is intended to fix

Yes, manual test steps if it is impractical to write an automated test (for example because you need to be using a specific user ID on the host).

breaks backwards compability

It is fine, DockerClient is really just an internal utility for this plugin.

Move dockerClient.whoAmI() to occur before the invocation

Right, safer.

Are there any additional changes you would require?

No, the test—or just some demonstration that this is fixing a particular bug I can see for myself—is the main thing.

@dweomer
Copy link

dweomer commented Sep 9, 2016

I do not understand why removing the user constraints put on a container for docker.inside is under consideration. The docker.inside mechanism is for injecting a workspace into a container so that it may act upon it. If a container cannot run as the specified user then it's image is simply not fit for this purpose. After injecting the workspace into the container to mutate it I expect that follow-on processes will be able to further mutate it as will. Choosing to forgo the specification of the user that the container should run as breaks this very fundamental expectation.

@carlossg pointed me here from carlossg/docker-maven#16, and yes this would fix the problem but at the cost of a good deal more downstream breakage. What this PR purports to fix, that being #25, seems of dubious value to me. I think it would make more sense to leave the default behavior of specifying the user as-is unless the image specified for docker.inside is specially packaged, say with labels, to support advanced functionality. This could be as simple as having a label such as io.jenkins.pipeline.docker.inside.user=root on the container. This would not, of course, cover the desired contract that the resulting workspace can still be written to by the invoking user but given that docker.inside is already docker-aware I imagine it could perform some nifty post processing (or possibly tricksy user namespace remapping) to have the output workspace owned and writable by the invoking user, as would be expected.

/cc @ndeloof @jglick

@antoniobeyah
Copy link
Author

@dweomer The constraint isn't being removed, it is being moved to the exec command. This will allow the container to start up as expected, without unintended side effects.

I've been slow on getting this pr updated with the requests, but this is our use case:

We use docker images for CI testing of RPM builds. The RPMs happen to use systemd. That means entrypoint is 'init' which must be run as root, otherwise the container blows up. All subsequent commands are executed as the jenkins user, and that is supported by 'docker exec --user '. The container behaves very different when it is started with 'docker run --user' vs 'docker exec --user'. You are unable to do certain things when --user is used on the 'docker run', such as using sudo to install any required packages using yum, or to get the container to start at all.

This behavior is already supported by other plugins, such as Docker Custom Build Environment plugin. We are running a forked version of the docker-pipeline plugin without the docker run --user constraint and it is working great.

This PR only allows for the possibility of using additional containers without the need of custom tailoring them to be used within Jenkins. I would also be for using a different mechanism for determining the user to run .inside as, but as it stands it works 'well enough' with the changes in this PR.

@dweomer Will this PR break your environment somehow? If so I may be able to add in some checks to make that less likely, just need some specific details on your concerns.

@jglick As far as the automated test- I don't think I will be able to write one due to the reasons you provided (the user in the host machine is root, my test requires it to be a non-root user). Can you provide more details on how specifically I can provide manual verification steps?

@jglick
Copy link
Member

jglick commented Sep 9, 2016

Can you provide more details on how specifically I can provide manual verification steps?

Well, something I can run locally (perhaps inside VirtualBox if host permissions matter) that would let me understand the problem for myself and evaluate how the proposed fix works. Minimally, a demo Pipeline script relying only on images on Docker Hub (can use .build as needed to create derivative images). If I can reproduce it manually, I at least have a shot at adding an automated test as well.

@jglick
Copy link
Member

jglick commented Sep 9, 2016

Will this PR break your environment somehow? If so I may be able to add in some checks to make that less likely

If there is any concern about this, I would rather make the new behavior opt-in.

@dweomer
Copy link

dweomer commented Sep 9, 2016

@antoniobeyah wrote:

Will this PR break your environment somehow? If so I may be able to add in some checks to make that less likely, just need some specific details on your concerns.

In my ire related to carlossg/docker-maven#16, I had forgotten about the run/exec distinction. This should not break environments but I feel that you should ensure that by implementing a test case such as:

  • pull down well known or sample project
  • build it via docker(img).inside as appropriate (for each of golang, maven, and possibly node/npm?)
  • wipe out the entire workspace without encountering write permission failures

@dweomer
Copy link

dweomer commented Sep 9, 2016

@antoniobeyah @jglick:
My other qualm with this is that I do not want my build containers to run as root unless I explicitly tell them to via code (as that implies pipeline code which is something that can be audited). Currently most official images for build systems such as golang and maven run as root by default (which is correct because these are very low level base images) and this is currently overriden by docker.inside by default to match the user:group of the process that invokes the in-container build.

In short, defaulting to the image's USER introduces a potential security hole as a side-effect which is not acceptable. Opt-in semantics would address this issue.

@zasdfgbnm
Copy link

@jglick it might be better to add -v /etc/group:/etc/group:ro -v /etc/passwd:/etc/passwd:ro if the final decision is to keep the --user

@joshuaspence
Copy link

Is this PR going anywhere?

@michaelneale
Copy link
Member

doing a sweep of older PRs and it looks like this one is no longer live, and there are sufficient work arounds - can we close this?

@ndeloof
Copy link
Contributor

ndeloof commented Nov 29, 2017

@michaelneale I still think we need this one, especially if we restore ENTRYPOINT support then this one will expect to run with userID as designed in docker image (let's say, to start background services)

@michaelneale
Copy link
Member

@ndeloof @antoniobeyah want to bring this one up to date then?

@adamvoss
Copy link

adamvoss commented Jan 24, 2018

All the discussion here has been with the Scripted Pipeline. Declarative pipeline is also affected.

For example, to make the Python image work, you need to add args '--user root:root'

Example:

agent {
  docker {
    image 'python:2'
    args '--user root:root'
  }
}

I find this rather unexpected and confusing behavior when commands will work directly if using the container on CLI but don't under Jenkins. It has taken me quite a while (~a month) to figure out what is going on, find this issue, and know how to make all builds consistently work.

My gut reaction is to agree that Jenkins should use the user from the container unless specified otherwise. This would make the naive declarative Jenkinsfile just work with images pulled from Dockerhub and those wanting different behavior could still specify so. As such I would like to see this go forward, but based on the discussion above (and the wontfix tag), I'm not sure if there is support for the change of behavior.

@digitalresistor
Copy link

The problem of the Jenkins uid/gid outside the container not matching anything in /etc/passwd inside the container is a problem I've been running into, and the decision made in https://issues.jenkins-ci.org/browse/JENKINS-41050 to do the checkout outside means that files created inside the container when running as "root" inside the container (as @adamvoss points out for the Python images) can no longer be cleaned up by Jenkins, so git clean fails, and fails hard (see https://issues.jenkins-ci.org/browse/JENKINS-24440).

@fessmage
Copy link

By my opinion, default behaviour of running docker images from jenkins pipeline must be the same, what you expect and what you get, when running same docker images on your local workspace.
At least current situation must be documented and contain examples - because for working around with what's going on, why you can't reproduce workflow in pipeline - take times!

@atrifex
Copy link

atrifex commented May 1, 2019

What is the ETA for the resolution of this issue?

Never mind. I see that this has been marked as wontfix.

@qbaze
Copy link

qbaze commented Nov 12, 2019

Hey guys.

Why is this one 'wontfix'? Is it a desired behaviour? What's the purpose if yes?
Sorry if I missed something.

Best,
Piotr

@dmolesUC
Copy link

@michaelneale what are the "sufficient workarounds"?

@jglick
Copy link
Member

jglick commented Apr 29, 2020

Should be closed, risk of regression is too high. The workaround is to simply not use withDockerContainer in even a remotely subtle case. If it works the first time you try it, great. If not, use sh 'docker …' instead and forget about this misconceived feature.

@metametadata
Copy link

metametadata commented Apr 29, 2020

This is my current code, not sure how it can be replaced with sh 'docker …':

image.inside(...

            // HACK: setting root user is needed because otherwise the current user id from the host will be used. See:
            //   https://issues.jenkins-ci.org/browse/JENKINS-38438
            //   https://github.com/jenkinsci/docker-workflow-plugin/pull/57
            // Without it something as simple as 'mkdir foo' unexpectedly fails because of permission denied.
            "-u root " +

            ...

            ) {
        try {
            stage('...') {
                ...
            }

            stage('...') {
                ...
            }

            ...
        }
        finally {
            // HACK: fix permissions because Docker creates root-owned files which cannot be deleted
            // by Jenkins user when it tries to clean the workspace.
            // https://issues.jenkins-ci.org/browse/JENKINS-24440#comment-335677
            // https://issues.jenkins-ci.org/browse/JENKINS-46686
            sh('chmod -R 777 .')
        }

Edit: frankly speaking, it was a long time since I looked at this issue and code and I'm quite hesitant to rewrite it while it works. But maybe this example will provide more input before closing the issue as wont-fix or something.

@digitalresistor
Copy link

Using sh docker ... instead (i.e. invoking docker oneself) doesn't solve the problem of still needing access to the checked out source code.

Unless you are suggesting we check out the source code inside the docker container, because doing a bind mount (which is what image.inside is effectively doing) would still run into issues with file permissions.

@jglick
Copy link
Member

jglick commented Apr 30, 2020

There is no universal advice, but if you need access to files from checkout scm or the like, it is typically most straightforward to reconceive the build process as a docker build command with a Dockerfile using COPY to pick up files from a relative location. This also avoids accidentally polluting the workspace with files owned by other UIDs; on the flip side, if you need some build products in the workspace in order to run Jenkins publishers, you will need to use docker cp as shown in #105.

@zerkms
Copy link

zerkms commented Apr 30, 2020

if you need access to files from checkout scm or the like, it is typically most straightforward to reconceive the build process as a docker build command with a Dockerfile using COPY to pick up files from a relative location

This is a horrible workaround: given in other cases workspace is shared automatically - doing something manually anywhere is a recipe for a disaster.

@digitalresistor
Copy link

Copying large source trees into a docker build only to then use that docker image is also expensive.

Yet, in most cases I'll need the source code checked out to be able to use the included Dockerfile to now build the docker image so that I can build the source code and pull artifacts out.

@arowland-informatics
Copy link

This bug still affects declarative pipelines and essentially makes using a docker agent not possible. What would be the workaround if you are not using scripted pipelines?

@reddwarf69
Copy link

There is https://issues.jenkins.io/browse/JENKINS-63233. In a rootless podman container you are root insider the container, but jenkins outside. So the workspace file permission issue doesn't exist.

I guess the same applies to https://docs.docker.com/engine/security/rootless/. But you may have less control over the user the Docker daemon is running as while with podman, being daemon-less, it just runs as the user calling it (i.e. jenkins).

@iliis
Copy link

iliis commented Apr 8, 2021

I'm using simple declarative pipelines like this

pipeline {
    agent {
        dockerfile true
    }

    stages {
		...
	}
}

or like this:

pipeline {
    agent {
        docker {
            image "some-predefined-docker-image"
        }
    }

    stages {
		...
	}
}

as described in https://www.jenkins.io/doc/book/pipeline/docker/

However, due to this issue I need to add the following to all my Dockerfiles:

RUN groupadd --gid 1000 jenkins && \
    useradd --create-home --uid 1000 --gid 1000 --groups sudo jenkins && \
    echo "jenkins ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

Which breaks as soon as I run Jenkins on a machine where the user jenkins doesn't have ID 1000, for example when installing it somewhere where there's already another default user that uses this ID.

@konserw
Copy link

konserw commented May 13, 2021

Hi! Is there any progress with this MR? I've just been hit by issue with unknow uid in container when run via jenkins. I would also appreciate if jenkins documentation would mention that jenkins is overriding uid in my containers. I'll try out workarounf from previous post for now

@diorcety
Copy link

My PR #160 try to resolve that issue.
I will try to summerize the issue.

The plugins do the following:
Run the image with the following command : docker run -u jenkins cat
Run the commands with the following command: docker exec CMD

The docker behaviour is that, the commands run with exec, without any override will be executed with the same user specifed at docker run: https://stackoverflow.com/questions/35734474/connect-to-docker-container-as-user-other-than-root

In order to use an other user, in the pipeline, he can add another "-u user:user" parameter in order to override the default plugin behaviour. The issue is in that case all the next exec commands will be also executed with that user, which for most the case is not wanted.
One solution should only be to also put the -u jenkins for every docker exec command decorated. Without any override of user in the run arguments the behaviour will be exactly the same. But this will allow to enter in the image using another user (for exemple root) doing some action in order to create the same user that the host one (like the entrypoint of dockcross https://github.com/dockcross/dockcross/blob/master/imagefiles/entrypoint.sh
) and run all the commands with an equivalent jenkins in the docker. All the files created will be owned by the jenkins user, in the docker or on the host.
If the current behaviour is needed at the end, that can be done optionnally following a parameter.

@jacobnollette
Copy link

Before I was shown this issue, I had a workaround documented here -
https://stackoverflow.com/questions/74646034/jenkins-docker-pipeline-not-passing-exit-codes

Wondering if there's a better solution to my hacky approach or if there's a better solution coming from this PR? Any updates since last year?

@jglick
Copy link
Member

jglick commented Dec 2, 2022

#57 (comment) though BuildX now supports --output making it more convenient to retrieve results (e.g., for a Jenkins publisher like junit) from docker build.

@ketozhang
Copy link

ketozhang commented Apr 14, 2023

For the love of xyz, document this behavior...if you're not going to merge this.

Do the example code in this documentation even work as-is (without setting the -u flag to something else)?
https://www.jenkins.io/doc/book/pipeline/docker/#dockerfile

@jglick
Copy link
Member

jglick commented Apr 14, 2023

Do the example code in this documentation even work as-is (without setting the -u flag to something else)?

Yes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.