Automating SSH and Sudo with Expect
Let’s imagine a hypothetical scenario: you have a list of a hundred Linux servers and you need to log into each one of them and remove a local user “roger” and his home directory. Doing this by hand will get tedious and, chances are, you will make a few typos and there will be some collateral damage.
It would have been easy if you could just configure passwordless SSH for root, but your company’s compsec policies do not allow this (and rightly so). All of the servers on your list are part of the same NIS/LDAP domain and you have a standard user account. Each server has sudo configured and your NIS/LDAP account is on the sudoers list for root access, but it requires that you enter a password for all sudo commands. Many companies, as well as government and military organizations do not allow the NOPASSWD setting for sudo on their servers.
If you wish, you can configure passwordless SSH access for your non-privileged NIS/LDAP account, but you would still have to enter a password whenever you use sudo. The “expect” utility can provide a solution. Below we have an example “expect” script that will connect to a single host with a non-privileged account, enter a password, run sudo command, enter the password again, execute the “userdel” command as root, and log out. Pay attention to the syntax.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/usr/bin/expect set timeout 5 set user [lindex $argv 0] set host [lindex $argv 1] set pass [lindex $argv 2] spawn ssh -q ${user}@${host} expect "assword" send "$pass\r" expect "${user}@" send "sudo userdel -r roger\r" expect "assword" send "$pass\r" expect "${user}@" send "exit\r" interact |
Save this script as expect_userdel.exp, chmod 700, and run like so:
|
1 |
./expect_userdel.exp your_username remote_hostname your_password |
Entering your username and password from command line as arguments for the script is more secure (although still not ideal) than storing them directly inside the expect script.
To make this method work with a list of hosts, you can either write a wrapper script that would read the host list and launch the expect script, or you can incorporate the expect script into a shell script. Let’s take a look at the first option.
|
1 2 3 4 5 6 7 |
#!/bin/bash user=$1 pass=$2 cat server_list | while read host do ./expect_userdel.exp $user $host $pass done |
Save this script as wrapper_expect_userdel.sh and run like so:
|
1 |
./wrapper_expect_userdel.sh your_username your_password |
It is possible to insert expect commands directly into a shell script. This way you don’t need to create a wrapper script. The basic syntax for doing this is shown in the example below.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/bash user=$1 pass=$2 cat server_list | while read host do expect -c " set timeout 5 spawn spawn ssh -q ${user}@${host} expect "ssword:" { send \"${pass}\r\" } expect "${user}@" send "sudo userdel -r roger\r" expect "assword" send "${pass}\r" expect eof " done |
When working with these script examples, keep in mind that with different versions of expect, SSH, sudo and shells, there maybe small syntax variations. As long as you understood the general idea, you should have no problem adjusting these commands to suit your requirements.

Pingback: KrazyWorks » Using Expect with SSH and Su