Search code examples
dockerdocker-in-dockerrootless

Docker Rootless in Docker Rootless, It's possble?


For my job, I would like to run Jenkins and Docker Rootless (with the sysbox runtime only for this container), all in Docker Rootless.

I would like this because I need a secure environment given I don't inspect Jenkins pipelines

But when I run docker rootless in docker rootless, I get this error:

[rootlesskit:parent] error: failed to setup UID/GID map: newuidmap 54 [0 1000 1 1 100000 65536] failed: newuidmap: write to uid_map failed: Operation not permitted
: exit status 1

I tried many actions but failed to get it to work. Someone would have a solution to do this, please?

Thanks for reading me, have a nice day!


Edit 1

Hello, I take the liberty of relaunching this question, because being essential for the safety of our environment, my bosses remind me every day. Would someone have the answer to this problem please


Solution

  • Things getting a little tricky when you want to use the docker build command inside a Jenkins container. I stumbled upon this issue when wanted to build docker images without being root, under the user 'jenkins' instead. I wrote the solution in an article in which I explain in detail what is happening under the hood. The key point is to figure out which GID the docker.sock socket is running under (depends on the system). So here is what you gotta do:

    Run the command:

    $ stat /var/run/docker.sock
    

    Output:

    jenkins@wsl-ubuntu:~$ stat /var/run/docker.sock
      File: /var/run/docker.sock
      Size: 0               Blocks: 0          IO Block: 4096   socket
    Device: 17h/23d Inode: 552         Links: 1
    Access: (0660/srw-rw----)  Uid: (    0/    root)   Gid: ( 1001/  docker)
    Access: 2021-03-03 10:43:05.570000000 +0200
    Modify: 2021-03-03 10:43:05.570000000 +0200
    Change: 2021-03-03 10:43:05.570000000 +0200
     Birth: -
    

    In this case, the GID is 1001, but can also be 999 or something else in your machine.

    Now, create a Dockerfile and paste the code below replacing the ENV variable with your own from the stat command output above:

    FROM jenkins/jenkins:lts-alpine
    
    USER root
    
    ARG DOCKER_HOST_GID=1001 #Replace with your own docker.sock GID
    ARG JAVA_OPTS=""
    
    ENV DOCKER_HOST_GID $DOCKER_HOST_GID
    ENV JAVA_OPTS $JAVA_OPTS
    
    RUN set -eux \
    && apk --no-cache update \
    && apk --no-cache upgrade --available \
    && apk --no-cache add shadow \
    && apk --no-cache add docker curl --repository http://dl-cdn.alpinelinux.org/alpine/latest-stable/community \
    && deluser --remove-home jenkins \
    && addgroup -S jenkins -g $DOCKER_HOST_GID \
    && adduser -S -G jenkins -u $DOCKER_HOST_GID jenkins \
    && usermod -aG docker jenkins \
    && apk del shadow curl
    
    USER jenkins
    WORKDIR $JENKINS_HOME
    

    For the sake of a working example, here is a docker-compose file:

    version: '3.3'
    
    services:
      jenkins:
        image: jenkins_master
        container_name: jenkins_master
        hostname: jenkins_master
        restart: unless-stopped
        env_file:
          - jenkins.env
        build:
          context: .
        cpus: 2
        mem_limit: 1024m
        mem_reservation: 800M
      
        ports:
          - 8090:8080
          - 50010:50000
          - 2375:2376
        volumes:
          - ./jenkins_data:/var/jenkins_home
          - /var/run/docker.sock:/var/run/docker.sock
        networks:
          - default
    
    volumes:
      jenkins_data: {}
    networks:
      default:
        driver: bridge
    

    Now lets create the ENV variables:

    cat > jenkins.env <<EOF
    DOCKER_HOST_GID=1001 #Replace with your own docker.sock GID
    JAVA_OPTS=-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
    EOF
    

    and lastly, run the command docker-compose up -d. It will build the image, and run it. Then visit HTTP://host_machine_ip:8090 , and that's all.

    If you run docker inspect --format '{{ index (index .Config.Env) }}' jenkins_master you will see that the 1st and 2nd variables are the ones we set. More details can be found here: How to run rootless docker in dockerized Jenkins installation