paramiko实现运维堡垒机原型

import paramiko
import sys,os,socket,getpass,logging

try:
    import termios
    import tty
    has_termios = True
except ImportError:#windows没有termios,可以通过它来判断程序当前运行环境
    has_termios = False

def interactive_shell(chan,logger):
    initLog(logger)
    if has_termios:
        posix_shell(chan,logger)
    else:
        windows_shell(chan,logger)

def initLog(logger):
    logger.setLevel(logging.DEBUG)

    fh = logging.FileHandler('command-history.log')
    fh.setLevel(logging.DEBUG)

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)

    logger.addHandler(fh)

def posix_shell(chan,logger):
    import select
    #获取当前tty属性,用于退出后恢复
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        #改变tty为原始模式,不认识回车/ctrl+c等特殊符号(不会触发特殊按键对应的功能),以便把用户输入原封不动全部发往远程服务器
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)

        flag = False
        temp_list = []#列表的形式,一个一个字符地收集一条命令字符串,最后拼成一条命令
        while True:
            #同时监听用户输入和远程服务器返回数据
            #select会阻塞到其中有一个句柄可读,中间两个参数在这没用,最后一个参数表是最多阻塞1秒不填就表示一直阻塞
            r,w,e = select.select([chan,sys.stdin],[],[])

            if chan in r:
                try:
                    x = chan.recv(1024).decode()
                    if len(x) == 0:#有可能远程终端断开了
                        print('\r\n***EOF***\r\n')
                        break

                    #如果上一次循环时用户输入了tab,则返回的是命令补全的字符,可以利用它记录命令日志
                    if flag:
                        if x.startswith('\r\n'):
                            pass
                        else:
                            temp_list.append(x)
                        flag = False

                    #把返回结果一次性输出到本地终端
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                if len(x) == 0:
                    break

                if x == '\t':#tab符,表示使用命令补全,不能把tab符号加入temp_list
                    flag = True
                else:#不是tab符,表示命令还没输入完全
                    temp_list.append(x)

                if x == '\r':#回车符,表示执行命令
                    logger.debug(''.join(temp_list))
                    temp_list.clear()#清空等待下一条命令

                #逐字符把用户输入发送给远程服务器
                chan.send(x)
    except Exception as ex:
        print(ex)
    finally:
        #退出时重置当前tty的属性
        termios.tcsetattr(sys.stdin,termios.TCSADRAIN,oldtty)


def windows_shell(chan,logger):
    import threading

    sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")

    def writeall(sock):
        while True:
            data = sock.recv(256).decode()
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            sys.stdout.flush()

    writer = threading.Thread(target=writeall, args=(chan,))
    writer.start()

    try:
        while True:
            d = sys.stdin.read(1)
            if not d:
                break
            chan.send(d)
    except EOFError:
        # user hit ^Z or F6
        pass

def run():
    #获取当前系统登录用户名
    default_username = getpass.getuser()
    username = input('Username [%s]: ' % default_username)
    if len(username) == 0:
        username = default_username

    hostname = input('Hostname: ')
    if len(hostname) == 0:
        print('***Hostname required***')
        sys.exit(1)

    tran = paramiko.Transport((hostname,22))
    tran.start_client()

    default_auth = 'p'
    auth = input('Auth by (p)assword or (r)sa key[%s]: ' % default_auth)
    if len(auth) == 0:
        auth = default_auth

    if auth == 'r':
        default_path = os.path.join(os.environ['HOME'],'.ssh','id_rsa')
        path = input('RSA key[%s]: ' % default_path)
        if len(path) == 0:
            path = default_path
        try:
            key = paramiko.RSAKey.from_private_key_file(path)
        except paramiko.PasswordRequiredException:
            password = getpass.getpass('RSA key password: ')#密钥文件需要密码,提示用户输入密码
            key = paramiko.RSAKey.from_private_key_file(path,password)
        tran.auth_publickey(username,key)
    else:
        #print(username,hostname)
        pw = getpass.getpass(prompt='Password for %s@%s: ' % (username,hostname))
        tran.auth_password(username,pw)

    #打开通道,建立与远程服务器的持久连接
    chan = tran.open_session()
    chan.get_pty()
    chan.invoke_shell()
    
    #用于记录命令历史到日志文件
    logger = logging.getLogger('history-log')

    interactive_shell(chan,logger)
    chan.close()
    tran.close()

if __name__ == '__main__':
    run()

 

转载于:https://my.oschina.net/codespring/blog/778439

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值