SHELL實現跳板機

SHELL實現跳板機,只允許用戶執行少量允許的命令

 

注意:請謹慎使用,到現在爲止,使用了,我還沒找到改回去的方法。

1.     問題

第一、很多大公司的服務器都不允許直接登錄,而是通過一個跳板機才能登錄過去。在跳板機中,通常只能執行幾個少數命令(如SSH),而其他命令是不允許執行的,那麼怎樣才能實現這個功能呢?

 

第二、一些小公司,由於服務器比較少,不需要什麼跳板機之類的說法,公司的開發運維人員加起來也就那麼十幾二十人,通常大家都知道root密碼,所有人都是直接root登錄上去,但是有時有人由於失誤,把什麼服務弄掛了,這時是肯定抓不到人的,因爲所有人都知道密碼,那麼怎樣才能知道誰都執行過什麼命令呢?或者乾脆只讓他們執行少數允許的命令呢?

 

2.     解答

這兩個需求,看似不同,其實原理都相同,只要在用戶與shell之間加入個檢測機制就可以了,您可以去網上找些開源軟件,但是如果您嫌那些軟件太複雜,又不能完全滿足您的需求,那麼就完全可以像我這樣,自己用shell寫一個簡易的模擬終端。

 

在模擬終端裏,你需要做的,就是向用戶提供一個看起來像是真的但實際不是真的、的虛擬終端。而你需要做的,就是讀取用戶輸入的命令,並在替用戶執行之前進行記錄,這樣就實現了上面第二個需求中的記錄功能。如果你不想讓用戶執行哪些命令,那麼僅僅echo一句“Permission denied”就可以將用戶阻擋在真正的shell大門之外了。

 

如果用戶要執行的命令是你允許的,那麼在執行之前,記錄個log就行了。如果將這個虛擬終端設置成只能執行ssh這個命令,那麼不就實現了第一個問題中的跳板機的功能了嗎。

 

所以以上兩個需求,都可以總結爲一個需求,那就是:攔截用戶的輸入,在用戶與shell之間加一道門,而腳本的功能,就是根據用戶不同的輸入採取不同的動作,腳本的長短也就跟需要的命令的多少有關,需要的命令越多,腳本需要判斷的就越多,腳本也就越長,但實際並不需要爲每個命令都寫一個action,只要爲每個種類的寫一個action就行了,比如:最簡單的情形,沒有任何參數的命令,如:pwd等,稍複雜些的,如touch、ls等命令,這時需要處理參數,再複雜些的,如rm、mv等命令,不只需要處理參數,還要避免用戶誤刪數據,而以上所有命令,都需要判斷用戶是否有權限等等,腳本的複雜程度,完全取決於你的需求了,下面的虛擬終端內,實現了用戶可以執行cd ls rm mv download upload pwd passwd等等幾個命令,如果您只實現ssh命令,那就是跳板機了。

 

3.     實現腳本

腳本主要技術:

1.echo 顯色

2.trap處理信號

3.stty,dd關閉回顯並讀取一個字符

4.讀取密碼

5.<<EOF行內輸入

6.命令使用日誌記錄

7.將用戶固定在某目錄下

8.執行固定的命令

 

 

# ~/virtual terminal

 

 

# 將此文件放在/etc/profile.d/目錄下,並添加可執行權限。

# 如: -rwxr-xr-x 1 root root 7340 Oct 23 18:12 /etc/profile.d/vt.sh

 

 

############################################################################

# 本行直至文件末尾的代碼,用於實現用戶登錄使用的虛擬終端

# 請勿刪除或更改

# 如有疑問、請聯繫維護負責人: xiaoxi227(QQ451914397)

############################################################################

 

# 該文件存放用戶名和密碼 文件格式爲==> 用戶名:密碼 (每行一個用戶)

passwd_file=/etc/user.password

 

function red()

{

     # 以紅色顯示

     echo -e "\033[31;40m$*\033[0m\n"

}

 

 

function green()

{

     # 以綠色顯示

     echo -e "\033[32;40m$*\033[0m\n"

}

 

# 忽略所有信號,以免用戶使用Ctrl+C或者Ctrl+Z等操作退出Virtual Terminal(VT)

for signal in `seq 1 64`

do

     trap : $signal &> /dev/null

done

 

clear

 

function getchar() # 關閉回顯,用於輸入密碼

{

     stty cbreak -echo

     dd if=/dev/tty bs=1 count=1 2>/dev/null

     stty -cbreak echo

}

 

###############################################################################

green "登錄成功。" # 用戶通過unix pam認證以後顯示“登陸成功”,隨後進行身份驗證

while :

do

     read -p "請輸入用戶名:" username

     echo -n "請輸入密碼: "

 

     while :

     do

         ret=$(getchar)

         if [ x"$ret" = x"" ];then

              echo

              break

         fi

         passwd="$passwd$ret"

         echo -n '*'

     done

     correct_passwd=$(gawk -F: "/$username/{ print \$2 }" $passwd_file)

 

     if [ -z "$username" -o -z "$passwd" ];then

         clear

         red "用戶名或密碼不能爲空"

         continue

     fi

     input_password=$(echo $passwd | md5sum | gawk '{ print $1 }')

     if [ x"$input_password" != x"$correct_passwd" ];then

         clear

         red "用戶名或密碼錯誤,請重新輸入"

     else

         break

     fi

done

 

title="

##################################################################################

############################## Virtual Terminal ##################################

##################################################################################

 "

cat | more <<EOF

$title

 

使用說明:

 

歡迎使用虛擬終端(Virtual Terminal,以下簡稱VT).

在此模式下,用戶只能執行少量被允許執行的命令,其他命令將會被拒絕執行。

本模式支持的命令及功能如下:

 

cd <DIR> 進入<DIR>目錄,前提是用戶有進入該目錄的權限。

ls       功能和系統中的ls命令相同,但是隻能顯示有讀取權限的目錄下的文件。

         本命令支持參數,系統中ls命令可以使用的選項都可以在VT模式下使用。

mv <SRC> <DST>     將文件<SRC>移至<DST>,如果路徑相同則含義爲改名。

         用戶必須同時對<SRC>和<DST>有讀寫權限。

rm <FILE>     將<FILE>從系統中刪除。

         當<FILE>是目錄時,會遞歸刪除所有子目錄及子目錄下的文件。

pwd      顯示當前的工作路徑。

download <FILELIST>

         將<FILELIST>下載至本機

         運行此命令時,用戶使用的終端必須支持ZModem協議,如SecureCRT等。

         下載多個文件需將各文件用空格隔開。

         如果下載的文件是目錄,系統會自動將其打包後再下載。

upload        向系統中上傳文件,運行此命令後,終端會彈出一個對話框,按提示選擇文件上傳即可。

         注意:運行此命令時,用戶使用的終端必須支持ZModem協議,如SecureCRT等。

passwd        修改自己的登錄口令

 

如需其他功能,請與管理員聯繫,VT維護負責人:xiaoxi227(QQ:451914397)

$title

 

EOF

 

echo -e "當前登錄用戶是:\033[32;40m$username\033[0m"

 

logfile=/var/log/vt.log

 

function run()     # 本函數用於替用戶執行命令,並記錄log

{

     local command="$@"

     {

         echo -n -e "\033[32;40m$username\033[0m\033[7G -- "

         echo -n -e "\033[31;40m`date '+%Y-%m-%d %H:%M:%S'` -- \033[0m$command"

         echo

     } | sed 's/sz/download/g;s/rz/upload/g' >> $logfile

     $command

}

 

 

# 通常開發人員需要部署的程序都在同一目錄下,所以爲其指定根目錄,限定僅在該目錄有權限。

ROOT_DIR=/usr # 本例中用/usr目錄測試

 

cd $ROOT_DIR  # 進入用戶的“根目錄”

 

 

while :  # 死循環讀取用戶輸入

do

     # 這裏的主機名寫成“localhost”寫死了,您也可以寫成$HOSTNAME,這樣就會顯示主機名了。

     read -e -p "[${username}localhost ${PWD##*/}]$ " REPLY

     # 如上語句模擬產生shell提示符(其實是假的)

 

     # 分隔命令和參數

     COMMAND=$(echo $REPLY | gawk '{ print $1 }')

     OPTION=$(echo $REPLY | sed -r "s/^$COMMAND//g;s/^[[:space:]]+//g")

 

     # 判斷要執行的命令是什麼,根據不同命令設置不同的動作。如下是本例中實現的幾個命令:

     case $COMMAND in

     cd|ls)

         # 當執行cd命令且參數爲空時,默認cd到根目錄,行爲要與系統中的cd命令保持一致。

         if [ x"$OPTION" = x"" -a x"$COMMAND" = x"cd" ];then

              cd $ROOT_DIR

              continue

         fi

         # 判斷參數的第一個字符是不是"/",如果不是,說明想要cd或ls的是相對路徑。

         # 也就是當前目錄下的文件活目錄,此時用戶是有權限的。

         first_char=${OPTION:0:1}

         if [ x"$first_char" != x"/" ];then

              run $REPLY

         else

         # 如果是絕對路徑,那麼判斷路徑是否以$ROOT_DIR開頭,若否則提示用戶沒權限。

              echo $OPTION | grep -q ^$ROOT_DIR

              if [ $? -eq 0 ];then

                   run $REPLY

              else

                   echo "$COMMAND: Permission denied." >&2

              fi

         fi

         ;;

     # 其他支持的參數:rm、mv、pwd、passwd等等,就不寫註釋了,自己理解下吧。

     rm|mv)  

         if [ x"$COMMAND" = x"rm" ];then RM='-rf';else RM='';fi

         run='ok'

         for _file in $OPTION

         do

              echo $_file | grep -q '-'

              [ $? -eq 0 ] && {

                   red "本命令在VT模式下不支持參數";

                   continue;

              }

 

              echo $_file | grep -q ^$ROOT_DIR

              ret=$?

              first_char=${_file:0:1}

              if [ x"$first_char" = x"/" ];then

                   if [ $ret -ne 0 ];then

                       run='not ok'

                   fi

              fi

         done

 

         if [ x"$run" = x"ok" ];then

              run $COMMAND $RM $OPTION

         else

              echo "$COMMAND: Permission denied." >&2

         fi

         ;;

     pwd)

         run $REPLY

         ;;

     upload)

         run rz

         ;;

     download)

         for _file in $OPTION

         do

              if [ ! -d $_file ];then

                   echo "開始下載文件:$_file"

                   run sz $_file

                   [ $? -eq 0 ] && green "下載完成" || red "下載失敗"

              else

                   echo "${_file}是目錄,正在將其打包成${_file}.tar"

                   tar -cf ${_file}.tar $_file

                   [ $? -eq 0 ] && green "打包完成,開始下載${_file}.tar" \

                   || { red "打包失敗,無法下載";continue; }

                   run sz ${_file}.tar

                   [ $? -eq 0 ] && {

                       green "下載完成"

                       echo "正在刪除打包文件"

                       rm -rf ${_file}.tar

                       [ $? -eq 0 ] && green "刪除成功" \

                            || red "刪除失敗"

                   } || red "下載失敗"

              fi

         done

         ;;

     passwd)

         read -e -s -p "請輸入舊密碼:" old_passwd

         echo '******'

         old_passwd=$(echo $old_passwd | md5sum | gawk '{ print $1 }')

         if [ x"$old_passwd" != x"$correct_passwd" ];then

              red "密碼輸入錯誤"

              continue

         else

              read -e -s -p "請輸入新密碼:" new_passwd1

              echo '******'

              read -e -s -p "請重複新密碼:" new_passwd2

              echo '******'

              if [ x"$new_passwd1" != x"$new_passwd2" ];then

                   red "兩次輸入的密碼不一致"

                   continue

              else

                   new_passwd=$(echo $new_passwd1 | md5sum | gawk '{ print $1 }')

                   sed -i "/$username/s/.*/$username:$new_passwd/g" $passwd_file

              fi

         fi

         ;;

     "")

         continue

         ;;

     *)

         echo "-bash: $COMMAND: command not found" >&2

         ;;

     esac

 

done


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章