• 堡垒机


    堡垒机功能需求分析和实现

    1、权限管理:

    • 收回所有人员的直接登录服务器的权限,所有的登录动作都通过堡垒机授权,运维人员或开发人员不知道远程服务器的密码,这些远程机器的用户信息都绑定在了堡垒机上,堡垒机用户只能看到他能用什么权限访问哪些远程服务器。

    • 允许A开发人员通过普通用户登录5台web服务器,通过root权限登录10台hadoop服务器,但对其余的服务器无任务访问权限

    • 多个运维人员可以共享一个root账户,但是依然能分辨出分别是谁在哪些服务器上操作了哪些命令,因为堡垒机账户是每个人独有的,也就是说虽然所有运维人员共享了一同一个远程root账户,但由于他们用的堡垒账户都是自己独有的,因此依然可以通过堡垒机控制每个运维人员访问不同的机器。


    2、审计管理:

    • 所有人包括运维、开发等任何需要访问业务系统的人员,只能通过堡垒机访问业务系统回收所有对业务系统的访问权限,做到除了堡垒机管理人员,没有人知道业务系统任何机器的登录密码;网络上限制所有人员只能通过堡垒机的跳转才能访问业务系统;

    确保除了堡垒机管理员之外,所有其它人对堡垒机本身无任何操作权限,只有一个登录跳转功能;确保用户的操作纪录不能被用户自己以任何方式获取到并篡改

    3、功能需求:

    • 所有的用户操作日志要保留在数据库中

    • 每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码

    • 允许用户对不同的目标设备有不同的访问权限,例:

      • 对10.0.2.34 有mysql 用户的权限

      • 对192.168.3.22 有root用户的权限

      • 对172.33.24.55 没任何权限

    4、分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限 

    5、ssh公钥登录过程

    不需要密码连接服务器时,在主机上生成一对秘钥(公钥和私钥),只需要把公钥给对方即可;
    Linux生成秘钥的命令:

    • ssh-keygen  会生成两个文件
      • - /root/.ssh/.id_rsa 私钥 (.表示隐藏文件)

      • - /root/.ssh/.id_rsa.pub 公钥

    • - 把公钥拷贝到要登陆的主机上,命令:scp -rp id_rsa.pub alex@192.168.10.35:/home/alex/

    • - 把远程主机上的文件拷贝到本机命令:scp -rp alex@192.168.10.35:/home/alex/id_rsa.pub /tmp

    • - id 或 whoami 查看当前用户

    • - sudo - alex 切换用户

    6、表结构

     

     7、Linux环境下运行:

    settings.py修改内容:
    - ALLOWED_HOSTS = ['*',] 允许所有的主机访问这个Django程序
    - AUTH_USER_MODEL = 'web.UserProfile' # APP名+表名 告诉Django用自己定义的用户认证表

    8、测试:

    把项目拷贝到Linux环境下==>cd到目录下执行python shield_manager.py run==>xjl@126.com xjl1991 ==> 登陆堡垒机成功,开始选择远程主机
    ==》链接成功后,开始执行命令,日志自动记录

    9、实现登录Linux就执行堡垒机脚本:

    • 1、ls -a 查看当前目录有两个文件:.bashrc-登陆就会执行这个文件 .bash_logout 退出登陆就执行这个文件
    • 2、vim .bashrc (比如:在最下面写上echo 'hello!',登陆就会看到),所以在最下面加入以下两行命令即可:
      • python3 shield_manager.py run (登陆后会直接进入登陆堡垒机界面)
      • logout (退出远程主机之后,会直接退出堡垒机,不能让用户在堡垒机上操作)
    • 3、通过Linux的IP和端口登陆admin查看日志: sudo python3 manage.py runserver 0.0.0.0:8000,这样就可以用Linux自己的IP和8000端口登陆admin

    10、其实源码就是修改了paramiko源码里的demo.py 和interactive.py,整个源码执行流程为:

    • shield_manager.py
    import sys,os
    
    if __name__ == '__main__':
        # 在Django环境外调用其内部的数据库,需要配置环境变量,下面三步缺一不可
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'BlueShield.settings')
        import django
        django.setup()
    
        from backend import main
        interactive_obj = main.ArgvHandler(sys.argv)
        interactive_obj.call()
    • main.py
    class ArgvHandler(object):
        """接收用户参数,并调用相应的功能"""
        def __init__(self,sys_args):
            self.sys_args = sys_args
    
        def help_msg(self,error_msg=''):
            """打印帮助信息"""
            msgs = """
            error:%s
            run 启动用户交互程序
            """%error_msg
            exit(msgs) # 打印帮助信息
    
        def call(self):
            """根据参数调用对应方法"""
            if len(self.sys_args) == 1:
                self.help_msg()
            if hasattr(self,self.sys_args[1]):
                func = getattr(self,self.sys_args[1])
                func()
            else:
                self.help_msg('没有方法 %s'%self.sys_args[1])
    
        def run(self):
            """启动用户交互程序"""
            from backend.ssh_interactive import SshHandler
            # 把自己的对象传入
            obj = SshHandler(self)
            obj.interactive()
    • ssh_interactive.py
    from django.contrib.auth import authenticate
    from backend import paramiko_ssh
    from web import models
    
    class SshHandler(object):
        """堡垒机交互脚本"""
        def __init__(self,argv_handler_instance):
            self.argv_handler_instance = argv_handler_instance
            # 把web的数据库传给类的对象
            self.models = models
    
        def auth(self):
            """用户认证程序"""
            count = 0
            while count < 3:
                username = input('堡垒机帐号:').strip()
                password = input('Password:').strip()
                user = authenticate(username=username,password=password)
                if user:
                    # 如果用户登陆成功了,就把当前堡垒机帐号赋值给这个类的实例对象
                    self.user = user
                    return True
                else:
                    count += 1
    
        def interactive(self):
            """启动交互脚本"""
            # 如果用户登陆成功,进入循环程序
            if self.auth():
                print('Ready to print all the authorized hosts...to this user...')
                while True:
                    host_group_list = self.user.host_groups.all()
                    for index,host_group_obj in enumerate(host_group_list):
                        print('%s.	%s[%s]'%(index,host_group_obj.name,host_group_obj.host_to_remote_users.count()))
    
                    # 打印所有未分组的主机,注意:数据库里要保证单独分给用户的主机不在分组里
                    print('z.	未分组主机[%s]' % (self.user.host_to_remote_users.count()))
                    choice = input('请选择主机组>>:').strip()
                    selected_host_group = ''
                    if choice.isdigit():
                        choice = int(choice)
                        # 取出用户选择的组里所有的主机名加帐号
                        selected_host_group = host_group_list[choice]
                    elif choice == 'z':
                        # 取出未分组里所有的主机名加帐号
                        selected_host_group = self.user
                    while True:
                        for index,host_to_user_obj in enumerate(selected_host_group.host_to_remote_users.all()):
                            print('%s.	%s' % (index, host_to_user_obj))
                        choice = input('请选择主机>>:').strip()
                        if choice.isdigit():
                            choice = int(choice)
                            selected_host_to_user_obj = selected_host_group.host_to_remote_users.all()[choice]
                            print('going to logon %s'%selected_host_to_user_obj)
                            # 开始连接
                            paramiko_ssh.ssh_connect(self,selected_host_to_user_obj)
                        if choice == 'b':
                            break
    • paramiko_ssh.py
    import base64
    from binascii import hexlify
    import getpass
    import os
    import select
    import socket
    import sys
    import time
    import traceback
    from paramiko.py3compat import input
    
    import paramiko
    
    try:
        import interactive
    except ImportError:
        from . import interactive
    
    
    
    def manual_auth(t,username, hostname,password):
        default_auth = "p"
        # auth = input(
        #     "Auth by (p)assword, (r)sa key, or (d)ss key? [%s] " % default_auth
        # )
        auth = 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)
            t.auth_publickey(username, key)
        elif auth == "d":
            default_path = os.path.join(os.environ["HOME"], ".ssh", "id_dsa")
            path = input("DSS key [%s]: " % default_path)
            if len(path) == 0:
                path = default_path
            try:
                key = paramiko.DSSKey.from_private_key_file(path)
            except paramiko.PasswordRequiredException:
                password = getpass.getpass("DSS key password: ")
                key = paramiko.DSSKey.from_private_key_file(path, password)
            t.auth_publickey(username, key)
        else:
            # pw = getpass.getpass("Password for %s@%s: " % (username, hostname))
            t.auth_password(username, password)
    
    
    def ssh_connect(ssh_handler_instance,host_to_user_obj):
        # now connect
        hostname = host_to_user_obj.host.ip_addr
        port = host_to_user_obj.host.port
        username = host_to_user_obj.remote_user.username
        password = host_to_user_obj.remote_user.password
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((hostname, port))
        except Exception as e:
            print("*** Connect failed: " + str(e))
            traceback.print_exc()
            sys.exit(1)
    
        try:
            t = paramiko.Transport(sock)
            try:
                t.start_client()
            except paramiko.SSHException:
                print("*** SSH negotiation failed.")
                sys.exit(1)
    
            try:
                keys = paramiko.util.load_host_keys(
                    os.path.expanduser("~/.ssh/known_hosts")
                )
            except IOError:
                try:
                    keys = paramiko.util.load_host_keys(
                        os.path.expanduser("~/ssh/known_hosts")
                    )
                except IOError:
                    print("*** Unable to open host keys file")
                    keys = {}
    
            # check server's host key -- this is important.
            key = t.get_remote_server_key()
            if hostname not in keys:
                print("*** WARNING: Unknown host key!")
            elif key.get_name() not in keys[hostname]:
                print("*** WARNING: Unknown host key!")
            elif keys[hostname][key.get_name()] != key:
                print("*** WARNING: Host key has changed!!!")
                sys.exit(1)
            else:
                print("*** Host key OK.")
    
    
            if not t.is_authenticated():
                manual_auth(t,hostname,username ,password)
            if not t.is_authenticated():
                print("*** Authentication failed. :(")
                t.close()
                sys.exit(1)
    
            chan = t.open_session()
            chan.get_pty()
            chan.invoke_shell()
            # 把堡垒机帐号赋值给chan
            chan.shield_account = ssh_handler_instance.user
            chan.host_to_user_obj = host_to_user_obj
            chan.models = ssh_handler_instance.models
    
            print("*** Here we go!
    ")
            # 开始连接,记录登陆日志
            ssh_handler_instance.models.AuditLog.objects.create(
                user = ssh_handler_instance.user,
                log_type = 0,
                host_to_remote_user = host_to_user_obj,
                content = '***user  login***'
            )
            interactive.interactive_shell(chan)
            chan.close()
            t.close()
    
            # 记录退出日志
            ssh_handler_instance.models.AuditLog.objects.create(
                user=ssh_handler_instance.user,
                log_type=2,
                host_to_remote_user=host_to_user_obj,
                content='***user  logout***'
            )
    
        except Exception as e:
            print("*** Caught exception: " + str(e.__class__) + ": " + str(e))
            traceback.print_exc()
            try:
                t.close()
            except:
                pass
            sys.exit(1)
    • interactive.py
    import socket
    import sys,time
    from paramiko.py3compat import u
    
    # windows does not have termios...
    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):
        """Linux环境"""
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            cmd = []
            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
                    if x == '
    ':
                        # 回车打印完整命令
                        # print('输入命令>>:',''.join(cmd))
                        # log = '%s  %s
    '%(time.strftime('%Y-%m-%d %X',time.gmtime()),''.join(cmd))
                        # 记录用户输入命令日志
                        chan.models.AuditLog.objects.create(
                            user=chan.shield_account,
                            log_type=1,
                            host_to_remote_user=chan.host_to_user_obj,
                            content=''.join(cmd)
                        )
                        cmd = []
                    else:
                        cmd.append(x)
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    
    # thanks to Mike Looijmans for this code
    def windows_shell(chan):
        """windows环境"""
        import threading
        print(chan.shield_account,'*******')
        print(chan.host_to_user_obj,'-----------')
        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
  • 相关阅读:
    SPOJ913 Query on a tree II
    SPOJ375 Query on a tree
    HDU4348 To the moon
    Bzoj2753 [SCOI2012]滑雪与时间胶囊
    HDU4612 Warm up
    Uva11374 Airport Express
    Uva1624 Knots
    DevExpress的GridControl的使用以及怎样添加列和绑定数据源
    C#中使用Path、Directory、Split、Substring实现对文件路径和文件名的常用操作实例
    ZedGraph的曲线的LineItem对象的Tag属性存储信息进而在鼠标悬浮时进行显示
  • 原文地址:https://www.cnblogs.com/charliedaifu/p/10544368.html
Copyright © 2020-2023  润新知