Repository: iterative/dvc

Issue:
SSH password authentication not working for dvc push
DVC version: 0.52.1
Platform and method of installation: Windows 10 + pip installed

I am using remote cache and storage (ssh) to version folders of image. However when I try to 

`dvc push`

 I have a strange comportment. Indeed, I have been asked to enter my ssh password once but it does check the local cache first and later it asked 4 times in my password if I enter it once it does not work and if I press enter 4 times I get the following error.

I did not have this issue for `dvc add`

![image](https://user-images.githubusercontent.com/37658138/61626059-83e11800-ac7c-11e9-8dfe-9ac93d9bdf43.png)





Discussion:
@efiop , this issue is slightly different than the one in #2310 

This is specifically related to not `push`ing anything, even if the password was entered. I assume that the problem is due to the long `TIMEOUT` (currently **30 minutes**) to establish a connection to the SSH server.
There might be any error on the URL or configuration and it is waiting for the time out (maybe because it can't reach the SSH server) instead of closing the connection right away with a `Permission denied`.

The other issue ( #2310 ) is about the interface (asking for password multiple times -- once for each worker)
@mroutis So why I would have an error when using a passphrase and why it would work without any passphrase ?

:eyes: ! @thomasryck , my bad! I didn't notice the `Authentication Failed` error.
I thought (from the conversation on Discord) that it hung for more than 10 minutes without error :thinking: 
When I tried to reproduce it I was able to make a successful connection with the prompt (see #2310)
I think that there are multiple issues under the hood. The authentication issue is probably on my side  but it is hard to debug with the other problems. 
To be precise when it hang for a long time, some time when I keyboard interrupt sometimes I get `Keyboard Interrupt` and sometimes I get `Authentication failed`
If we have any other error here besides several password prompts then it effectively masked. So I will go for that first.

Resolving patch:
diff --git a/dvc/remote/ssh/__init__.py b/dvc/remote/ssh/__init__.py
--- a/dvc/remote/ssh/__init__.py
+++ b/dvc/remote/ssh/__init__.py
@@ -6,6 +6,7 @@
 import itertools
 import errno
 from concurrent.futures import ThreadPoolExecutor
+import threading
 
 try:
     import paramiko
@@ -26,6 +27,10 @@
 logger = logging.getLogger(__name__)
 
 
+saved_passwords = {}
+saved_passwords_lock = threading.Lock()
+
+
 class RemoteSSH(RemoteBASE):
     scheme = Schemes.SSH
     REQUIRES = {"paramiko": paramiko}
@@ -115,13 +120,19 @@ def ssh(self, path_info):
         host, user, port = path_info.host, path_info.user, path_info.port
 
         # NOTE: we use the same password regardless of the server :(
-        if self.ask_password and not self.password:
-            self.password = prompt.password(
-                "Enter a private key passphrase or a password for "
-                "host '{host}' port '{port}' user '{user}'".format(
-                    host=host, port=port, user=user
-                )
-            )
+        if self.ask_password and self.password is None:
+            with saved_passwords_lock:
+                server_key = (host, user, port)
+                password = saved_passwords.get(server_key)
+
+                if password is None:
+                    saved_passwords[server_key] = password = prompt.password(
+                        "Enter a private key passphrase or a password for "
+                        "host '{host}' port '{port}' user '{user}'".format(
+                            host=host, port=port, user=user
+                        )
+                    )
+                self.password = password
 
         return get_connection(
             SSHConnection,


Test patch: