Can I get SSH to use an askpass program even if it was run from a terminal?

This will be a bit more complicated, but combination of several pieces will make it working:

Explanation

  1. To force ssh to use $SSH_ASKPASS program, you can't allow ssh to see the real tty. It is just condition. This can be done by using setsid and using -n switch to ssh.

    This case would initiate connection, but you would not be able to interact with the shell, which is probably also your requirement ;) (and also breaks your local TTY).

    But you can give up the "first session". You should also add -N switch, which will suppress the remote command and will do just the authentication.

    Also the possible output "junk" can be redirected to &> /dev/null if you are not interested in it.

  2. Set up ControlMaster in ssh_config. It is cool feature and once the connection is established, you can "fire up" sessions pretty fast. This snippet in ~/.ssh/config should do that:

    ControlPath ~/.ssh/controlmasters/%r@%h:%p
    ControlMaster auto
    ControlPersist 5m
    

    You can add that into some host block listing your "slow candidates", or just everywhere. It is almost no overhead.

Final line

Then you should be able to connect in this way to the host you expect it will take a while:

setsid ssh -nN host
# wait, insert password in the X11 prompt
ssh host
# will bring you directly to your session

Whole process might be simplified by alias or bash function doing both in one step, but it is left on readers imagination.

Only command-line arguments

You can join both things together on command-line without ssh_config part:

setsid ssh -nNMS ~/.ssh/masters/%C host
# wait, insert password in the X11 prompt
ssh -S ~/.ssh/masters/%C host
# will bring you directly to your session

The following function should work when SSH options aren't specified:

ssh() {
    if ! command ssh -o PasswordAuthentication=no "$1" true
    then
        setsid -w ssh -fnN "$1"
    fi
    command ssh "$@"
}
  • -f instructs SSH to go to the background just before program execution, which is after it has got the password.
  • -w tells setsid to wait for the program to end. In this case, that happens when SSH goes to the background. Combined with ssh -f, the manual wait between the two SSH commands can be eliminated.
  • The function assumes the first argument is the hostname.
  • The test is just to prevent unnecessary SSH connections.

A per SSH manual (man ssh):

If ssh does not have a terminal associated with it but DISPLAY and SSH_ASKPASS are set, it will execute the program specified by SSH_ASKPASS.

Therefore you need to disassociate the terminal (e.g. by adding a pipe) and make sure DISPLAY isn't set (if you want to use terminal for your passphrase instead).

Simple example:

echo foo | SSH_ASKPASS=/my/cmd DISPLAY= ssh ...

The same with ssh-add:

$ echo foo | SSH_ASKPASS=/my/cmd DISPLAY= ssh-add id_rsa
ssh_askpass: exec(/my/cmd): No such file or directory

With OpenSSH 8.4 you can set $SSH_ASKPASS_REQUIRE environment variable to force. Quoting ssh(1) man page:

 SSH_ASKPASS_REQUIRE   Allows further control over the use of
                       an askpass program.  If this variable
                       is set to "never" then ssh will never
                       attempt to use one.  If it is set to
                       "prefer", then ssh will prefer to use
                       the askpass program instead of the TTY
                       when requesting passwords.  Finally, if
                       the variable is set to "force", then
                       the askpass program will be used for
                       all passphrase input regardless of
                       whether DISPLAY is set.

As OpenSSH 8.4 was released September 27, 2020 you will need to wait a bit to have this feature available in any major Linux distribution.

Tags:

Linux

Ssh

Openssh