Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authenticating with user/password *once* for multiple commands? (session multiplexing)

I got this trick from Solaris documentation, for copying ssh public keys to remote hosts.

note: ssh-copy-id isn't available on Solaris

$ cat some_data_file | ssh user@host "cat >/tmp/some_data_file; some_shell_cmd"

I wanted to adapt it to do more involved things.

Specifically I wanted some_shell_command to be a script sent from the local host to execute on the remote host... a script would interact with the local keyboard (e.g. prompt user when the script was running on the remote host).

I experimented with ways of sending multiple things over stdin from multiple sources. But certain things that work in in local shell don't work over ssh, and some things, such as the following, didn't do what I wanted at all:

$ echo "abc" | cat <(echo "def")     # echoes: def  (I wanted abc\ndef)
$ echo "abc" | cat  < <(echo "def")  # echoes: def  (I wanted abc\ndef)

$ echo "abc" | cat <<-EOF
> echo $(</dev/stdin)   #echoes: echo abc  (I wanted: abc) 
> EOF

# messed with eval for the above but that was a problem too.

@chepner concluded it's not feasible to do all of that in a single ssh command. He suggested a theoretical alternative that didn't work as hoped, but I got it working after some research and tweaking and documented the results of that and posted it as an answer to this question.

Without that solution, having to run multiple ssh, and scp commands by default entails being prompted for password multiple times, which is a major drag.

I can't expect all the users of a script I write in a multi-user environment to configure public key authorization, nor expect they will put up with having to enter a password over and over.

like image 929
clearlight Avatar asked Dec 06 '22 15:12

clearlight


2 Answers

OpenSSH Session Multiplexing


    This solution works even when using earlier versions of OpenSSH where the
    ControlPersistoption isn't available. (Working bash example at end of this answer)


Note: OpenSSH 3.9 introduced Session Multiplexing over a "control master connection" (in 2005), However, the ControlPersist option wasn't introduced until OpenSSH 5.6 (released in 2010).

ssh session multiplexing allows a script to authenticate once and do multiple ssh transactions over the authenticated connection. For example, if you have a script that runs several distinct tasks using ssh, scp, or sftp, each transaction can be carried out over OpenSSH 'control master session' that refers to location of its named-socket in the filesystem.

The following one-time-password authentication is useful when running a script that has to perform multiple ssh operations and one wants to avoid users having to password authenticate more than once, and is especially useful in cases where public key authentication isn't viable - e.g. not permitted, or at least not configured.


enter image description here


Most solutions I've seen entail using ControlPersist to tell ssh to keep the control master connection open, either indefinitely, or for some specific number of seconds.

Unfortunately, systems with OpenSSH prior to 5.6 don't have that option (wherein upgrading them might not be feasible). Unfortunately, there doesn't seem to be much documentation or discussion about that limitation online.

Reading through old release docs I discovered ControlPersist arrived late in the game for ssh session multiplexing scene. implying there may have been an alternative way to configure session multiplexing without relying on the ControlPersist option prior to it.

Initially trying to configure persistent-sessions from command line options rather than the config parameter, I ran into the problem of the ssh session terminating prematurely, closing control connection client sessions with it, or, alternatively, the connection was held open (kept ssh control master alive), terminal I/O was blocked, and the script would hang.

The following clarifies how to accomplish it.


OpenSSH option            ssh flag          Purpose
-------------------       ---------         -----------------------------
-o ControlMaster=yes      -M                Establishes sharable connection
-o ControlPath=path       -S path           Specifies path of connection's named socket
-o ControlPersist=600                       Keep shareable connection open 10 min.
-o ControlPersist=yes                       Keep shareable connection open indefinitely
                          -N                Don't create shell or run a command
                          -f                Go into background after authenticating
                          -O exit           Closes persistent connection
ControlPersist form       Equivalent        Purpose
-------------------       ----------------  -------------------------
-o ControlPersist=yes     ssh -Nf           Keep control connection open indefinitely
-o ControlPersist=300     ssh -f sleep 300  Keep control connection open 5 min.

Note: scp and sftp implement -S flag differently, and -M flag not at all, so, for those commands, the -o option form is always required.


Sketchy Overview of Operations:
Note: This incomplete example doesn't execute as shown.

ctl=<path to dir to store named socket>
ssh -fNMS $ctl user@host      # open control master connection
ssh -S $ctl …                 # example of ssh over connection
scp  -o ControlPath=$ctl …    # example of scp over connection
sftp -o ControlPath=$ctl …    # example of sftp over connection
ssh -S $ctl -O exit           # close control master connection

Session Multiplexing Demo

(Try it. You'll like it. Working example - authenticates only once):

Running this script will probably help you understand it quicker than reading it, and it is fascinating.

Note: If you lack access to remote host, just enter localhost at the "Host...?" prompt if you want to try this demo script

#!/bin/bash       # This script demonstrates ssh session multiplexing

trap "[ -z "$ctl" ] || ssh -S $ctl -O exit $user@$host" EXIT # closes conn, deletes fifo

read -p "Host to connect to? " host
read -p "User to login with? " user

BOLD="\n$(tput bold)"; NORMAL="$(tput sgr0)"

echo -e "${BOLD}Create authenticated persistent control master connection:${NORMAL}"

sshfifos=~/.ssh/controlmasters

[ -d $sshfifos ] || mkdir -p $sshfifos; chmod 755 $sshfifos

ctl=$sshfifos/$user@$host:22 # ssh stores named socket ctrl conn here

ssh -fNMS $ctl $user@$host  # Control Master: Prompts passwd then persists in background

lcldir=$(mktemp -d /tmp/XXXX)

echo -e "\nLocal  dir: $lcldir"

rmtdir=$(ssh -S $ctl $user@$host "mktemp -d /tmp/XXXX")

echo      "Remote dir: $rmtdir"

echo -e "${BOLD}Copy self to remote with scp:${NORMAL}"

scp -o ControlPath=$ctl ${BASH_SOURCE[0]} $user@$host:$rmtdir 

echo -e "${BOLD}Display 4 lines of remote script, with ssh:${NORMAL}"
echo "====================================================================="
echo $rmtdir | ssh -S $ctl $user@$host "dir=$(</dev/stdin); head -4 \$dir/*"
echo "====================================================================="

echo -e "${BOLD}Do some pointless things with sftp:${NORMAL}"
sftp -o ControlPath=$ctl $user@$host:$rmtdir <<EOF
    pwd
    ls
    lcd $lcldir
    get *
    quit
EOF
like image 170
clearlight Avatar answered Dec 30 '22 11:12

clearlight


Using a master control socket, you can use multiple processes without having to authenticate more than once. This is just a simple example; see man ssh_config under ControlPath for advice on using a more secure socket.

It's not quite clear what you mean by sourcing somecommand locally; I'm going to assume it is a local script that you want copied over to the remote host. The simplest thing to do is just copy it over to run it.

# Copy the first file, and tell ssh to keep the connection open
# in the background after scp completes
$ scp -o ControlMaster=yes -o ControlPersist=yes -o ControlPath=%C somefile user@host:/tmp/somefile
# Copy the script on the same connection
$ scp -o ControlPath=%C somecommand user@host:
# Run the script on the same connection
$ ssh -o ControlPath=%C user@host somecommand
# Close the connection
$ ssh -o ControlPath=%C -O exit user@host

Of course, the user could use public key authentication to avoid entering their credentials at all, but ssh would still go through the authentication process each time. Here, the authentication process is only done once, by the command using ControlMaster=yes. The other two processes reuse that connection. The last commmand, with -O exit, doesn't actually connect; it just tells the local connection to close itself.

like image 32
chepner Avatar answered Dec 30 '22 11:12

chepner