Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-64552

ssh-steps-plugin doesn't allow credentials passed without string interpolation

    • Icon: Improvement Improvement
    • Resolution: Unresolved
    • Icon: Minor Minor
    • ssh-steps-plugin
    • None

      Hello,

      I have the following pipeline stage, which works great:

      stage('Database') {
        steps {
          script {
            remote = createDeployRemote()
      
            withCredentials([
              usernamePassword(credentialsId: "credential-id", usernameVariable: 'THE_USERNAME', passwordVariable: 'THE_PASSWORD')
            ]) {
              sshCommand remote: remote,
                command: """
                  somecommand \
                    --username="${THE_USERNAME}" \
                    --password="${THE_PASSWORD}"
                """
            }
          }
        }
      }
      

      Jenkins complaints with the following warning:

      Warning: A secret was passed to "sshCommand" using Groovy String interpolation, which is insecure.
      
      Affected argument(s) used the following variable(s): [THE_USERNAME, THE_PASSWORD]
      
      See https://jenkins.io/redirect/groovy-string-interpolation for details.
      

      I've changed my code to the following:

      stage('Database') {
        environment {
          THE_CREDS = credentials("credential-id")
        }
      
        steps {
          script {
            remote = createDeployRemote()
      
            sshCommand remote: remote,
              command: """
                printenv
      
                somecommand \
                    --username="\$THE_CREDS_USR" \
                    --password="\$THE_CREDS_PSW"
              """
          }
        }
      }
      

      It seems that this plugin doesn't pass any of the environment variables to the remote machine.

      I think there should be an option to pass all or some pre-defined environment variables to `sshCommand`.

          [JENKINS-64552] ssh-steps-plugin doesn't allow credentials passed without string interpolation

          Viacheslav added a comment -

          Hi!
          Found the same issue when using the docker login command:

          withCredentials([
              sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID', keyFileVariable: 'SSH_KEYFILE', passphraseVariable: 'SSH_PASSPHRASE', usernameVariable: 'SSH_USERNAME'),
              usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID', passwordVariable: 'DOCKER_PASSWD', usernameVariable: 'DOCKER_USERNAME')
              ]) {
                  script {
                      def remote = [:]
                      remote.name = 'XXX.XXX.XXX.XXX'
                      remote.host = 'XXX.XXX.XXX.XXX'
                      remote.allowAnyHosts = true
                      remote.user = SSH_USERNAME
                      remote.identityFile = SSH_KEYFILE
                      remote.passphrase = SSH_PASSPHRASE
                      sshCommand remote: remote,
                          command: """
                              echo ${DOCKER_PASSWD} | docker login -u ${DOCKER_USERNAME} --password-stdin ${env.DOCKER_REGISTRY_URI}
                          """
                  }
              }
          

           In Jenkins' logs I see the following:

          Warning: A secret was passed to "sshCommand" using Groovy String interpolation, which is insecure.
          Affected argument(s) used the following variable(s): [DOCKER_PASSWD, DOCKER_USERNAME]
          See https://jenkins.io/redirect/groovy-string-interpolation for details.
          

          I am looking for a secure way to pass login and password to "Docker Hub" via "sshCommand".

          Viacheslav added a comment - Hi! Found the same issue when using the docker login command: withCredentials([ sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID' , keyFileVariable: 'SSH_KEYFILE' , passphraseVariable: 'SSH_PASSPHRASE' , usernameVariable: 'SSH_USERNAME' ), usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID' , passwordVariable: 'DOCKER_PASSWD' , usernameVariable: 'DOCKER_USERNAME' ) ]) { script { def remote = [:] remote.name = 'XXX.XXX.XXX.XXX' remote.host = 'XXX.XXX.XXX.XXX' remote.allowAnyHosts = true remote.user = SSH_USERNAME remote.identityFile = SSH_KEYFILE remote.passphrase = SSH_PASSPHRASE sshCommand remote: remote, command: """ echo ${DOCKER_PASSWD} | docker login -u ${DOCKER_USERNAME} --password-stdin ${env.DOCKER_REGISTRY_URI} """ } }  In Jenkins' logs I see the following: Warning: A secret was passed to "sshCommand" using Groovy String interpolation, which is insecure. Affected argument(s) used the following variable(s): [DOCKER_PASSWD, DOCKER_USERNAME] See https://jenkins.io/redirect/groovy-string-interpolation for details. I am looking for a secure way to pass login and password to "Docker Hub" via "sshCommand".

          Jordan added a comment -

          fabiang, simply change :

          command: """
           somecommand \
           --username="${THE_USERNAME}" \
           --password="${THE_PASSWORD}"
          """

          by :

          command: """
           somecommand \
           --username="$THE_USERNAME" \
           --password="$THE_PASSWORD"
          """

           

          vkolupaev, do the same. Change :

          command: """
           echo ${DOCKER_PASSWD} | docker login -u ${DOCKER_USERNAME} --password-stdin ${env.DOCKER_REGISTRY_URI}
           """

          by

          command: """
           echo $DOCKER_PASSWD | docker login -u $DOCKER_USERNAME --password-stdin ${env.DOCKER_REGISTRY_URI}
           """

          Jordan added a comment - fabiang , simply change : command: """ somecommand \ --username= "${THE_USERNAME}" \ --password= "${THE_PASSWORD}" """ by : command: """ somecommand \ --username= "$THE_USERNAME" \ --password= "$THE_PASSWORD" """   vkolupaev , do the same. Change : command: """ echo ${DOCKER_PASSWD} | docker login -u ${DOCKER_USERNAME} --password-stdin ${env.DOCKER_REGISTRY_URI} """ by command: """ echo $DOCKER_PASSWD | docker login -u $DOCKER_USERNAME --password-stdin ${env.DOCKER_REGISTRY_URI} """

          jlenuff: I don't see any solution in your comment? This still uses string interpolation.

          Fabian Grutschus added a comment - jlenuff : I don't see any solution in your comment? This still uses string interpolation.

          Jordan added a comment -

          fabiang, no.
          ${THE_USERNAME} ==> string interpolation
          $THE_USERNAME ==> no string interpolation
           

          Jordan added a comment - fabiang , no. ${THE_USERNAME} ==> string interpolation $THE_USERNAME ==> no string interpolation  

          jlenuff: ok, but those variables aren't passed by sshCommand to the remote machine and therefore aren't available.

          Fabian Grutschus added a comment - jlenuff : ok, but those variables aren't passed by sshCommand to the remote machine and therefore aren't available.

          Jordan added a comment - - edited

          fabiang

          After several tests, I find the problem.

          I defined a JENKINS_SSH_KEY_ID credential with a functionnal SSH Key in my Jenkins with one of my servers (ie : mygreatserver).

          I also defined a JENKINS_DOCKERHUB_CREDENTIALS_ID credential with "tata" as password and "toto" as username in my Jenkins.

          With the following script with string interpolation, I reproduce the "A secret was passed to "sshCommand" using Groovy String interpolation, which is insecure." :

          node{
              withCredentials([
              sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID', keyFileVariable: 'SSH_KEYFILE', passphraseVariable: 'SSH_PASSPHRASE', usernameVariable: 'SSH_USERNAME'),
              usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID', passwordVariable: 'DOCKER_PASSWD', usernameVariable: 'DOCKER_USERNAME')
              ]) {
                  script {
                      def remote = [:]
                      remote.name = 'mygreatserver'
                      remote.host = 'mygreatserver'
                      remote.allowAnyHosts = true
                      remote.user = SSH_USERNAME
                      remote.identityFile = SSH_KEYFILE
                      remote.passphrase = SSH_PASSPHRASE
                      sshCommand remote: remote,
                          command: """
                              hostname > /tmp/test
                          """
                      sshCommand remote: remote,
                          command: """
                              echo "Password : ${DOCKER_PASSWD} Username : ${DOCKER_USERNAME}" >> /tmp/test
                          """
                  }
              }
          }
          

          Here is the /tmp/test file content : 

          mygreatserver
          Password : tata Username : toto
          

           

          But with the following script without script interpolation, I think I solve your problem :

          node{
              withCredentials([
              sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID', keyFileVariable: 'SSH_KEYFILE', passphraseVariable: 'SSH_PASSPHRASE', usernameVariable: 'SSH_USERNAME'),
              usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID', passwordVariable: 'DOCKER_PASSWD', usernameVariable: 'DOCKER_USERNAME')
              ]) {
                  script {
                      def remote = [:]
                      remote.name = 'mygreatserver'
                      remote.host = 'mygreatserver'
                      remote.allowAnyHosts = true
                      remote.user = SSH_USERNAME
                      remote.identityFile = SSH_KEYFILE
                      remote.passphrase = SSH_PASSPHRASE
                      sshCommand remote: remote,
                          command: "hostname > /tmp/test"
                      sshCommand remote: remote,
                          command: "echo 'Password : " + DOCKER_PASSWD + " Username : " + DOCKER_USERNAME + "' >> /tmp/test"
                  }
              }
          }
          

          Here is the /tmp/test file content :

          mygreatserver
          Password : tata Username : toto
          

          I have no warning no more.

          The probleme is in the multines script. You should concatenate your variables, but you can't do it in a multiline script way.

           

          Jordan added a comment - - edited fabiang After several tests, I find the problem. I defined a JENKINS_SSH_KEY_ID credential with a functionnal SSH Key in my Jenkins with one of my servers (ie : mygreatserver). I also defined a JENKINS_DOCKERHUB_CREDENTIALS_ID credential with "tata" as password and "toto" as username in my Jenkins. With the following script with string interpolation , I reproduce the "A secret was passed to "sshCommand" using Groovy String interpolation, which is insecure." : node{ withCredentials([ sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID' , keyFileVariable: 'SSH_KEYFILE' , passphraseVariable: 'SSH_PASSPHRASE' , usernameVariable: 'SSH_USERNAME' ), usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID' , passwordVariable: 'DOCKER_PASSWD' , usernameVariable: 'DOCKER_USERNAME' ) ]) { script { def remote = [:] remote.name = 'mygreatserver' remote.host = 'mygreatserver' remote.allowAnyHosts = true remote.user = SSH_USERNAME remote.identityFile = SSH_KEYFILE remote.passphrase = SSH_PASSPHRASE sshCommand remote: remote, command: """ hostname > /tmp/test """ sshCommand remote: remote, command: """ echo "Password : ${DOCKER_PASSWD} Username : ${DOCKER_USERNAME}" >> /tmp/test """ } } } Here is the /tmp/test file content :  mygreatserver Password : tata Username : toto   But with the following script without script interpolation , I think I solve your problem : node{ withCredentials([ sshUserPrivateKey(credentialsId: 'JENKINS_SSH_KEY_ID' , keyFileVariable: 'SSH_KEYFILE' , passphraseVariable: 'SSH_PASSPHRASE' , usernameVariable: 'SSH_USERNAME' ), usernamePassword(credentialsId: 'JENKINS_DOCKERHUB_CREDENTIALS_ID' , passwordVariable: 'DOCKER_PASSWD' , usernameVariable: 'DOCKER_USERNAME' ) ]) { script { def remote = [:] remote.name = 'mygreatserver' remote.host = 'mygreatserver' remote.allowAnyHosts = true remote.user = SSH_USERNAME remote.identityFile = SSH_KEYFILE remote.passphrase = SSH_PASSPHRASE sshCommand remote: remote, command: "hostname > /tmp/test" sshCommand remote: remote, command: "echo 'Password : " + DOCKER_PASSWD + " Username : " + DOCKER_USERNAME + "' >> /tmp/test" } } } Here is the /tmp/test file content : mygreatserver Password : tata Username : toto I have no warning no more. The probleme is in the multines script. You should concatenate your variables, but you can't do it in a multiline script way.  

          jlenuff Thanks for your efforts.

          Unfortunately your solution seems like an workaround to me. From the documentation:

          means that the literal value will be visible as an argument to the sh process on the agent in OS process listings.

          With your solution this is still the case. So imho Jenkins should also trigger a warning when string concatenation is used.

          Fabian Grutschus added a comment - jlenuff Thanks for your efforts. Unfortunately your solution seems like an workaround to me. From the documentation : means that the literal value will be visible as an argument to the sh process on the agent in OS process listings. With your solution this is still the case. So imho Jenkins should also trigger a warning when string concatenation is used.

          Idris added a comment -

          Just here to confirm what Fabian posted earlier.

           

          Jordan: what you posted was indeed just a workaround. 

           

          So far, there doesn't seem to be a fix yet... Hopefully somebody can't patch it up quickly.

          Idris added a comment - Just here to confirm what Fabian posted earlier.   Jordan: what you posted was indeed just a workaround.    So far, there doesn't seem to be a fix yet... Hopefully somebody can't patch it up quickly.

            nrayapati Naresh Rayapati
            fabiang Fabian Grutschus
            Votes:
            7 Vote for this issue
            Watchers:
            10 Start watching this issue

              Created:
              Updated: