title: How to use SSH key in a Windows Container
body: <p>I have created a windows container that includes a Git client and I am able to clone and checkout from our Bitbucket server using HTTP. This means I need to provide username and password for every transaction with Git.
  I am now wanting to rather use SSH. So I have generated an SSH key using ssh-keygen and have added the public key to my user account in Bitbucket and on my local PC I am able to transact with Bitbucket successfully as I loaded my key using Pageant.</p>
  <p>Within the container I am struggling to get this to work.
  I found some info <a href="https://code.visualstudio.com/docs/remote/containers#_sharing-git-credentials-with-your-container" rel="nofollow noreferrer">here</a> which I included in my Dockerfile - specifically the commands Set-Service, Start-Service and Get-Service.
  This works up to a point, this line <code>RUN printf &quot;${SSH_KEY_PASSPHRASE}&quot; | ssh-add C:\Users\Jenkins\.ssh\id_rsa</code> fails to build - it doesn't recognize <code>printf</code>. I have tried to replace it with <code>echo</code> but also fails.</p>
  <p>If I remove this line from my Dockerfile I can build and run this container; I then tried go into it with a powershell session and tried to run <code>ssh-add C:\Users\Jenkins\.ssh\id_rsa</code> manually but I get response <code>Could not open a connection to your authentication agent.</code>
  If I run <code>Get-Service ssh-agent</code> it reports that ssh-agent is running.</p>
  <p>So my questions are as follows:</p>
  <ol>
  <li>Why is the <code>ssh-add</code> command not working?</li>
  <li>How to I add my ssh key within this container?</li>
  <li>When I get the <code>ssh-add</code> command to work, how do I specify the passphrase in the Dockerfile?</li>
  </ol>
  <p>Here is my Dockerfile:</p>
  <pre><code># escape=`
  
  FROM mcr.microsoft.com/windows/servercore
  ARG SSH_KEY_PASSPHRASE
  
  COPY .\files\Git-2.18.0-64-bit.exe      C:\installers\git-installer.exe
  COPY .\files\Git-2.18.0-64-bit.inf      C:\installers\git-installer-script.inf
  RUN powershell -Command `
    # Install Git ; `
    Start-Process -filepath C:\installers\git-installer.exe -passthru -wait -argumentlist &quot;/LOADINF=C:\installers\git-installer-script.inf&quot;,&quot;/VERYSILENT&quot;,&quot;/NORESTART&quot; ; `
  Remove-Item C:\installers\ -Force -Recurse;
  
  RUN powershell -Command `
    net user /add Jenkins; `
    net localgroup Administrators Jenkins /add;
  USER Jenkins
  RUN powershell -Command `
    mkdir ~\.ssh;
  
  COPY .\id_rsa  C:\Users\Jenkins\.ssh\id_rsa
  
  RUN powershell -Command `
    # Make sure you're running as an Administrator `
    Set-Service ssh-agent -StartupType Automatic; `
    Start-Service ssh-agent; `
    Get-Service ssh-agent; 
  
  # This doesn't work and I don't know how to do this
  RUN  printf &quot;${SSH_KEY_PASSPHRASE}&quot; | ssh-add C:\Users\Jenkins\.ssh\id_rsa
  </code></pre>
  <hr />
  <p><strong>Edit 1</strong></p>
  <p>I have now discovered that if run this container (without the <code>ssh-add</code> command in the dockerfile) and then run a git clone using the ssh URL, it states that the authenticity of the bitbucket host can't be established, do you want to continue connecting? I enter &quot;yes&quot; and it then &quot;<em>permanently adds my bit bucket host to the list of known hosts</em>&quot; before asking for the passphrase for key 'c/Users/Jenkins/.ssh/id_rsa'.</p>
  <p>Once I enter the passphrase it successfully clones the repo.
  This result surprises me and results in a new question:</p>
  <ul>
  <li>How does it know about the ssh key in c/Users/Jenkins/.ssh if I haven't added it yet? I'm clearly missing some understanding somewhere.</li>
  </ul>
  <hr />
  <p><strong>Edit 2</strong></p>
  <p>I have played around with my Dockerfile and found that the line
  <code>RUN powershell -Command mkdir ~\.ssh</code> seems to be important in some way.
  If I remove this line and run the container, I can see that my key is present in <code>C:\Users\Jenkins\.ssh\</code> but if I run <code>ssh-add .\Users\Jenkins\.ssh\id_rsa</code> it fails with <code>Permissions for id_rsa are too open</code>.
  If I include the <code>mkdir</code> line there is no obvious difference to me but the <code>ssh-add</code> command seems to work and reports <code>Identity added: .\Users\Jenkins\.ssh\id_rsa (&lt;my company domain&gt;+&lt;my windows username@PC name&gt;)</code>.</p>
  <p>If I don't manually create the .ssh folder it still seems to be implicitly created during the <code>COPY</code> command but the <code>ssh-add</code> fails. What is going on here?</p>
  <p>Next I included the <code>ssh-add</code> command into my Dockerfile and rebuilt it and could see that the key was added - it reports <code>Identity added: .\Users\Jenkins\.ssh\id_rsa</code>.</p>
  <p>At this stage I am not able to interact with Git as the path was not setup, so I modified my Dockerfile to setup the path by adding the following lines to the command where I install Git;</p>
  <pre><code>${env:PATH} = 'C:\Git\bin;C:\Git\usr\bin;C:\Git\mingw64\bin;' + $env:PATH;
  [Environment]::SetEnvironmentVariable('PATH', ${env:PATH}, [EnvironmentVariableTarget]::Machine);
  </code></pre>
  <p>Now when I build the image the ssh-add command fails with &quot;Could not open a connection to your authentication agent.&quot;
  I ran <code>Get-Service ssh-agent</code> which reports that it is running.</p>
  <p>So somehow the inclusion of Git into the path has resulted in a difference regarding the <code>ssh-add</code> command.
  What is happening here?
  How do I resolve this?</p>
  <p>I also recreated the key without a passphrase so have removed the need to enter a passphrase during the docker build and I specified the version of windows.</p>
  <p>Here is my updated Dockerfile:</p>
  <pre><code># escape=`
  
  FROM mcr.microsoft.com/windows/servercore:1903
  
  # Install Git
  COPY .\files\Git-2.18.0-64-bit.exe      C:\installers\git-installer.exe
  COPY .\files\Git-2.18.0-64-bit.inf      C:\installers\git-installer-script.inf
  RUN powershell -Command `
    Start-Process -filepath C:\installers\git-installer.exe -passthru -wait -argumentlist &quot;/LOADINF=C:\installers\git-installer-script.inf&quot;,&quot;/VERYSILENT&quot;,&quot;/NORESTART&quot; ; `
    Remove-Item C:\installers\ -Force -Recurse; `
    # Set the path to include Git folders - not listed in my original post `
    ${env:PATH} = 'C:\Git\bin;C:\Git\usr\bin;C:\Git\mingw64\bin;' + $env:PATH; `
    [Environment]::SetEnvironmentVariable('PATH', ${env:PATH}, [EnvironmentVariableTarget]::Machine);
  
  # Create a Jenkins user with admin permissions
  RUN powershell -Command `
    net user /add Jenkins; `
    net localgroup Administrators Jenkins /add;
    USER Jenkins
  # The presence or absence of this mkdir command affects ssh-add for some reason
  RUN powershell -Command `
    mkdir ~\.ssh;
  
  # This key was created without a passphrase
  COPY .\id_rsa  C:\Users\Jenkins\.ssh\id_rsa
  
  RUN powershell -Command `
    # Make sure you're running as an Administrator `
    Set-Service ssh-agent -StartupType Automatic; `
    Start-Service ssh-agent; `
    Get-Service ssh-agent; `
    ssh-add .\Users\Jenkins\.ssh\id_rsa;
  </code></pre>
  <hr />
  <p><strong>Edit 3</strong></p>
  <p>Thanks to information I found <a href="https://coderwall.com/p/rdi_wq/fix-could-not-open-a-connection-to-your-authentication-agent-when-using-ssh-add" rel="nofollow noreferrer">here</a> I it seems that the inability to run <code>ssh-add</code> successfully was due to the <code>SSH_AUTH_SOCK</code> environment variable not being set.<br />
  Running <code>ssh-agent</code> provides the data needed to set this environment variable and in Linux you could run <code>eval $(ssh-agent)</code> but powershell in windows doesn't recognize the <code>eval</code> command. At this stage I tried commenting out the <code>ssh-add</code> command from my last dockerfile and then inside the container I manually ran the <code>ssh-agent</code> command and set the environment variable using <code>$Env:SSH_AUTH_SOCK = &quot;&lt;value returned from ssh-agent&gt;&quot;</code> which succeeded and if I run <code>Get-ChildItem Env:</code> I see this new environment variable listed.</p>
  <p>I then tried to run a <code>git clone ssh://git@mybitbucketurl/myrepo.git</code> which reported that &quot;the authenticity of host mybitbucketurl (xx.yy.zz.aa:port) can't be established. Are you sure you want to continue (yes/no)?&quot;<br />
  I entered &quot;yes&quot; and it &quot;Permanently added mybitbucketurl:port,[xx.yy.zz.aa]:port (RSA) to the list of known hosts&quot; and then cloned my repository.</p>
  <p>I thought I had this cracked now and thought I just needed to work out how to do the <code>eval</code> command equivalent in powershell. While I was searching for clues on how to do this I found this <a href="https://stackoverflow.com/questions/24154816/git-bash-could-not-open-a-connection-to-your-authentication-agent">link</a> with an answer that listed this command:
  <code>ssh-agent ssh-add C:\Users\&lt;username&gt;\.ssh\id_rsa</code>.
  I tried running this manually inside my container and it reported that the identity was added, however when I tried to run a <code>git clone</code> it first add the URL to the list of known hosts and then failed with &quot;fatal: could not read from remote repository&quot;.
  Running <code>Get-ChildItem Env:</code> showed that the <code>SSH_AUTH_SOCK</code> environment variable is not set so I guess that running it like this is an isolated session of sorts although I don't really know what is happening with this command.</p>
  <p>So from here it looks like I need to work out how to run the <code>ssh-agent</code> command and use its result in powershell to setup the environment variable permanently.
  Once I have got that working I need to work out how to add my Git host URL to the list of known hosts.</p>
  <hr />
  <p><strong>Edit 4</strong></p>
  <p>The crux of my issue seems to be the difference between the build-time and run-time of my docker image and specifically of what I am hoping to achieve with <code>ssh-agent</code> and <code>ssh-add</code>.
  If I setup my image to include the ssh key and then run it, I am able to manually do the following (note that I've switched from powershell to bash):</p>
  <pre><code>$ echo $SSH_AUTH_SOCK
  
  $ eval $(ssh-agent)
  Agent pid 2036
  $ sh-add Users/Jenkins/.ssh/id_rsa
  Identity added: Users/Jenkins/.ssh/id_rsa
  $ echo $SSH_AUTH_SOCK
  /tmp/ssh-qCM264wFqpqq/agent.1748
  </code></pre>
  <p>So from this I can see that at run-time in the container it does what I want it to.
  However if I try and run these commands at build-time from the Dockerfile things do not work as I would hope.
  After some thought I suspect that the <code>eval $(ssh-agent)</code> command probably runs but the <code>SSH_AUTH_SOCK</code> environment variable is not retained within the image's layer and when the container is run ssh-agent will not be running, something probably needs to launch it.</p>
  <p>What is the right way to do this:</p>
  <ol>
  <li>Do I try to get these commands to run during startup of the container via a startup script?</li>
  <li>Is there a way to still do this at build time (i.e. from the Dockerfile) - to achieve this I would manually need to set the <code>SSH_AUTH_SOCK</code> environment variable but this seems to be different every time you run <code>ssh-agent</code> - is this worth pursuing?</li>
  </ol>