Modern Time Technology
That was it... I was finally fed up with amount of time I was spending to SSH into each host manually. I was repeating the same procedure probably hundreds of times every day... Typing the ssh command and the host name, waiting for the initial prompt, entering my password, typing the sudo -i command to get root access, and entering my password again. Just as expected from a modern time worker. But wait, there was more to deal with...
Working with a lot of servers, I need to use my favorite terminal emulator to organize and manage the number of sessions. This means that every window has to be properly named and assigned to a specific session. So before logging into each server, I had to switch to the correct TMUX session (or create a new one), create a new TMUX window, name the window properly, and finally move on to the SSH stage.
I was aware of a script that was created by an ex-colleague. The script automated the process by storing the credentials in a key chain; however, it was written in Python and designed to run on a Mac. Not to mention, that long promised Kerberos implementation was just a mirage. I was more likely to see Unicorns running around in the office than a Kerberos solution. What I needed was a native Linux implementation written with BASH!
Before I begin, here are some details about my environment. I run the scripts on my Fedora 20 instance accessing primarily CentOS servers. The SSH hop script is dependent on components such as the KDE Wallet Manager and the expect tool. The TMUX hop script requires the TMUX tool as can be easily guessed from its name.
KDE Wallet Manager Configuration
The first step is to utilize the KDE Wallet Manager to store the SSH username and password in a secure manner. I simply created a new wallet named ssh and protected it with a GPG key. In addition, I created two entries named ssh_username and ssh_password under the Password folder.
SSH Login Automation
This awesome blog post about how to access the KWalletManager via the command line interface was the key for this implementation. The first time the ssh.hop script is called, KDE Wallet Manager responds with a prompt to decrypt the wallet and from then on you can use the script without any prompts (provided that you allow access not just once). Here is the script:
ssh.hop
#!/bin/bash if [ "$#" -eq 0 ] then echo "Usage: $0 <hostname> [<username>]" exit fi hostname=$1 namedPipe=/tmp/expect.$$ trap "rm -f $namedPipe; exit;" SIGHUP SIGINT SIGQUIT SIGTERM EXIT walletClient="ssh.hop" walletName="ssh" walletUsernameEntry="ssh_username" walletPasswordEntry="ssh_password" walletId=$(qdbus org.kde.kwalletd /modules/kwalletd org.kde.KWallet.open "$walletName" 0 "$walletClient") if [ -z "$walletId" ] then echo "Unable to get wallet id." exit fi if [ -z "$2" ] then username=$(qdbus org.kde.kwalletd /modules/kwalletd readPasswordList $walletId Passwords "$walletUsernameEntry" "$walletClient" | awk '{print $2}') if [ -z "$username" ] then echo "Unable to get username from wallet." exit fi else username=$2 fi password=$(qdbus org.kde.kwalletd /modules/kwalletd readPasswordList $walletId Passwords "$walletPasswordEntry" "$walletClient" | awk '{print $2}') if [ -z "$password" ] then echo "Unable to get password from wallet." exit fi export HISTIGNORE="expect*" mkfifo $namedPipe echo " spawn ssh -o StrictHostKeyChecking=no -t $hostname sudo -i expect { \"?assword:\" { send \"$password\r\" } timeout exit } expect { \"password for $username:\" { send \"$password\r\" } timeout exit } interact" > $namedPipe & expect -f $namedPipe rm -f $namedPipe
A couple of things to note here... The named pipe is required so that we can background the echo statement and exit. If we don't utilize a named pipe and use a regular echo "expect commands" | expect -f - call, expect fails to work properly since the first process is needed to be kept alive. Because, by default, interact expects the user to be writing stdin and reading stdout of the expect process itself. You can prevent the pipe from closing by running a command like (echo "expect commands"; cat) | expect -f - but that means your process list would be littered with echo statements containing clear text passwords. Long story short, to prevent that from happening, a named pipe was used to keep the process list clean.
TMUX Session and Window Setup Automation
As I mentioned before I also needed a way to manage my TMUX session. So I wrote the tmux.hop script to automate TMUX session and window setup and kick of an SSH login right away utilizing the ssh.hop script included above.
tmux.hop
#!/bin/bash if [ "$#" -eq 0 ] then echo "Usage: $0 <hostname> [<session>] [<username>]" exit fi h=$1 s=$2 u=$3 tmux=$TMUX # if a session name provided if [ ! -z "$s" ] then tmux has-session -t "$s" &>/dev/null if [ "$?" -ne 0 ] then TMUX="" tmux new-session -d -s "$s" fi tmux new-window -t "$s" -n "$h" "ssh.hop $h $u" if [ -z "$tmux" ] then tmux attach-session -d -t "$s" else tmux switch-client -t "$s" fi tmux select-window -t "$s:$h" # no session name provided else if [ -z "$tmux" ] then echo "No current tmux session." exit fi tmuxCurrentSession=$(tmux display-message -p '#S') tmux new-window -n "$h" "ssh.hop $h $u" tmux select-window -t "$tmuxCurrentSession:$h" fi
Conclusion
This solution seems to be kinda ugly and not practical; however, I am already saving a lot of cycles in my workday by using these scripts. So in my book, the technology works... :)