run jenkins pipeline agent with sudo

I have an Jenkins Server running in an docker container and have access to docker an the host system, so far it is working well. Now I want to set up a pipeline testing an script inside an docker container.

Jenkinsfile:

pipeline {
    agent { docker 'nginx:1.11' }
    stages {
        stage('build') {
            steps {
                sh 'nginx -t'
            }
        }
    }
}

Error Message:

> + docker pull nginx:1.11
> 
> Warning: failed to get default registry endpoint from daemon (Got
> permission denied while trying to connect to the Docker daemon socket
> at unix:///var/run/docker.sock: Get
> http://%2Fvar%2Frun%2Fdocker.sock/v1.29/info: dial unix
> /var/run/docker.sock: connect: permission denied). Using system
> default: https://index.docker.io/v1/
> 
> Got permission denied while trying to connect to the Docker daemon
> socket at unix:///var/run/docker.sock: Post
> http://%2Fvar%2Frun%2Fdocker.sock/v1.29/images/create?fromImage=nginx&tag=1.11:
> dial unix /var/run/docker.sock: connect: permission denied
> 
> script returned exit code 1

My problem is that jenkins needs to run the docker command with sudo, but how to say the agent running the command with sudo?


Solution 1:

I have faced the same issue. After analysing the console log, I have found that the reason is that the Docker Jenkins Plugin starts a new container with a specific option -u 107:112:

...
docker run -t -d -u 107:112 ...
...

After trying many options such as: add jenkins to sudo group (it did not work because jenkins user does not exist in container), add USER root into Dockerfile, ... but none of them do the trick.

Finally I have found a solution that is using args in docker agent to overwrite the -u option. This is my Jenkinsfile:

pipeline {
    agent {
        docker {
            image 'ubuntu'
            args '-u root:sudo -v $HOME/workspace/myproject:/myproject'
        }
    }
    stages {
        stage("setup_env") {
            steps {
                sh 'apt-get update -y'
                sh 'apt-get install -y git build-essential gcc cmake make'
            }
        }

        stage("install_dependencies") {
            steps {
                sh 'apt-get install -y libxml2-dev'
            }
        }
        stage("compile_dpi") {
            steps {
                sh 'cd /myproject && make clean && make -j4'
            }
        }

        stage("install_dpi") {
            steps {
                sh 'cd /myproject && make install'
            }
        }

        stage("test") {
            steps {
                sh 'do some test here'
            }
        }
    }
    post {
        success {
            echo 'Do something when it is successful'
            bitbucketStatusNotify(buildState: 'SUCCESSFUL')
        }
        failure {
            echo 'Do something when it is failed'
            bitbucketStatusNotify(buildState: 'FAILED')
        }
    }
}

There's maybe a security issue here but it is not the problem in my case.

Solution 2:

You can work around that by:

1- In your Dockerfile add jenkins to the sudoers file:

RUN echo "jenkins ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

2- Add an extra step in your Jenkinsfile to give jenkins the right permissions to use docker:

pipeline {

    agent none

    stages {

        stage("Fix the permission issue") {

            agent any

            steps {
                sh "sudo chown root:jenkins /run/docker.sock"
            }

        }

        stage('Step 1') {

            agent {
                docker {
                    image 'nezarfadle/tools'
                    reuseNode true
                }
            }

            steps {
                sh "ls /"
            }

        }

    }
}

Solution 3:

I'd solve the problem differently, matching the jenkins group id inside the container to that of the docker socket you've mounted a volume. I do this with an entrypoint that runs as root, looks up the gid of the socket, and if that doesn't match that of the gid inside the current container, it does a groupmod to correct it inside the container. Then I drop privileges to the jenkins user to launch Jenkins. This entrypoint run on every startup, but fairly transparently to the Jenkins app that is launched.

All the steps to perform this are included in this github repo: https://github.com/sudo-bmitch/jenkins-docker/