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()
  • 相关阅读:
    12.使用正则表达式
    12/12
    thinkphp 5 及一下或php项目里实现模糊查询
    mysql中文乱码--存入mysql里的中文变成问号的解决办法
    ATOM使用的一点心得与技巧——在一个窗口打开多个项目
    php里的$this的 含义
    pycharm2017.3专业版激活注册码
    thinkphp3.2.3的使用心得之i方法(零)
    thinkphp3.2.3的使用心得(零)
    linux系统下phpstudy里的mysql使用方法
  • 原文地址:https://www.cnblogs.com/fuckily/p/6061485.html
Copyright © 2011-2022 走看看