Dockerfile: clone repo with passwordless private key. Errors: "authentication agent" or "read_passphrase: can't open /dev/tty"

I try to clone a test project from GitLab using a Dockerfile and an SSH key pair: ssh-keygen -t rsa -P "". The private key is passwordless, the public key is published at the GitLab account.

This can be quickly tested by anyone else, just open an account on GitLab and publish your SSH public key, and add a new empty project to clone.

Without Docker, using ssh -i C:\path\to\my\private_key\id_rsa [email protected], it works. Without Docker, cloning the project also works.

I load the private key in the Dockerfile and delete it in the end. Not important, but for all who see a security risk here: I delete the key pair directly after usage, both on client and server. I only try to get around the password entry for the user account when cloning, since that does not seem to work during the run of a Dockerfile, and would be the real security risk as it could leave the password in the logs.

Start: Go to the Dockerfile directory, paste the private key "id_rsa" in a new ".ssh" subfolder. Then:

docker build -t NEW_IMAGENAME . --build-arg ssh_prv_key="$(cat ./.ssh/id_rsa)"

Working code up to now:

FROM vcatechnology/linux-mint:18.2
ARG ssh_prv_key
RUN apt-get update && \
    apt upgrade -y && \
    apt-get install -y git  
RUN apt-get install -y openssh-client # openssh-server
RUN mkdir /root/.ssh/
RUN echo "Host *\n  User git\n  HostName gitlab.com\n  AddKeysToAgent yes\n  IdentityFile /root/.ssh/id_rsa" >> /etc/ssh/ssh_config
RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa

RUN ssh-keyscan -t rsa -H gitlab.com >> /root/.ssh/known_hosts
RUN ssh -o StrictHostKeyChecking=no [email protected] || true
RUN eval "$(ssh-agent -s)"
RUN chmod 666 /dev/tty

Which leads to:

docker build -t CONTAINERNAME . --build-arg ssh_prv_key="$(cat /.ssh/id_rsa)"
[+] Building 254.9s (14/17)
 => [internal] load build definition from Dockerfile                                                                                                                                                                                    0.0s
 => => transferring dockerfile: 6.84kB                                                                                                                                                                                                  0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                       0.0s
 => => transferring context: 2B                                                                                                                                                                                                         0.0s
 => [internal] load metadata for docker.io/vcatechnology/linux-mint:18.2                                                                                                                                                                0.8s
 => CACHED [ 1/14] FROM docker.io/vcatechnology/linux-mint:18.2@sha256:0557a4999d43c0c622f3a57c3db5b13536024fb5999ecf4f03c6ffec0e4fdb47                                                                                                 0.0s
 => [ 2/14] RUN apt-get update &&  apt upgrade -y &&  apt-get install -y git                                                                                                                                                          244.3s
 => [ 3/14] RUN apt-get install -y openssh-client # openssh-server                                                                                                                                                                      3.1s
 => [ 4/14] RUN mkdir /root/.ssh/                                                                                                                                                                                                       0.6s
 => [ 5/14] RUN echo "Host *\n  User git\n  HostName gitlab.com\n  AddKeysToAgent yes\n  IdentityFile /root/.ssh/id_rsa" >> /etc/ssh/ssh_config                                                                                         0.6s
 => [ 6/14] RUN echo "$(cat /.ssh/id_rsa)" > /root/.ssh/id_rsa &&     chmod 600 /root/.ssh/id_rsa                                                                                                                                       0.6s
 => [ 7/14] RUN ssh-keyscan -t rsa -H gitlab.com >> /root/.ssh/known_hosts                                                                                                                                                              1.3s
 => [ 8/14] RUN ssh -o StrictHostKeyChecking=no [email protected] || true                                                                                                                                                                  1.6s
 => [ 9/14] RUN eval "$(ssh-agent -s)"                                                                                                                                                                                                  0.6s
 => [10/14] RUN chmod 666 /dev/tty                                                                                                                                                                                                      0.6s

The last steps after this working code in the Dockerfile would be as follows, though each of the first three lines stops the script up to now:

RUN ssh-add /root/.ssh/id_rsa
RUN ssh -tti /root/.ssh/id_rsa [email protected]
RUN git clone [email protected]:GITLAB_USERNAME/test.git
RUN rm -r /root/.ssh
  1. If RUN ssh-add /root/.ssh/id_rsa comes directly after the last working code:

      => ERROR [11/14] RUN ssh-add /root/.ssh/id_rsa                                                                                                                                                                                         0.6s
     ------
      > [11/14] RUN ssh-add /root/.ssh/id_rsa:
     #14 0.579 Could not open a connection to your authentication agent.
     ------
     executor failed running [/bin/sh -c ssh-add /root/.ssh/id_rsa]: exit code: 2
    

The error Could not open a connection to your authentication agent. is famous, see Could not open a connection to your authentication agent. But I could not solve it with that thread.

  1. If RUN ssh -tti /root/.ssh/id_rsa [email protected] comes directly after the last working code:

     #14 1.545 Permission denied (publickey,keyboard-interactive).
     ------
     executor failed running [/bin/sh -c ssh -tti /root/.ssh/id_rsa [email protected]]: exit code: 255
    
  2. If RUN git clone [email protected]:GITLAB_USERNAME/test.git comes directly after the last working code:

     #16 0.450 Cloning into 'test'...
     #16 1.466 Permission denied (publickey,keyboard-interactive).
     #16 1.467 fatal: Could not read from remote repository.
     #16 1.467
     #16 1.467 Please make sure you have the correct access rights
     #16 1.467 and the repository exists.
     ------
     executor failed running [/bin/sh -c git clone [email protected]:GITLAB_USERNAME/test.git]: exit code: 128
    

Thus, ssh obviously needs the "ssh-add" that adds the private key to the "ssh-agent" to know the private key on the client at all, and I guess it also needs the ssh -tti /root/.ssh/id_rsa [email protected] to let ssh know the way to the server.

  1. If RUN ssh -Tvvv [email protected] (the -T avoids Pseudo-terminal will not be allocated because stdin is not a terminal) comes directly after the last working code (this is not needed in the final Dockerfile, it is just a check):

      => ERROR [12/12] RUN ssh -Tvvv [email protected]                                                                                                                                                                                          1.2s
     ------
      > [12/12] RUN ssh -Tvvv [email protected]:
     #16 0.376 OpenSSH_7.2p2 Ubuntu-4ubuntu2.10, OpenSSL 1.0.2g  1 Mar 2016
     #16 0.376 debug1: Reading configuration data /etc/ssh/ssh_config
     #16 0.376 debug1: /etc/ssh/ssh_config line 19: Applying options for *
     #16 0.376 debug1: /etc/ssh/ssh_config line 57: Applying options for *
     #16 0.376 debug2: resolving "gitlab.com" port 22
     #16 0.397 debug2: ssh_connect_direct: needpriv 0
     #16 0.397 debug1: Connecting to gitlab.com [172.65.251.78] port 22.
     #16 0.450 debug1: Connection established.
     #16 0.450 debug1: permanently_set_uid: 0/0
     #16 0.450 debug1: key_load_public: No such file or directory
     #16 0.450 debug1: identity file /root/.ssh/id_rsa type -1
     #16 0.450 debug1: key_load_public: No such file or directory
     #16 0.450 debug1: identity file /root/.ssh/id_rsa-cert type -1
     #16 0.451 debug1: Enabling compatibility mode for protocol 2.0
     #16 0.451 debug1: Local version string SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.10
     #16 0.977 debug1: Remote protocol version 2.0, remote software version OpenSSH_7.9p1 Debian-10+deb10u2
     #16 0.977 debug1: match: OpenSSH_7.9p1 Debian-10+deb10u2 pat OpenSSH* compat 0x04000000
     #16 0.977 debug2: fd 3 setting O_NONBLOCK
     #16 0.977 debug1: Authenticating to gitlab.com:22 as 'git'
     #16 0.977 debug3: hostkeys_foreach: reading file "/root/.ssh/known_hosts"
     #16 0.977 debug3: send packet: type 20
     #16 0.977 debug1: SSH2_MSG_KEXINIT sent
     #16 0.994 debug3: receive packet: type 20
     #16 0.994 debug1: SSH2_MSG_KEXINIT received
     #16 0.994 debug2: local client KEXINIT proposal
     #16 0.994 debug2: KEX algorithms: [email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,ext-info-c
     #16 0.994 debug2: host key algorithms: [email protected],[email protected],[email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
     #16 0.994 debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
     #16 0.994 debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
     #16 0.994 debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
     #16 0.994 debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
     #16 0.994 debug2: compression ctos: none,[email protected],zlib
     #16 0.994 debug2: compression stoc: none,[email protected],zlib
     #16 0.994 debug2: languages ctos:
     #16 0.994 debug2: languages stoc:
     #16 0.994 debug2: first_kex_follows 0
     #16 0.994 debug2: reserved 0
     #16 0.994 debug2: peer server KEXINIT proposal
     #16 0.994 debug2: KEX algorithms: curve25519-sha256,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1
     #16 0.994 debug2: host key algorithms: rsa-sha2-512,rsa-sha2-256,ssh-rsa,ecdsa-sha2-nistp256,ssh-ed25519
     #16 0.994 debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
     #16 0.994 debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
     #16 0.994 debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
     #16 0.994 debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
     #16 0.994 debug2: compression ctos: none,[email protected]
     #16 0.994 debug2: compression stoc: none,[email protected]
     #16 0.994 debug2: languages ctos:
     #16 0.994 debug2: languages stoc:
     #16 0.994 debug2: first_kex_follows 0
     #16 0.994 debug2: reserved 0
     #16 0.994 debug1: kex: algorithm: [email protected]
     #16 0.994 debug1: kex: host key algorithm: ecdsa-sha2-nistp256
     #16 0.994 debug1: kex: server->client cipher: [email protected] MAC: <implicit> compression: none
     #16 0.994 debug1: kex: client->server cipher: [email protected] MAC: <implicit> compression: none
     #16 1.014 debug3: send packet: type 30
     #16 1.014 debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
     #16 1.182 debug3: receive packet: type 31
     #16 1.185 debug1: Server host key: ecdsa-sha2-nistp256 SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw
     #16 1.186 debug3: hostkeys_foreach: reading file "/root/.ssh/known_hosts"
     #16 1.187 debug3: hostkeys_foreach: reading file "/root/.ssh/known_hosts"
     #16 1.187 debug1: read_passphrase: can't open /dev/tty: No such device or address
     #16 1.188 Host key verification failed.
     ------
     executor failed running [/bin/sh -c ssh -Tvvv [email protected]]: exit code: 255    
    

I have tried a lot, but up to now, I could not solve the error read_passphrase: can't open /dev/tty: No such device or address. The file exists, else I could not change its rights to 666, using chmod 666 /dev/tty. I guess that a terminal is required just for entering an empty password.

What to change to clone a repo from GitLab with a passwordless SSH key pair, using just one Dockerfile?

If there is no chance to do this in just one Dockerfile, an accepted workaround would be a docker-compose file; this is not the favoured answer, though.

EDITED: The needed path and file can be found when looking into the container.

(The start was: docker build -t . --build-arg ssh_prv_key="$(cat ./.ssh/id_rsa)", until the end of the working code only!)
docker run -d -it --name test_bash -d NEW_IMAGENAME:latest
docker exec -it test_bash bash
cd root/.ssh;ls

The last command shows the id_rsa and the known_hosts.

d3cbc35351fd / # cd root/.ssh;ls
id_rsa  known_hosts

And if I run d3cbc35351fd .ssh # ssh -Tvvv [email protected] inside the container, I am asked for the password:

Enter passphrase for key '/root/.ssh/id_rsa':
debug2: bad passphrase given, try again...
Enter passphrase for key '/root/.ssh/id_rsa':
debug2: bad passphrase given, try again...
Enter passphrase for key '/root/.ssh/id_rsa':
debug2: no passphrase given, try next key

This is a try with "", '' and just pressing enter, they all do not work.

If I cannot use SSH in the Dockerfile and neither in the container of that Dockerfile' image, I wonder whether cloning a repo out of a Dockerfile or container is possible at all. I guess that suppressing the password entry will be the next big step to solve this, but even that might not solve it entirely, since I have already tried entering an empty password in the container, to no avail.


Solution 1:

If you want to get rid of the private key in the image for security reasons, or if you need more information, see Using SSH keys inside docker container with a more thorough answer.


The main error is caused by the

echo "$ssh_prv_key" > /root/.ssh/id_rsa

which passes a wrongly formatted ssh_prv_key, as just one line, although the private key needs many lines. The idea is from Add private key to ssh-agent in docker file that hints at Gitlab CI/Docker: ssh-add keeps asking for passphrase.

--> Thus, do not use RUN echo ... for the private key, instead, use COPY ...!!!

Another small error is RUN ssh -o StrictHostKeyChecking=no [email protected] || true, which does not work, but

both

RUN echo "Host *\n\t StrictHostKeyChecking no" >> /etc/ssh/ssh_config

and

RUN ssh-keyscan -t rsa -H gitlab.com >> /root/.ssh/known_hosts 

work and reach the same aim, choose just one of the two.

Another step is to drop ssh-agent and ssh-add. Both are only needed if you have a password, see Add private key to ssh-agent in docker file.

This is working now.

The Dockerfile looks as follows:

FROM ubuntu:latest
RUN apt-get update && apt-get install -y git
RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh
COPY /.ssh/id_rsa /root/.ssh/id_rsa
RUN chmod 600 /root/.ssh/id_rsa
RUN ssh-keyscan -t rsa -H gitlab.com >> /root/.ssh/known_hosts
RUN git clone [email protected]:GITLAB_USERNAME/test.git
RUN rm -r /root/.ssh

To create the image from the Dockerfile:

  • Go to the directory of the Dockerfile.

  • Paste your private key "id_rsa" (or whatever name you have, then change the code of course) in a new subfolder "/.ssh/" (or paste it in the Dockerfile directory and change the code to COPY id_rsa /root/.ssh/id_rsa).

  • Start (Do not forget the "." at the end, which is the build context):

    docker build -t test .
    

Solution 2:

The ssh-agent doesn't work as expected in the build process, but you don't need it at all.

I build a minimal sample that works

FROM ubuntu:latest
ARG ssh_prv_key

RUN apt-get update \
&& apt-get install -y git

RUN mkdir -p /root/.ssh &&  chmod 700 /root/.ssh

RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa

# Check if the key was successfully copied
# RUN cat -n /root/.ssh/id_rsa

# Dummy ssh to store the host into known_hosts
# The expected output is something like: 
# Hi <user>! You've successfully authenticated, but GitHub does not provide shell access.
RUN ssh -T -o StrictHostKeyChecking=no [email protected] || true

RUN git clone ssh://[email protected]/<myrepo>