#!/bin/sh # Limit path for good security practice OLDPATH=$PATH export PATH=/bin:/usr/bin # Name me! PROGRAM=sshdo VERSION=3.4 # Some put locations may be dangerous. Especially when doing so automatically # and multiple times. Here we try to filter out bad ones. BP="dev/ proc/ sshd.conf etc/shadow etc/gshadow etc/passwd etc/group" # Some commands may be dangerous. Especially when doing so automatically # and multiple times. Here we try to filter out bad ones. BC="rm mkfs mv dd shred gshred wipe bcwipe scrub" # Output man page to this default unless specified later. mcmd=cat usage(){ cat 1>&2 < ] [ -g ] Each get is suffixed by '-hostname'. [ -p ] [ -s ] save (redirect) output of remote command of each host to local separate file 'hostname-suffix'. [ "remote command" ] WARNING: Be sure to validate your command before issuing it. One mistake dupicated across multiple hosts could be catatrophic. Backups are also recommended. EOF } man() { cat <. EOF exit } version(){ printf "$PROGRAM version $VERSION" } killagent(){ eval "$(ssh-agent -k)" 2>&1 1>/dev/null } error(){ # Errors are sent to stderr # This syslog action need only be used on hosts that have a running syslog #logger -s -t $PROGRAM "$@, $STATUS $ERROR" printf "Error: $@\n" 1>&2 usage exit 1 } validate(){ name=$1 shift value=$@ if [[ -z $value ]] then error "Missing argument $name" elif [[ $name = "To" ]] && [[ "0$put" -eq 1 ]] && [[ "0$force" -ne 1 ]] then for i in $BP do printf "$value" | grep "$i" > /dev/null if [[ $? -eq 0 ]] then error "Potential dangerous 'put' to $value" fi done elif [[ $name = "Remote command" ]] && [[ "0$force" -ne 1 ]] then for i in $BC do printf "$value" | grep "^$i " > /dev/null if [[ $? -eq 0 ]] then error "Potential dangerous remote command: $value" fi done fi } # get opts while test $# -gt 0 do case $1 in # For each option #-o) #shift #option="$1" #;; # Add custom options ABOVE the defaults below. --recurse | --recurs | --recur | --recu | --rec | --rc | --r|\ -recurse | -recurs | -recur | -recu | -rec | -rc | -r) recurse="-r" ;; --force | --forc | --for | --fo | --f |\ -force | -forc | -for | -fo | -f ) force=1 ;; --man | --ma | --m |\ -man | -ma | -m ) man=1 # Read man command mcmd="eval nroff -c -man | col | less" ;; --mman | --mma | --mm |\ -mman | -mma | -mm ) mman=1 ;; --get | --ge | --g | -get | -ge | -g) get=1 shift from="$1" shift to="$1" ;; --put | --pu | --p | -put | -pu | -p) put=1 shift from="$1" shift to="$1" ;; --user | --use | --us |--u |\ -user | -use | -us |-u ) shift u="$1@" ;; --save | --sav | --sa | --s |\ -save | -sav | -sa | -s ) shift s="$1" ;; --version | --versio | --versi | --vers | --ver | --ve | --v |\ -version | -versio | -versi | -vers | -ver | -ve | -v) version exit 0 ;; --help | --hel | --he | --h | --\? |\ -help | -hel | -he | -h | -\?) usage exit 0 ;; -*) error "Unrecognized option: $1" ;; *) break ;; esac shift done # Left over arguments are the remote command rcmd="$@" # Validate arguments if [ "0$man" -eq 1 ] && [ "0$mman" -eq 1 ] then error "-m and --m are mutually exclusive" elif [ "0$man" -eq 1 ] || [ "0$mman" -eq 1 ] then man fi if [ "0$get" -eq 1 ] || [ "0$put" -eq 1 ] then validate "From" $from validate "To" $to else validate "Remote command" $rcmd echo "$rcmd" > /tmp/$$sshdo rcmd=/tmp/$$sshdo fi # get hosts from stdin while read host do case $host in \#*) # Skip lines that begin with a comment '#' ;; *) # remove lines with trailing comments host=$(printf "$host" | sed -e s/#.*$//g) HOSTS="$HOSTS $host" ;; esac done # Agent broken in Cygwin. # Use ssh-agent for pass-phrase storage if [[ -f $(which ssh-agent) ]] then # This prevents ask_passpw from being invoked. OLDDISPLAY=$DISPLAY unset DISPLAY # Kill ssh-agent on any exit trap "killagent; rm -f $rcmd; exit" EXIT # Load private key into ssh-agent but, only for 5 minutes eval "$(ssh-agent -t 300)" 2>&1 1>/dev/null # Add private keys to ssh-agent ssh-add # Reset DISPLAY export DISPLAY=$OLDDISPLAY fi # Main matter. Perform tasks for each host in list for h in $HOSTS do # Set env variable for hostname. This is useful for gather the same # information but saving it by hostname. export H=${h} # Remote command if from and to are empty if [ -z "$from" ] && [ -z "$to" ] then if [ -z "$s" ] then eval ssh -t ${u}$h < $rcmd else eval ssh -t ${u}$h > ${h}-$s < $rcmd fi # Else we want to copy else if [ "0$get" -eq 1 ] then eval scp $recurse ${u}${h}:${from} ${to}-${h} elif [ "0$put" -eq 1 ] then eval scp $recurse $from ${u}${h}:${to} fi fi done # clean up rm -f $rcmd exit 0