0

I am trying to run below script on remote machine, I am facing two issues here

  1. The while loop defined in here doc is running endless
  2. read enter key does not work on remote host

Without the sshpass command script is working fine on local host.

Can someone look into this and help.

      1 #!/bin/bash
      2 #read -p "Enter the user name to connect to server: " user
      3 #read -s -p "Enter the password : "   passd
      4 #sshpass -p $passd ssh -qt -o StrictHostKeyChecking=no -o ConnectTimeout=4 $user@$1 << 'MYSCR'
      5 ##############################################################################################################################################
      6 while :
      7 do
      8 clear
      9 echo "Server Name - $(hostname)"
     10 echo "1. Check Server Information"
     11 echo "2. Check Network Information"
     12 echo "q. to Exit"
     13 read -p "Enter your choice [ 1 - 3 ]" choice
     14 case $choice in
     15 1)
     16 echo "Server Date                       : `date`"
     17 echo "Server Uptime                     :`uptime`"
     18 read -p "Press [Enter] key to continue..."
     19 readEnterKey
     20 ;;
     21 2)
     22 echo "File system information"
     23 df -hP | column -t
     24 read -p "Press [Enter] key to continue..."
     25 readEnterKey
     26 ;;
     27 q)
     28 echo "Bye!"
     29 exit 0
     30 ;;
     31 *)
     32 echo "Error: Invalid option..."
     33 read -p "Press [Enter] key to continue..."
     34 readEnterKey
     35 ;;
     36 esac
     37 done
     38 #MYSCR
A2K
  • 3
  • I assume you haven't posted here the entire bash code. For instance, you don't have the definition of the readEnterKey routine, and also there isn't the exit condition of the while loop and it's closing. – aviro Jun 13 '23 at 11:39
  • Yes, I trimmed the whole script and kept a smaller version so it can be easy to troubleshoot, I have pasted the script again, When I comment line 1, 2,3, and heredoc closure line 38, I am able to run this script on local machine, I want to run this script on remote hosts to collect the similar info. read prompt should prompt the user for username and password and menu option should be executed on remote server. – A2K Jun 13 '23 at 12:17
  • heredocs are useful, but it needs to be remembered that they are a form of quoting, and nested quoting in bash becomes a PITA real fast. You can avoid the hassles of nested quoting by writing your heredoc as a standalone script, using scp to copy it to the remote host (perhaps to /tmp), and then using ssh to run it. This also avoids issues with stdin being redirected. – cas Jun 14 '23 at 03:01

2 Answers2

2

Several problems:

  • to read a line, it's IFS= read -r line. Your read -s -p "Enter the password : " passd in particular won't work properly for users whose password contains backslashes or start or end in characters of $IFS. So it should be IFS= read -rsp 'Enter the password: ' passd || exit

  • in bash, parameter expansions in list contexts must be quoted. "$user@$1", not $user@$1 for instance.

  • that -o StrictHostKeyChecking=no sounds a bit like please hack me. Unless the network can be fully trusted, authenticating the server to the client is almost as important as authenticating the client to the server. And that directive would be like saying "trust the user to be whoever they say they are without asking them for proof of identity" if it were done on the server side.

  • passwords and other secret information should never be passed in command arguments which are public information within a system. The sshpass man page has a big warning about that. Use SSHPASS="$passd" sshpass -e ssh..., or better still use better forms of authentication than password authentication.

  • It's good practice to unset variables before having them store secret data to make sure they're not exported to the environment (and leak to commands that might themselves leak them further afield; likely not the case here). In bash, that's with unset -v passd.

  • With sshpass ... ssh ... << 'HEREDOC', the here-document becomes the stdin of sshpass which passes it along to ssh which passes it along to the remote command. Here, you're not specifying which command to run, so it will be the login shell of the remote user, whichever it it.

    Since ssh's stdin is not a terminal device, it will request sshd not to create a pseudo-terminal on the remote side, even with -t which is just as well, so that shell will be started non-interactively which again is just as well as it would otherwise start echoing a prompt and do all the things an interactive shell does while here you want to to execute a script.

    So here, that shell will read the code to execute from its stdin.

    But that code also happens to read from its stdin. That read -p "Enter your choice [ 1 - 3 ]" choice, would read from that heredoc. Thankfully, that read won't be run until the code for the full while loop has been read by the shell, so it will only read what's after that in that here-doc, or if there's nothing after that, it will just read nothing and return failure upon eof.

Here, you need to run a bash shell (since you're using bash-specific syntax) on the remote host, not the user's login shell (who would use bash as their login shell? :-b), non-interactively and pass it the code some other way than via stdin. If the ssh server has some AcceptEnv LC_* in its configuration which is common, you could do:

code=$(
cat <<'EOF'
the code there
EOF
)

And then:

SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
  exec bash -c -- "$LC_CODE"'

Here assuming the login shell of $user on the remote host understands that syntax, that is where $var is used to refer to variables and double-quotes can be used for quoting (and which are necessary on Bourne-like ones to avoid the split+glob). If their login shell is rc or derivative or zsh or fish, exec bash -c -- $LC_CODE is enough (though the quotes would only be harmful for rc-like shells); if it's csh or tcsh: exec bash -c -- $LC_CODE:q.

Writing it:

SSHPASS="$passd" LC_CODE="$code" sshpass -e ssh -o SendEnv=LC_CODE -qt "$user@$1" '
  exec bash -c '\''eval -- "$LC_CODE"'\'

Would work if the user's login shell is any of the ones above, as the bash -c 'eval -- "$LC_CODE"' code is understood the same by all of Bourne-like, rc-like, csh-like and fish shells at least. Also has the advantage of not exposing that code in the output of ps -Awww on the remote host.

You can learn more about that at: How to execute an arbitrary simple command over ssh without knowing the login shell of the remote user?

SSHPASS="$passd" sshpass -e ssh -qt "$user@$1" " $code"

As in @aviro's answer (with the addition of that extra space before $code, needed for the cases where $code starts with - or +¹) would only work if the login shell of the user happened to be bash. Also note that it exposes the code in the output of ps -Awww on both the local and remote system.


¹ as in that case, it's sshd that does bash -c thatcode, and at least in current versions of openssh, it doesn't do bash -c -- thatcode (possibly because not all shells support or need that -- or more likely because the issue (unlikely to happen in practice) has been overlooked; that same issue for C's system() has been overlooked for many decades)

  • You're naming the code variable code instead of LC_CODE. – aviro Jun 13 '23 at 12:26
  • @aviro, thanks. Fixed now. Point was to create a $code unexported shell variable so we can more easily pass its contents as the value of the $LC_CODE environment variable for sshpass only, but I had forgotten to add that LC_CODE="$code". – Stéphane Chazelas Jun 13 '23 at 12:28
  • @StéphaneChazelas, Thank you very much for detailed information, I have make a note of the errors and try to improve my code, Script is working fine as expected, Thanks you once again – A2K Jun 13 '23 at 12:38
  • Could you please expand a bit (or direct me to some reference) about what happens when the ssh command starts with - or + ? I don't understand how it works. Do you mean, when you're running an executable whose name starts with - or +? – aviro Jun 14 '23 at 14:30
  • @aviro, yes, see bash -c ++c vs bash -c -- ++c. Or ssh localhost ++c. Or awk 'BEGIN{system("++c")}'. See also the footnote – Stéphane Chazelas Jun 14 '23 at 14:38
0

When you're using here-document, the text is written to the Standard Input (STDIN) of the process, in this case - to the STDIN of your ssh process. Since you're redirecting the here-document to the STDIN of your ssh, it cannot read your keyboard.

Instead of providing those commands in the STDIN of your ssh, assign it to a variable, and then use this variable as the command for ssh to run on the remote host.

# Read the here-document into the $COMMAND variable
read -r -d '' COMMAND << 'MYSCR'
while :
do
  clear
...
MYSCR

Run ssh with $COMMAND

sshpass -p $passd ssh -qt -o StrictHostKeyChecking=no -o ConnectTimeout=4 $user@$1 "$COMMAND"

aviro
  • 5,532
  • thanks for correct code, I have checked both the method and these are working fine – A2K Jun 13 '23 at 12:39