cmv has asked for the wisdom of the Perl Monks concerning the following question:

Monks-

I have the unfortunate task to execute various commands on various machines via ssh in a very restricted environment. By that I mean, for me to run a command on target machine D, I need to run nested ssh commands from machine A through machines B and C. We've already explored other alternatives in a previous thread, and so far, nested ssh commands seems to be the most reasonable route to go.

Given that, the question to be addressed here is how can I build a reasonable structure of quoting these nested ssh commands?

Here are some examples that show some pretty nasty looking solutions I've come up with. I'm hoping there is something better, but from the slim search results that I've done, I'm not holding my breath. I know that if anyone can help, I'll find them here.

I should explain that from machine A->B, I'm using Net::SSH::Perl to connect, and run the commands that appear in the examples. The rest of the machines B->C, and C->D, are all Sun boxes running Solaris, and I'm simply using the provided ssh binary (which is very old).

The contents of $cmd is provided as an argument to a subroutine that runs it on machine B.

What is happening here is that I've setup public/private keys on all the machines in the chain except for the last one. At this point, I'm trying to setup the final public key on the last machine in the chain. The commands have been simplified somewhat for readability (no really, the actual commands are even uglier).

Example 1

I was happy with this one for awhile, until I realized that the $HOME was being expanded on machine B instead of machine D. The only way I found it was that the same user had different home directories on the two machines.
my $cmd = "/usr/bin/ssh -t -i \$HOME/.ssh/privkey user\@machineC '/usr +/bin/ssh -t -l user machineD \"mkdir \$HOME/.ssh;chmod go-w \$HOME && + chmod go-w \$HOME/.ssh && ( echo $public_key ) >>\$HOME/.ssh/authori +zed_keys\"'";

Example 2

This was my fix for Example 1, but I never could quite get it to work right. I couldn't figure out why, and isn't very easy on the eyes.
#/usr/bin/ssh -t -i \$HOME/.ssh/privkey user\@machineC '/usr/bin/ssh - +t -l user machineD mkdir \\\$HOME/.ssh;chmod go-w \\\$HOME && chmod g +o-w \\\$HOME/.ssh && ( echo $public_key ) >>\\\$HOME/.ssh/authorized_ +keys';

Example 3:

This is what I ended up with that works. It is my current favorite (maybe least-hated is a better way to say it). Using the embedded perl here-document style seems to make this more understandable than other methods, even though I still have to backslash the heck out of dollar, semi-colon, double-quotes, and redirects.

my $cmd = <<EOF; /usr/bin/ssh -t -i \$HOME/.ssh/LeadRNCkey -l usr machineC '/usr/bin/ss +h -t -l usr machineD uname -a\\; mkdir \\\$HOME/.ssh\\; chmod go-w \\ +\$HOME\\; chmod go-w \\\$HOME/.ssh\\; echo \\"$public_key\\\n\\" \\>\ +\>\\\$HOME/.ssh/authorized_keys' EOF
Any thoughts or pointers are much appreciated!

Thanks

-Craig

Replies are listed 'Best First'.
Re: Quoting Solutions for Nested SSH Commands?
by salva (Canon) on Feb 04, 2009 at 09:25 UTC
    Don't do it by hand, make a sub to do the quoting... or move to Net::OpenSSH that already has a shell quoting method (or just copy the relevant code from there ;-)
    use Net::OpenSSH; my $public_key = "..."; my $cmd1 = <<EOC; uname -a mkdir \$HOME/.ssh chmod go-w \$HOME \$HOME/.ssh echo "$public_key" >>\$HOME/.ssh/authorized_keys EOC $cmd1 =~ s/\n/;/g; $cmd1 = Net::OpenSSH->shell_quote($cmd1); my $cmd2 = '/usr/bin/ssh -t -l usr machineD ' . $cmd1; $cmd2 = Net::OpenSSH->shell_quote($cmd2); my $cmd3 = '/usr/bin/ssh -t -i $HOME/.ssh/LeadRNCkey -l usr machineC ' + . $cmd2; system $cmd3;
Re: Quoting Solutions for Nested SSH Commands?
by ELISHEVA (Prior) on Feb 04, 2009 at 07:59 UTC

    I had a similar situation once where I had to generate scripts in real-time on a local machine and then run them on a remote machine (we had a cluster glued together with ssh and the local machine was the master node). The way I handled it was to stuff the entire generated script into a here document. Our bash script looked something like this:

    ssh -T nodeN <<"EOF" ... script here ... EOF

    The quotes around the EOF are essential. They prevent variable names and backticks from being resolved locally. Thus they get resolved only when the script is run on the remote machine. The -T option disables pseudo-tty allocation. For reasons I don't quite remember we needed to do that to get this to work.

    This approach also has the advantage that ssh will return with the exit code of the last command in the here document so you know what happened when you ran the script remotely.

    As for Perl - we used it to generate the remote script and the bash command. however, the command itself was run directly via bash - but that was particular to our situation and my being neurotic about being able to inspect any generated code before I run it.

    Best, beth