zoukankan      html  css  js  c++  java
  • 堡垒机续-堡垒机实现终端交互、堡垒机信息从数据库里读

    1. 打开会话

    import paramiko
    import sys
    import os
    import socket
    import select
    import getpass
    
    #创建连接
    tran = paramiko.Transport(('10.211.55.4', 22,))
    tran.start_client()
    tran.auth_password('zsc', '123')
     
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
     
    #####这段代码就是让你肆意妄为的####
    # 利用sys.stdin收到用户的输入;利用select监听对端的socket变化
    # 用户在终端输入内容,并将内容发送至远程服务器
    # 远程服务器执行命令,并将结果返回
    # 用户终端显示内容
    #########
     
    chan.close()
    tran.close()

    2.加上sys.stdin和select实现堡垒机基础功能

    import paramiko
    import sys
    import os
    import socket
    import select
    import getpass
    from paramiko.py3compat import u
     
    tran = paramiko.Transport(('10.211.55.4', 22,))
    tran.start_client()
    tran.auth_password('zsc', '123')
     
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
     
    while True:
        # 监视用户输入和服务器返回数据
        # sys.stdin 处理用户输入
        # chan 是之前创建的通道,用于接收服务器返回信息
        # select.select([chan, sys.stdin, ],[],[],1)的第一个参数是监听变化,只要列表里的变量有变化,就会将变化的参数放到readable里面,如果都没有变化,readable就是空值;当用户输入值按下回车后,就监听到了sys.stdin有变化,那么readable就等于sys.stdin;当对端socket返回命令结果时,select就监听到chan有变化,那么readable就等于chan。
        readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
        if chan in readable:
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print('
    *** EOF
    ')
                    break
                # 将数据写入缓冲区
                sys.stdout.write(x)
                # 将数据显示到屏幕
                sys.stdout.flush()
            except socket.timeout:
                pass
        if sys.stdin in readable:
            # 将用户的输入存为inp
            inp = sys.stdin.readline()
            # 将inp值通过chan通道传给对端机器
            chan.sendall(inp)
     
    chan.close()
    tran.close()

    上面的代码就实现了创建一个终端来与对端服务器交互数据的效果,不过select依赖终端,所以此代码只能在linux下运行。

    上面的效果是每次输入一行按下了回车后,才会把命令发到对端服务器,所以连上对端服务器后不能用tab补全命令,要实现只要按下回车上的任意字符都会传到对端服务器的话,需要做点额外的操作。

    3.实现将单个字符传给对端机器和tab补全

    之所以需要按下回车才能将命令传给对端机器,是因为linux终端有一个设置:只有按下回车,才会触发sys.stdin,又只有触发了sys.stdin才会将命令传给对端机器,所以必须按了回车数据才会传给对端机器,也没法使用tab补全。

    接下来要做的就是,修改终端的属性,使其每输入一个字符,就触发一次sys.stdin,而不是只有摁下回车才出发sys.stdin。输入一个“l”就把“l”发到对端机器,输入一个“s”就把“s”发到对端机器,输入一个回车,在对端机器就相当于执行了一个“ls”;实现了单个字符实时发送,所以也就实现了tab补全。

    import paramiko
    import sys
    import os
    import socket
    import select
    import getpass
    import termios
    import tty
    from paramiko.py3compat import u
     
    tran = paramiko.Transport(('10.211.55.4', 22,))
    tran.start_client()
    tran.auth_password('zsc', '123')
     
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
     
     
    # 获取原tty(终端)属性
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        #  为tty设置新属性
        #  默认当前tty设备属性:
        #        输入一行回车,执行
        #        CTRL+C 进程退出,遇到特殊字符,特殊处理。
     
        # 这是为原始模式,不认识所有特殊符号
        # 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
        tty.setraw(sys.stdin.fileno())
        chan.settimeout(0.0)
     
        while True:
            # 监视 用户输入 和 远程服务器返回数据(socket)
            # 阻塞,直到句柄可读
            r, w, e = select.select([chan, sys.stdin], [], [], 1)
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        print('
    *** EOF
    ')
                        break
                    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
                chan.send(x)
     
    finally:
        # 将终端属性恢复为默认
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
     
     
    chan.close()
    tran.close()

    登录目标机器后,紧跟着进入docker容器

    import paramiko
    import sys
    import os
    import socket
    import select
    import getpass
    import termios
    import tty
    import time
    from paramiko.py3compat import u
    
    
    container_name = str(sys.argv[1])
     
    tran = paramiko.Transport(('10.102.36.154', 9880,))
    tran.start_client()
    tran.auth_password('dockerj', 'dockerj')
     
    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()
     
     
    # 获取原tty(终端)属性
    oldtty = termios.tcgetattr(sys.stdin)
    try:
        #  为tty设置新属性
        #  默认当前tty设备属性:
        #        输入一行回车,执行
        #        CTRL+C 进程退出,遇到特殊字符,特殊处理。
     
        # 这是为原始模式,不认识所有特殊符号
        # 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
        tty.setraw(sys.stdin.fileno())
        chan.settimeout(0.0)
        tag_num = 1
        while True:
            # 监视 用户输入 和 远程服务器返回数据(socket)
            # 阻塞,直到句柄可读
            r, w, e = select.select([chan, sys.stdin], [], [], 1)
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        print('
    *** EOF
    ')
                        break
                    sys.stdout.write(x)
                    sys.stdout.flush()
                    if tag_num == 1 and x.startswith('[dockerj'):
                        x = 'docker exec -it ' + container_name + ' bash' + '
    '
                        sys.stdout.write(x)
                        sys.stdout.flush()
                    # 如果不是第一次连接,并且用户退出了容器,则强制用户退回到最初状态,即不让用户停留在容器的宿主机上;这台宿主机的shell提示符是“[dockerj@t-caiwu-36-154 ~]$”,当检测到返回的字符串以'[dockerj'开头时,就表示是回到宿主机shell上了,就强制退出宿主机shell。
                    if tag_num != 1 and x.startswith('[dockerj'):
                        print('
    *** EOF
    ')
                        break
                except socket.timeout:
                    pass
            if sys.stdin in r:
                # 当用户第一次连目标机器时,自动输入一个进入容器的命令,这样就能保证,用户连上宿主机后第一时间进入相应容器,而不是停留在宿主机上操作。
                if tag_num == 1:
                    x = 'docker exec -it ' + container_name + ' bash' + '
    '
                    tag_num = 0
                    chan.send(x)
                else:
                    x = sys.stdin.read(1)
                    if len(x) == 0:
                        break
                    chan.send(x)
     
    finally:
        # 将终端属性恢复为默认
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
     
     
    chan.close()
    tran.close()

    4.堡垒机较完整版本

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 终端没有termios,根据模块来判断是linux终端还是windows终端
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        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
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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:
            pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

    上面的堡垒机代码可以说功能上比较完善了,但是没有记录日志的功能。

    5.记录日志的堡垒机

    5.1 记录对端返回日志

    会把命令和命令的结果都记录到日志

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 没有termios,根据模块来判断是linux还是windows
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        # 将返回信息写入日志文件
                        with open('baolei.log','a+') as baoleilog:
                            baoleilog.write(x)
                        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
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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:
            pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

    5.2 记录用户操作日志

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 没有termios,根据模块来判断是linux还是windows
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            baoleilog = open('baolei.log','a+')
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        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
                    # 记录用户操作日志
                    baoleilog.write(x)
                    baoleilog.flush()
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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:
            pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

    按照上面的方法记录用户操作日志有个问题,如果用户使用了tab补全,那tab键会直接记录到日志里,而tab出来的命令不会记录到日志里,见下,

    ip a^Mls^Mid^Mif                        c                       o                       ^M^D,用户在输入ifcon后按下tab,然后记录到日志里就是ifcon和一个tab符,并没有记录ifconfig。事实上我们并不希望记录tab符。

    tab补全的两种情况:

    1.输入的命令信息太少,返回以if开头的所有命令,并且返回以“回车”开头

    [root@python python]# if
    if ifcfg ifconfig ifdown ifenslave ifnames ifstat ifup

    2.输入的命令信息足够多,直接补全命令,不换行

    [root@python python]# ifconfig 

    sys.stdin检测用户是否输入了tab键,如果输入了tab键,就不记录到日志,并且就把tab_flag置为True,因为输了tab键,所以对端会返回结果,chan里检测tab_flag,如果为True就表示用户输入了tab键,然后查看chan返回的内容是否以“换行符”开头,如果是以“换行符”开头,说明这个tab键获取到了很多以if开头的命令,如果不是以“换行符”开头,说明chan返回了具体的命令,则记录到日志里,记录后再把tab_flag置为False。

    处理tab后的代码:

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 没有termios,根据模块来判断是linux还是windows
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        tab_flag = False
        mingling = []
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            baoleilog = open('baolei.log','a+')
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        # tab_flag为真,则表示用户输入了tab键
                        if tab_flag:
                            # 如果返回数据以换行符开头,则不记录到日志
                            if not x.startswith('
    '):
                               baoleilog.write(x) 
                            # 把tab_flag重新置为False       
                            tab_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
                    # 如果用户输入的tab符,就把记录日志的操作转交给chan代码处理
                    if x == '	':
                        tab_flag = True
                    else:
                        baoleilog.write(x)
                        baoleilog.flush()
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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)
        username = 'sa'
        if len(username) == 0:
            username = default_username
    
    
        #hostname = input('Hostname: ')
        hostname = '192.168.0.30'
        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)
        auth = 'p'
        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:
            #pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            pw = 'xi'
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

    完成上面的代码后,日志还有一点点问题,见下,

    ip^Mls^Mifc^Gonfig ^G^M^D,记录的日志包含一些特殊字符,如“^M”是回车,下面我们需要对“^M”处理一下,其他字符暂时没找到对应的ord值。

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 没有termios,根据模块来判断是linux还是windows
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        tab_flag = False
        mingling = []
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            baoleilog = open('baolei.log','a+')
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        if tab_flag:
                            if not x.startswith('
    '):
                               baoleilog.write(x)        
                            tab_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 ord(x) == 13:
                       baoleilog.write('
    ')
                    else:
                        if x == '	':
                            tab_flag = True
                        else:
                            baoleilog.write(x)
                            baoleilog.flush()
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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)
        username = 'sa'
        if len(username) == 0:
            username = default_username
    
    
        #hostname = input('Hostname: ')
        hostname = '192.168.0.30'
        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)
        auth = 'p'
        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:
            #pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            pw = 'xi'
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

    6. 让用户选择登录哪台服务器

    import paramiko
    import sys
    import os
    import socket
    import getpass
    
    from paramiko.py3compat import u
    
    # windows 没有termios,根据模块来判断是linux还是windows
    try:
        import termios
        import tty
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan):
        if has_termios:
            posix_shell(chan)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan):
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        tab_flag = False
        mingling = []
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            baoleilog = open('baolei.log','a+')
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        if tab_flag:
                            if not x.startswith('
    '):
                               baoleilog.write(x)        
                            tab_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 ord(x) == 13:
                       baoleilog.write('
    ')
                    else:
                        if x == '	':
                            tab_flag = True
                        else:
                            baoleilog.write(x)
                            baoleilog.flush()
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    def windows_shell(chan):
        import threading
    
        sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.
    
    ")
    
        def writeall(sock):
            while True:
                data = sock.recv(256)
                if not data:
                    sys.stdout.write('
    *** EOF ***
    
    ')
                    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():
        # 打印该用户可用的服务器并选择进入
        host_info=[
            {'host':'192.168.0.30','username':'sa','pw':'xi','auth':'p'},
            {'host':'192.168.0.55','username':'sa','pw':'xi','auth':'p'}
        ]
        for key,item in enumerate(host_info,1):
            print(key,item['host'])
        sle_num = input('enter a number:')
        sle_num = int(sle_num) - 1
        hostname = host_info[sle_num]['host']
        username = host_info[sle_num]['username']
        pw = host_info[sle_num]['pw']
        auth = host_info[sle_num]['auth']
    
        tran = paramiko.Transport((hostname, 22,))
        tran.start_client()
    
        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:
            #pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
            pw = 'xi'
            tran.auth_password(username, pw)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        interactive_shell(chan)
    
        chan.close()
        tran.close()
    
    
    if __name__ == '__main__':
        run()

     7. 将堡垒机和数据库相连

    从数据库读取数据,将日志记录到数据库。

    7.1 建表

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://sqlalchemy:123456@192.168.0.57:3306/sqlalchemy", max_overflow=5)
    
    Base = declarative_base()
    
    #机器列表
    class Hostname(Base):
        __tablename__ = 'hostname'
        id = Column(Integer,primary_key=True)
        host_name = Column(String(32))
        host_port = Column(String(32))
    
    #用户列表
    class Username(Base):
        __tablename__ = 'username'
        id = Column(Integer,primary_key=True)
        username = Column(String(32))
        password = Column(String(32))
    
    #用户与机器的对应表
    class HostnametoUsername(Base):
        __tablename__ = 'hosttouser'
        id = Column(Integer,primary_key=True)
        hostname_id = Column(Integer,ForeignKey('hostname.id'))
        username_id = Column(Integer,ForeignKey('username.id'))
    
    #创建表
    def init_db():
        Base.metadata.create_all(engine)
    
    #删除表
    def drop_db():
        Base.metadata.drop_all(engine)
    
    #init_db()
    
    Session = sessionmaker(bind=engine)
    session = Session()
    
    #添加机器
    session.add_all([
        Hostname(host_name='192.168.0.30',host_port='22'),
        Hostname(host_name='192.168.0.55',host_port='22'),
        Hostname(host_name='192.168.0.56',host_port='22'),
        Hostname(host_name='192.168.0.57',host_port='22'),
    ])
    
    #添加用户
    session.add_all([
        Username(username='root',password='xi'),
        Username(username='sa',password='xi'),
        Username(username='xi',password='xi')
    ])
    
    #添加对应关系
    session.add_all([
        HostnametoUsername(hostname_id=3,username_id=1),
        HostnametoUsername(hostname_id=4,username_id=1),
        HostnametoUsername(hostname_id=4,username_id=2),
    ])
    session.commit()

     7.2 堡垒机

    #!/usr/bin/env python
    import paramiko
    import sys
    import os
    import socket
    import getpass
    import termios
    import tty
    import select
    from paramiko.py3compat import u
    
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://sqlalchemy:123456@192.168.0.57:3306/sqlalchemy", max_overflow=5)
    
    Base = declarative_base()
    class Hostname(Base):
        __tablename__ = 'hostname'
        id = Column(Integer,primary_key=True)
        host_name = Column(String(32))
        host_port = Column(String(32))
    
    class Username(Base):
        __tablename__ = 'username'
        id = Column(Integer,primary_key=True)
        username = Column(String(32))
        password = Column(String(32))
    
    class HostnametoUsername(Base):
        __tablename__ = 'hosttouser'
        id = Column(Integer,primary_key=True)
        hostname_id = Column(Integer,ForeignKey('hostname.id'))
        username_id = Column(Integer,ForeignKey('username.id'))
    
    #建立数据库连接会话
    Session = sessionmaker(bind=engine)
    session = Session()
    
    
    def posix_shell(chan):
        #获取linux终端属性
        oldtty = termios.tcgetattr(sys.stdin)
        #定义一个flag后面会用到
        tab_flag = False
        try:
            # 下面三行是设置属性,使linux终端能实时触发sys.stdin()
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            # 打开记录日志的文件
            baoleilog = open('baolei.log','a+')
            while True:
                # 只要chan或sys.stdin有变化,就将变化写入到r
                r, w, e = select.select([chan, sys.stdin], [], [])
                # 如果r里有chan,代表chan有变化,执行下面代码
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            break
                        # 如果用户输入“tab”键,会将tab_flag置为True
                        if tab_flag:
                            if not x.startswith('
    '):
                               baoleilog.write(x)
                            tab_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
                    # 如果用户输入回车,则不将回车符“^M”记录到日志,而是主动写入一个“
    ”
                    if ord(x) == 13:
                       baoleilog.write('
    ')
                    else:
                        # 如果用户输入的tab键,则将是否记录日志的活交给chan的代码处理
                        if x == '	':
                            tab_flag = True
                        else:
                            baoleilog.write(x)
                            baoleilog.flush()
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    def run():
        # 下面的代码就是从数据库里取到hostname、username、password、port,然后展示、认证等。
        ret = session.query(Hostname.host_name).all()
        ret1 = zip(*ret)
        ret2 = list(list(ret1)[0])
        for key,item in enumerate(ret2,1):
            print(key,item)
        select_hostname = int(input('enter a number:'))
        hostname_query = session.query(Hostname.host_name).filter_by(id=select_hostname).all()
        hostname = list(hostname_query[0])[0]
        #hostname = select_hostname
        hostport_query = session.query(Hostname.host_port).filter_by(host_name=hostname).all()
        port = int(list(hostport_query[0])[0])
    
        users = session.query(HostnametoUsername.username_id).filter_by(hostname_id=select_hostname).all()
        print('users:',users)
        users1 = list(list(zip(*users))[0])
        #for key,item in enumerate(users1,1):
        #    print(key,item) 
        user_list = []   
        for num in users1:
            user = session.query(Username.username).filter_by(id=num).all()
            user1 = list(list(zip(*user))[0])
            user_list.append(user1[0])
        for key,item in enumerate(user_list,1):
            print(key,item)
        select_username = int(input('enter a number:'))
        username_query = session.query(Username.username).filter_by(id=select_username).all()
        username = list(username_query[0])[0]
        print('username:',username)
        password_query = session.query(Username.password).filter_by(id=select_username).all()
        password = list(password_query[0])[0]
    
        tran = paramiko.Transport((hostname, port,))
        tran.start_client()
    
        auth = 'p'
    
        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:
            tran.auth_password(username, password)
    
        # 打开一个通道
        chan = tran.open_session()
        # 获取一个终端
        chan.get_pty()
        # 激活器
        chan.invoke_shell()
    
        posix_shell(chan)
    
        chan.close()
        tran.close()
    
    if __name__ == '__main__':
        run()
  • 相关阅读:
    java io系列23之 BufferedReader(字符缓冲输入流)
    java io系列22之 FileReader和FileWriter
    java io系列21之 InputStreamReader和OutputStreamWriter
    java io系列20之 PipedReader和PipedWriter
    java io系列19之 CharArrayWriter(字符数组输出流)
    java io系列18之 CharArrayReader(字符数组输入流)
    java io系列17之 System.out.println("hello world")原理
    java io系列16之 PrintStream(打印输出流)详解
    java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例
    java io系列14之 DataInputStream(数据输入流)的认知、源码和示例
  • 原文地址:https://www.cnblogs.com/fuckily/p/6061485.html
Copyright © 2011-2022 走看看