Score:1

Special character password variable from bash to expect block is not working

az flag
Ali

I have been struggling with this issue for a few days now. I have scoured the internet for a solution and tried many different methods but none seems to be fruitful so far. I am passing a password variable which contains special characters to expect block and it always error out at the password.

Here's my script:

#!/usr/bin/bash
server_List=("MySQLServer1" "MySQLServer2")
osUser='mysql'
osUserPwd='H3htg#41fRth!'
newRootPass='prnH4xRJSMwbWn9ht!M5t'
oldRootPwd='prnH4x^JSMwbWn9h!M6J'
    for host in ${server_List[@]}
      do
        echo "Processing host $host"
        /usr/bin/expect -c '
        spawn ssh -o "StrictHostKeyChecking no" '$osUser'@'$host';
        expect "password: "
        send "'$osUserPwd'\r"
        expect "$ "
        send -- "mysqladmin --user root -p password '$newRootPass'"
        expect "Enter password: "
        send -- "'$oldRootPwd'\r"
        expect "$ "
        send "exit\r" '
    done

Here's the Error:

[mysql@centralserver myself]$ ./test2.sh
Processing host MySQLServer1
spawn ssh -o StrictHostKeyChecking no mysql@MySQLServer1
Password:
Last login: Tue Jan 24 10:00:43 2023 from 10.14.20.18
[mysql@MySQLServer1 ~]$ mysqladmin --user root -p password prnH4xRJSMwbWn9ht!M5tprnH4x^JSMwbWn9h!M6J
-bash: !M5tprnH4x: event not found

I would appreciate if someone can point me where I am making the mistake.

FedKad avatar
cn flag
Try to use _here documents_ for the `expect` command. Replace the `'` with `<<EOT` in the `expect` command, remove `'` from the end of the last `send` command, and add a line that starts with `EOT` in the first column after that last `send` command.
Score:1
cn flag

All your shell variables are unquoted in the shell. That allows the shell to perform various substitutions

Instead of

send "'$osUserPwd'\r"

you have to do

send "'"$osUserPwd"'\r"
# .....^..........^

Alternately, instead of composing the expect code as a string piece-by-piece, pass the shell variables though the environment:

#!/usr/bin/bash
server_List=("MySQLServer1" "MySQLServer2")
osUser='mysql'
osUserPwd='***'
newRootPass='***'
oldRootPwd='***'
export osUser osUserPwd newRootPass oldRootPwd host

for host in "${server_List[@]}"     # <= quoted
do
    echo "Processing host $host"
    /usr/bin/expect << 'END_EXPECT'   # a quoted heredoc
        spawn ssh -o "StrictHostKeyChecking no" $env(osUser)@$env(host)
        expect "password: "
        send "$env(osUserPwd)\r"
        expect "$ "

        # Need to send literal quotes for the password on the mysqladmin
        # command below: this prevents the remote shell from altering
        # the password on the command line.
        # Don't forget \r to hit enter.
        send -- "mysqladmin --user root -p password '$env(newRootPass)'\r"
        expect "Enter password: "
        send -- "$env(oldRootPwd)\r"
        expect "$ "
        send "exit\r"
        expect eof     # <= gracefully wait for the ssh connection to end
END_EXPECT
done
Ali avatar
az flag
Ali
thank you very much for the detailed explanation and the answer, I tried your suggestion but still getting the same error: [mysql@MySQLServer1 ~]$ mysqladmin --user root -p password "prnH4xRJSMwbWn9ht!M5t" -bash: !M5t: event not found As you can see the password is now being passed quoted properly but bash is erroring out.
hr flag
Having eliminated the outer single quotes (by using a here-document to read the expect script from stdin rather than a `-c ' ... '` argument string), you can single-quote the password(s) I think? i.e. `'$env(newRootPass)'` in place of `\"$env(newRootPass)\"`. That should prevent history expansion in the remote shell.
cn flag
Good call! @Ali on the "send mysqladmin" line, note the single quotes.
Ali avatar
az flag
Ali
Thank you everyone, it works now.
Ali avatar
az flag
Ali
@glennjackman Any suggestion on how I can hide the passwords in the above script? When I go to the target servers' history where the root passwords was updated it shows the password string on all of them which defeats the purpose of rotating password :).
cn flag
You can't. The script is a text file that other people can (probably) read. The typical advice is "never store passwords in a plaintext file". You can _obscure_ the password, but the script will need to **unobscure** them, and people can read all about exactly how you do that. You might want to investigate using a password manager and use that tool's API to store and retrieve passwords more securely. Even that approach won't be able to be completely automated because you'll need to enter a master password.
Ali avatar
az flag
Ali
@glennjackman I meant to say when the password is provided in the foreach loop when it spawns the ssh and then change password on each mysql server. Is there a way I can hide the password at that moment from being displayed in the history? e.g. read password -s
Ali avatar
az flag
Ali
I was able to do this by passing -s flag in my update password command: send -- "mysqladmin --user root -p password -s '$env(newRootPass)'\r"
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.