跳板机用途只做ssh中转,为严格控制跳板机数据进出、命令管理等,特对跳板机入口操作进行限制管理。该脚本包含两种界面风格,详见下面说明 。

功能说明:

* 屏蔽用户对跳板机系统进行任何未授权操作操作
* 查询用户已授权主机,具备权限用户方可连接后台服务器
* 授权命令集合(密钥生成、上传、copy,密码更改等),可根据实际需求自定义
* 此脚本依赖于LDAP用户管理,如无LDAP服务,可自行定义用户和主机组对应关系即可

界面一:用户授权登陆主机数量不超过20台

  • 通过选择左侧列表序号登陆右侧主机
  • 选择可执行命令序号,进入执行命令界面,命令作用,后面说明

shell脚本实现企业级简易跳板机案例

界面二:用户授权登陆主机数量超过20台

  • 选择IP地址登陆序号,进入输入IP地址界面
  • 选择可执行命令序号,进入执行命令界面,命令作用,后面说明

shell脚本实现企业级简易跳板机案例

命令作用说明

  • ssh-keygen : 生成本机ssh公钥
  • ssh-copy-id : 将跳板机公钥复制到远程服务器上
  • upload-local-key : 把本地公钥上传到跳板机上,实现跳板机免密连接
  • passwd : 更改密码
  • exit : 第一层为关闭当前会话,第二层为返回上一页
    备注:除“upload-local-key”为自定义命令外,其它参考命令本身用法

完整代码

#!/bin/bash
# Version:            v1.5
# Function:           用于跳板机入口控制
# Author:             Sly Chen
# Create Date:  Jun.13 2018

# 将脚本放在/etc/profile.d/目录下,并添加可执行权限
#set -x
# 导入系统函数
export LANG=en_US.UTF-8
. /etc/init.d/functions
SHELL_NAME=$(basename $BASH_SOURCE)
SHELL_NAME_PREFIX=$(echo $SHELL_NAME | sed 's/.sh$//')
SHELL_LOG="/tmp/${SHELL_NAME_PREFIX}.log"

# 以红色显示
red() {
    echo -e "\033[31;40m$*\033[0m"
}

# 以绿色显示
green() {
    echo -e "\033[32;40m$*\033[0m"
}

# Write Log
shell_log () {
    LOG_INFO=$1
    echo "$(date "+%Y-%m-%d") $(date "+%H:%M:%S") ${LOG_INFO}" >> ${SHELL_LOG}
}

login_banner (){
action "${LOGIN_USER} login success" /bin/true
cat << _EOF_
#######################################################################################
#        Welcome Use Jumpserver To Login                                                                                                         #
#        Please contact the system administrator or Send mail to Sly@test.com                                                 #
#######################################################################################
_EOF_
}

get_hosts_info () {
    LOGIN_USER=`whoami`
    [[ "$LOGIN_USER" == "root" ]] && continue
    # 屏蔽Ctrl+C
    trap ':' INT
    clear

    # 定义不同用户连接跳板机展现不同旗标
    login_banner

    # 进入ldap api封装目录,用作用户登陆主机查询
    # ldap api依赖于openldap-devel python-devel ; GET命令依赖于perl-libwww-perl
    # yum -y install openldap-devel python-devel perl-libwww-perl
    # Installing python-ldap: https://www.python-ldap.org/en/latest/installing.html#installing-from-source
    cd /data/script/ldap
    # 格式化主机名,并删除任意及禁止登陆主机权限
    array_objects=($(python ldapadmin.py user hostinfo ${LOGIN_USER} | sed "s/\[\|,\|\]\|'//g; /*\|\!/d"))
    # 定义命令数组
    array_commands=(
    "ssh-keygen         --> authentication key generation, management and conversion"
    "ssh-copy-id        --> use locally available keys to authorise logins on a remote machine"
    "upload-local-key   --> upload the public key to the jumpserver"
    "passwd             --> update user's authentication tokens"
    "exit               --> return"
     )
    CMD_LIST_NAME="$(green "Execute Command") On Jumpserver."
    CLOSE_BASH="$(green "Exit Shell")"
    if [ -z "${#array_objects[@]}" ]; then
        red "No action object was found!"
        continue
    # 如果可登陆主机数超过20条,则通过自行指定IP地址登陆
    elif [ ${#array_objects[@]} -gt 20 ]; then
            IP_CUSTOM_NAME="Type $(green 'IP ADDRESS') To Login."
            array_objects=("${IP_CUSTOM_NAME}" "${CMD_LIST_NAME}")
    else
        num_host=${#array_objects[@]}
        array_objects[((num_host+1))]="${CMD_LIST_NAME}"
    fi
}

network_pint_test () {
    ping -c2 $LAN_IP &> /dev/null
    [ $? -ne 0 ] && echo "$(red "$LAN_IP"): Network is unreachable" && continue
    ssh ${LAN_IP}
}

invalid_ldap_host_del () {
    # 本函数用作LDAP无效主机条目删除
    # 如果获取内网IP为空,先判断运维平台API网络状态
    ping -c 2 ${BRIDGE_URL} &> /dev/null
    [ $? -ne 0 ] && echo "$(red "$BRIDGE_URL"): Network is unreachable" && continue
    # 删除LDAP无效主机记录
    LDAP_HOST_DEL=$(python ldapadmin.py user hostdel ${LOGIN_USER} ${OBJECT} | egrep -io 'sucessfully' | tr 'A-Z' 'a-z')
    # 记录删除主机日志
    if [ "${LDAP_HOST_DEL}" == "sucessfully" ]; then
        shell_log "${LOGIN_USER} LDAP delete $(green "$OBJECT") sucessfully"
    else
        shell_log "${LOGIN_USER} LDAP delete $(red "$OBJECT") failure "
    fi
}

main () {
    get_hosts_info

    # 获取IP数组生成菜单列表
    PS3=$(green "\nAccept Only List Numbers: ")
    # 必须加while死循环,不然无法屏蔽ctrl+d等操作
    BRIDGE_URL='bridge.test.com'
    while true; do
        select VAR in "${array_objects[@]}" "${CLOSE_BASH}"; do
            [ -z "$VAR" ] && red "Invalid number" && continue
            if [[ "$VAR" == ${IP_CUSTOM_NAME} ]]; then
                 read -p "Type $(green 'IP ADDRESS') To Login: " LAN_IP
                 network_pint_test
            elif [[ "$VAR" == "${CLOSE_BASH}" ]]; then
                exit
            elif [ "$VAR" != "${CMD_LIST_NAME}" ]; then
                # 判断操作对象,IP加执行SSH前缀执行,命令直接执行
                OBJECT=$(echo "${array_objects[@]}" | egrep -wo "${VAR}" | awk '{print $1}')
                # 操作对象包含数字,选择序号对应命令就等于 ssh + ip
                if [[ "$OBJECT" =~ .test.com ]]; then
                    [ x"$(which GET &> /dev/null; echo $?)" != x"0" ] &&
                    {
                        # GET依赖于perl-libwww-perl包
                        echo "-bash: $(red "GET"): command not found"
                        exit
                    }
                    LAN_IP=$(GET http://${BRIDGE_URL}/api/vm?hostname=${OBJECT} | grep -oP '"lan_ip[" :]+\K[^"]+')
                    # IP为空表示主机不存在,LDAP主机条目无效
                    if [ -z "$LAN_IP" ]; then
                        LAN_IP=$(GET http://${BRIDGE_URL}/api/phyhost?hostname=${OBJECT} | grep -oP '"lan_ip[" :]+\K[^"]+')
                        if [ -z "$LAN_IP" ]; then
                            # LDAP无效主机条目权限删除
                            invalid_ldap_host_del
                            echo "$(red "$OBJECT") not found"
                            continue
                        elif [ $(echo $LAN_IP | xargs -n 1 | wc -l) -gt 1 ]; then
                            echo "$(red "${OBJECT}") Contains multiple IP: $(echo ${LAN_IP} | xargs -n 10)"
                            continue
                        else
                            network_pint_test
                        fi
                    # 存在多个内网IP则退出执行
                    elif [ $(echo $LAN_IP | xargs -n 1 | wc -l) -gt 1 ]; then
                        echo "$(red "${OBJECT}") Contains multiple IP: $(echo ${LAN_IP} | xargs -n 10)"
                        continue
                    fi
                    network_pint_test
                else
                    echo "$(red "$VAR") Hostname does not meet the standard"
                    continue
                 fi
            # 判断变量为命令集合,则进入命令操作界面
            elif [[ "$VAR" == "${CMD_LIST_NAME}" ]]; then
                select COMMADN_SRC in "${array_commands[@]}"; do
                    [ -z "${COMMADN_SRC}" ] && red "Invalid command" && continue
                    COMMAND=$(echo "${COMMADN_SRC}" | awk '{print $1}')
                    if [ "$COMMAND" == "ssh-keygen" -o "$COMMAND" == "passwd" ]; then
                        # 执行命令
                        ${COMMAND}
                    elif [[ "$COMMAND" == "ssh-copy-id" ]]; then
                         read -p "Please specify the IP: "  IP
                        # 判断指定IP合法、网络通信
                        # 执行ssh-copy-id $IP
                        ssh-copy-id $IP
                    elif [[ "$COMMAND" == "exit" ]]; then
                        break
                    elif [[ "$COMMAND" == "upload-local-key" ]]; then
                        read -p "$(green "Please enter the public key content:") " KEY
                        if [ -z "$KEY" ]; then
                            echo "The KEY is empty."
                        elif [ -f "/home/${LOGIN_USER}/.ssh/authorized_keys" ]; then
                            [ -n "$(grep "$KEY" /home/${LOGIN_USER}/.ssh/authorized_keys)" ] &&
                            {
                                echo "The KEY already exists."
                                continue
                            }
                         fi
                         echo "$KEY" >> /home/${LOGIN_USER}/.ssh/authorized_keys
                         echo -e "\nUpload to complete."
                    fi
                done
            fi
        done
    done
}

main