From 4514a164b8ec45d7f76bba7f5ce71c167c7fae89 Mon Sep 17 00:00:00 2001 From: Jan Krag Date: Fri, 23 Nov 2012 14:03:16 +0100 Subject: [PATCH] Imported from svn by Bitbucket --- sshsudo | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100755 sshsudo diff --git a/sshsudo b/sshsudo new file mode 100755 index 0000000..0025882 --- /dev/null +++ b/sshsudo @@ -0,0 +1,185 @@ +#!/bin/bash +# +# sshsudo: 1.0 +# Author: jiwu.liu@gmail.com +# +# The script executes sudo command on one or more remote computers. +# Its arguments are the names to remote sudoers and remote computers. +# The script read the password from the standard input, which should +# be the same for all sudoer accounts. No password is explicitly shown +# in the terminal or logged to files. +# If the command is a local shell script, use '-r' to copy it to +# the remote computers to execute. A temporary script file will be +# automatically created for this purpose. +# +# +# TO DO: It can not execute a command that use the shell special charaters +# on the remote computer, such as & ; ! *, because all arguments +# are wrapped with ''. That means, all special characters have only +# literal meanings. In the case when they are needed, use a script +# instead of a command. +# +# Example:sshsudo foo@bar.net apt-get check +# sshsudo -r foo@bar.net myscript.sh + + +usage() +{ + local ProgName=$1; + echo "Usage: $ProgName -r -v [-u user] AccountList Command [Arguments]" + echo " -u Set the default user unless it is given within remote account list" + echo " -r Copy the command(usually the path to local script) to remote computers before execution" + echo " -v Verbose output" + echo " AccountList: [user1@]computer1,[user2@]computer2,[user3@]computer3,..." + echo " or the name to file which contains accounts(user@computer) in separate lines" + echo " Command: The Command/Script to be executed" + echo " Arguments: All arguments to be passed to command." +} + +pipewrap() +{ + # This function reads in the input from a terminal and output the same. + # The only purpose is to insert PASSWORD for "sudo -S" command. + # After initially insert the password, it simply copy input from terminal + # and send it to ssh command directly + echo $1 # which is password + local lockFile=$2; + while true; do + # The function will exit when output pipe is closed, + if [ ! -e $lockFile ]; then + return 0 + fi + # i.e., the ssh + read -t 1 line + if [ $? -eq 0 ]; then + # successfully read + echo $line + fi + done +} + +runsudo () +{ + # receive its arguments + local ACCOUNT=$1; + local PASSWORD=$2; + local COMMAND=$3; + local ARGUMENTS=$4; + local DEFAULTUSER=$5; + local COPY=$6; + local VERBOSE=$7; + + # Parse account which has the form user@computer.domain + local REMOTEUSER=${ACCOUNT%%@*} + local REMOTECOMPUTER=${ACCOUNT#*@} + local REMOTESCRIPT= + local REMOTECOMMAND=$COMMAND + local SSHARGS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" + if [ $REMOTEUSER = $REMOTECOMPUTER ]; then + # There is no @, i.e., only computer name is given + REMOTEUSER=$DEFAULTUSER + fi + echo =========================${REMOTEUSER}@$REMOTECOMPUTER: sudo $COMMAND $ARGUMENTS + if [ $COPY -ne 0 ]; then + # Make a script + seconds since 1970-01-01 + REMOTESCRIPT=/tmp/`basename $COMMAND``date +%s` + if [ $VERBOSE -ne 0 ]; then + echo $REMOTECOMPUTER: Secure copy \"$COMMAND\" to \"$REMOTESCRIPT\" + fi + # Copy to remote computer. Quote target name on the remote computer for script name + # that contains " " + sshpass -p "$PASSWORD" scp "$SSHARGS" "$COMMAND" "$REMOTEUSER@$REMOTECOMPUTER:'$REMOTESCRIPT'" + REMOTECOMMAND=$REMOTESCRIPT + fi + + # Assume we are lucky, result is fine. + local RES=DONE + # Invalidate the sudo timestamp. Simplify the situation. Henceforth sudo always ask for a password + sshpass -p "$PASSWORD" ssh "$SSHARGS" "$REMOTEUSER@$REMOTECOMPUTER" "sudo -K" + + # Use lock file to inform pipewrap function to exit. + local lockFile=`mktemp` + eval pipewrap '$PASSWORD' '$lockFile' | (sshpass -p "$PASSWORD" ssh "$SSHARGS" "$REMOTEUSER@$REMOTECOMPUTER" "sudo -S '$REMOTECOMMAND' $ARGUMENTS"; rm "$lockFile") + + if [ $? -ne 0 ]; then + RES=FAILED + fi + if [ $COPY -ne 0 ]; then + sshpass -p $PASSWORD ssh "$SSHARGS" "$REMOTEUSER@$REMOTECOMPUTER" rm \'$REMOTESCRIPT\' + if [ $VERBOSE -ne 0 ]; then + echo Remove temporary script \"$REMOTESCRIPT\" at [$REMOTECOMPUTER] + fi + fi + echo ----------------------------------${RES}!!---------------------------------------- +} + + +#=====================================Main Script======================================== +# +# Set default values for variables +COMMAND= +ARGUMENTS= +COPY=0 +VERBOSE=0 +ACCOUNTLIST= +DEFAULTUSER=$USER + +# Parse the command line arguments with '-' +while getopts u:hrv o ; do + case "$o" in + [?]) usage $0 + exit 1;; + h) usage $0 + exit 0;; + u) DEFAULTUSER=$OPTARG;; + v) VERBOSE=1;; + r) COPY=1;; + esac +done + +# Read the rest command line arguments +if [ $(($#-$OPTIND+1)) -lt 2 ]; then + echo Error: Account list and command have to be in the arguments + usage $0 + exit 4 +fi + +shift $(($OPTIND-1)) +ACCOUNTLIST=$1 +shift +COMMAND=$1 +shift +for PARAM; do + ARGUMENTS=$ARGUMENTS\'$PARAM\'" " +done + +# Check the validity of the script command if remote copy is needed +if [ $COPY -ne 0 ]; then + if [ ! -e "$COMMAND" ]; then + echo \"$COMMAND\" does not exist. + exit 3 + fi + if [ ! -x "$COMMAND" ]; then + echo \"$COMMAND\" can not be executed. + exit 3 + fi +fi + +# Read in the password from the STDIN +read -s -p "Please enter your password:" PASSWORD +echo + +# Start running +if [ -e $ACCOUNTLIST ]; then # read accounts from a file + for ACCOUNT in `cat $ACCOUNTLIST`; do + runsudo "$ACCOUNT" "$PASSWORD" "$COMMAND" "$ARGUMENTS" "$DEFAULTUSER" "$COPY" "$VERBOSE"; + done + +else # ACCOUNTLIST is a comma separated list of user@computer strings. + # Change the internal separation field. + IFS=, + for ACCOUNT in $ACCOUNTLIST; do + runsudo "$ACCOUNT" "$PASSWORD" "$COMMAND" "$ARGUMENTS" "$DEFAULTUSER" "$COPY" "$VERBOSE"; + done +fi +#EOF