Search code examples
dockerjenkinsdevopscredentialsjenkins-docker

How to correctly pass ssh key file from Jenkins credentials variable into to docker build command?


This question is a follow up to this question How to pass jenkins credentials into docker build command?

I am getting the ssh key file from jenkins credential store in my groovy pipeline and passing it into docker build command via --build-arg so that I can checkout and build artifacts from the private git repos from within my docker container

credentials store id : cicd-user, which works for checking out my private works as expected from my groovy Jenkinsfile

checkout([$class: 'GitSCM',
            userRemoteConfigs: [[credentialsId: 'cicd-user', url:'ssh://[email protected]:7999/A/software.git']]

I access it and try to pass the same to docker build command:

  withCredentials([sshUserPrivateKey(credentialsId: 'cicd-user', keyFileVariable: 'FILE')]) { 
           sh "cd ${WORKSPACE} && docker build -t ${some-name} --build-arg USERNAME=cicd-user --build-arg  PRIV_KEY_FILE=\$FILE --network=host -f software/tools/jenkins/${some-name}/Dockerfile ."
        }

in Dockerfile I do

RUN echo "$PRIV_KEY_FILE" > /home/"$USERNAME"/.ssh/id_rsa && \
 chmod 700 /home/"$USERNAME"/.ssh/id_rsa 

RUN echo "Host bitbucket.myorg.co\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config

But I am seeing the following issue

"Load key "/home/cicd-user/.ssh/id_rsa" :(invalid format) "[email protected]:Permission denied( Public key) "fatal: could not read from remote repository"

In the past I have passed the ssh priv key as --build-arg from outside by cat'ing like below

--build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)"

Should I do something similar

--build-arg PRIV_KEY_FILE="$(cat $FILE)"

Any idea on what might be going wrong or where I should be looking for debugging this correctly ?


Solution

  • I ran into the same issue yesterday and I think I've come up with a workable solution.

    Here are the basic steps I took - using the sshagent plugin to manage the sshagent within the Jenkins job. You could probably use withCredentials as well, though that's not what I ended up finding success with.

    The ssagent (or alternatively the key) can be made available to specific build steps using the docker build commands --ssh flag. (Feature reference) It's important to note that for this to work (at the current time) you need to set DOCKER_BUILDKIT=1. If you forget to do this, then it seems like it ignores this configuration and the ssh connection will fail. Once that's set, the sshagent

    Cut down look at the pipeline:

    pipeline {
        agent {
            // ...
        }
        environment {
            // Necessary to enable Docker buildkit features such as --ssh
            DOCKER_BUILDKIT = "1"
        }
        stages {
            // other stages
    
            stage('Docker Build') {
                steps {
                    // Start ssh agent and add the private key(s) that will be needed in docker build
                    sshagent(['credentials-id-of-private-key']) {
                        // Make the default ssh agent (the one configured above) accessible in the build
                        sh 'docker build --ssh default .'
                    }
                }
            // other stages
            }
        }
    }
    

    In the Dockerfile it's necessary to explicitly give lines that need it access to the ssh agent. This can be done by including mount=type=ssh in the relevant RUN command.

    For me, this looked roughly like this:

    FROM node:14
    # Retrieve bitbucket host key
    RUN mkdir -p -m -0600 ~/.ssh && ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts
    ...
    # Mount ssh agent for install
    RUN --mount=type=ssh npm i
    ...
    

    With this configuration, the npm install was able to install a private git repo stored on Bitbucket by utilizing the SSH private key within docker build via sshagent.