• 老男孩Day14作业:堡垒机


    一、作业需求:

    1.业务需求

        兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难
        保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即使有100%把握,也要做好最坏的打算,想好故障预案

    2.功能需求

        所有的用户操作日志要保留在数据库中
        每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
        允许用户对不同的目标设备有不同的访问权限,例:
            对10.0.2.34 有mysql 用户的权限
            对192.168.3.22 有root用户的权限
            对172.33.24.55 没任何权限
        分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限

    二、表结构图

     三、

    一、作业需求:
    1.业务需求
    
        兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难
        保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即使有100%把握,也要做好最坏的打算,想好故障预案
    
    2.功能需求
    
        所有的用户操作日志要保留在数据库中
        每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
        允许用户对不同的目标设备有不同的访问权限,例:
            对10.0.2.34 有mysql 用户的权限
            对192.168.3.22 有root用户的权限
            对172.33.24.55 没任何权限
        分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限
    
    二、博客地址:https://www.cnblogs.com/catepython/p/9177109.html
    
    三、运行环境
    
    操作系统:Win10
    
    Python:3.6.4rcl
    
    Pycharm:2017.3.4
    readme

    四、程序架构图

    五、核心模块

    bin目录

    #-*-coding:utf-8 -*-
    # Author: D.Gray
    import os,sys
    BASE_DESC = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    #print(BASE_DESC)
    sys.path.append(BASE_DESC)
    
    from modules.actions import excute_from_command_line
    excute_from_command_line(sys.argv)
    start

    conf目录

    #-*-coding:utf-8 -*-
    # Author: D.Gray
    from modules import views
    
    actions = {
        'start_session': views.start_session,  # 连接server
        # 'stop': views.stop_server,
        'syncdb': views.syncdb,  # 同步数据
        'create_users': views.create_users,  # 创建users
        'create_groups': views.create_groups,  # 创建组
        'create_hosts': views.create_hosts,  # 创建主机
        'create_bindhosts': views.create_bindhosts,  # 创建绑定关系
        'create_remoteusers': views.create_remoteusers,  # 创建远程用户
        'view_user_record': views.user_record_cmd  # 查看用户操作命令
    }
    action_registers
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    import sqlalchemy
    import os,sys
    BASE_DESC = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    #print(BASE_DESC)
    sys.path.append(BASE_DESC)
    
    
    CONN = 'mysql+pymysql://root:admin1988@localhost/mychine?charset=utf8'
    setting

    databases目录

    #-*-coding:utf-8 -*-
    # Author: D.Gray
    import sqlalchemy
    from sqlalchemy import create_engine
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import relationship
    from sqlalchemy_utils import ChoiceType
    from sqlalchemy import Column,Integer,String,ForeignKey,UniqueConstraint,Table,Text,DateTime
    from conf.setting import CONN
    
    Base = declarative_base()
    
    user_m2m_bind = Table(
        'user_m2m_bind',Base.metadata,
        Column('user_profile_id',Integer,ForeignKey('user_profile.id')),
        Column('bind_host_id',Integer,ForeignKey('bind_host.id'))
    )
    
    bind_m2m_group = Table(
        'bind_m2m_group',Base.metadata,
        Column('group_id',Integer,ForeignKey('group.group_id')),
        Column('bind_host_id',Integer,ForeignKey('bind_host.id'))
    )
    
    user_m2m_group = Table(
        'user_m2m_group',Base.metadata,
        Column('group_id',Integer,ForeignKey('group.group_id')),
        Column('user_id',Integer,ForeignKey('user_profile.id'))
    )
    
    class Host(Base):
        __tablename__ = 'host'
        host_id = Column(Integer,primary_key=True)
        host_name = Column(String(32),unique=True)
        IP = Column(String(32),nullable=False,unique=True)
        port = Column(Integer,default=22)
    
        def __repr__(self):
            return '<名称:【%s】    IP:【%s】     port:【%s】>'%(self.host_name,self.IP,self.port)
    
    class Group(Base):
        __tablename__ = 'group'
        group_id = Column(Integer,primary_key=True)
        group_name = Column(String(32),nullable=False,unique=True)
        group_bind = relationship('BindHost',secondary = 'bind_m2m_group',backref = 'groups_key')
    
        def __repr__(self):
            return '<组名:【%s】>'%(self.group_name)
    
    class RemUser(Base):
        '''
        远程登录用户
        '''
        __tablename__ = 'rem_user'
        __table_args__ = (UniqueConstraint('auth_type','username','password',name = 'rems_uc'),)
        id = Column(Integer,primary_key=True)
        Auth_types = [
            ('ssh-password','SSH/Password'),
            ('ssh-key','SSH/Key'),
        ]
        auth_type = Column(ChoiceType(Auth_types))
        username = Column(String(32),nullable=False)
        password = Column(String(32))
    
        def __repr__(self):
            return '<名称:【%s】    密码:【%s】     验证方式:【%s】>'%(self.username,self.password,self.auth_type)
    
    class BindHost(Base):
        '''
        此表是用来实现操控主机IP 和 登录用户 之间的绑定关系
            IP                                             远程登录名
        192.168.111.128                                       root
        192.168.111.129                                       admin_kyo
        host_id                                               remuser_id
        '''
        __tablename__ = 'bind_host'
        __table_args__ = (UniqueConstraint('remuser_id','host_id',name = 'binds_uc'),)
        id = Column(Integer,primary_key=True)
        remuser_id = Column(Integer,ForeignKey('rem_user.id'),nullable=False)
        host_id = Column(Integer,ForeignKey('host.host_id'),nullable=False)
        bind_hosts = relationship('Host',backref = 'bind_hosts')
        bind_remusers = relationship('RemUser',backref = 'bind_remusers')
    
        def __repr__(self):
            return '<IP:【%s】    远程登录名:【%s】>'
                   %(self.bind_hosts.IP,self.bind_remusers.username)
    
    class UserProfile(Base):
        '''
        堡垒机用户
        '''
        __tablename__ = 'user_profile'
        id = Column(Integer,primary_key=True)
        user_name = Column(String(32),nullable=False)
        password = Column(String(32),nullable=False)
        profile_bind = relationship('BindHost',secondary = 'user_m2m_bind',backref = 'user_profiles')
        profile_group = relationship('Group',secondary = 'user_m2m_group',backref = 'profile_groups')
    
        def __repr__(self):
            return '<名称:【%s】    密码:【%s】>'
                   %(self.user_name,self.password)
    
    class AuditLog(Base):
        '''
        用户操作日志表
        '''
        __tablename__ = 'audit_log'
        id = Column(Integer, primary_key=True)
        user_id = Column(Integer, ForeignKey('user_profile.id'))
        bind_host_id = Column(Integer, ForeignKey('bind_host.id'))
        action_choices = [
            (u'cmd', u'CMD'),
            (u'login', u'Login'),
            (u'logout', u'Logout'),
        ]
    
        action_type = Column(ChoiceType(action_choices))
        # 命令可能存的数值更大
        # cmd = Column(String(255))
        cmd = Column(Text(65535))
        date = Column(DateTime)
    
        user_profile = relationship("UserProfile")
        bind_host = relationship("BindHost")
    create_table

    modules目录

    #-*-coding:utf-8 -*-
    # Author: D.Gray
    
    from conf import action_registers
    from modules import utils
    
    
    def help_msg():
        '''
        帮助函数
        print help msgs
        :return:
        '''
        print("33[31;1mAvailable commands:33[0m")
        for key in action_registers.actions:
            print("	", key)
    
    def excute_from_command_line(argvs):
        '''
        命令执行函数
        print
        :param argvs:
        :return:
        '''
        if len(argvs) < 2:
            help_msg()
            exit()
        if argvs[1] not in action_registers.actions:
            utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True)
            # utils 工具箱
        action_registers.actions[argvs[1]](argvs[1:])
    actions
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    from database import create_table
    from modules.db_conn import engine, session
    from modules.utils import print_err
    
    def bind_hosts_filter(vals):
        '''
    
        :param vals:
        :return:
        '''
        print('**>', vals.get('bind_hosts'))
        bind_hosts = session.query(create_table.BindHost).
            filter(create_table.Host.host_name.in_(vals.get('bind_hosts'))).all()
        if not bind_hosts:
            print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'), quit=True)
        return bind_hosts
    
    def user_profiles_filter(vals):
        '''
    
        :param vals:
        :return:
        '''
        user_profiles = session.query(create_table.UserProfile).filter(create_table.UserProfile.user_name.
                                                                       in_(vals.get('user_profiles'))).all()
        if not user_profiles:
            print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'), quit=True)
        return user_profiles
    common_filters
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from conf import setting
    
    engine = create_engine(setting.CONN)
    # 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
    SessionCls = sessionmaker(bind=engine)
    session = SessionCls()
    db_conn
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
    #
    # This file is part of paramiko.
    #
    # Paramiko is free software; you can redistribute it and/or modify it under the
    # terms of the GNU Lesser General Public License as published by the Free
    # Software Foundation; either version 2.1 of the License, or (at your option)
    # any later version.
    #
    # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
    # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
    # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    # details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
    # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
    
    
    import socket
    import sys
    from paramiko.py3compat import u
    from database import create_table
    # from modules.views import log_recording
    import datetime
    import redis
    import time
    
    # windows does not have termios...
    try:
        import termios
        import tty
    
        has_termios = True
    except ImportError:
        has_termios = False
    
    
    def interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording):
        '''
        :param chan:
        :param user_obj:
        :param bind_host_obj: 主机
        :param cmd_caches: 命令列表
        :param log_recording: 日志记录
        :return:
        '''
        # 判断是否是windows shell
        if has_termios:
            posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording)
        else:
            windows_shell(chan)
    
    
    def posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording):
        '''
    
        :param chan:
        :param user_obj:
        :param bind_host_obj:
        :param cmd_caches:
        :param log_recording:
        :return:
        '''
        import select
    
        oldtty = termios.tcgetattr(sys.stdin)
        try:
            tty.setraw(sys.stdin.fileno())
            tty.setcbreak(sys.stdin.fileno())
            chan.settimeout(0.0)
            cmd = ''
            tab_key = False
            while True:
                r, w, e = select.select([chan, sys.stdin], [], [])
                if chan in r:
                    try:
                        x = u(chan.recv(1024))
                        if tab_key:
                            if x not in ('x07', '
    '):
                                # print('tab:',x)
                                cmd += x
                            tab_key = False
                        if len(x) == 0:
                            sys.stdout.write('
    *** EOF
    ')
                            # test for redis to mysql
                            break
                        sys.stdout.write(x)
                        sys.stdout.flush()
                    except socket.timeout:
                        pass
                if sys.stdin in r:
                    x = sys.stdin.read(1)
                    if '
    ' != x:
                        cmd += x
                    else:
                        user_record_cmd = user_obj.username + '_user_record'
                        pool = redis.ConnectionPool(host='localhost', port=22)
                        user_record = [user_obj.id, bind_host_obj.id, 'cmd', cmd,
                                       time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]
                        r = redis.Redis(connection_pool=pool)
                        r.lpush(user_record_cmd, user_record)
                        cmd = ''
                        # 最后用户退出的时候取出来log_item 列表循环写入数据库
                    if '	' == x:
                        tab_key = True
                    if len(x) == 0:
                        break
                    chan.send(x)
    
        finally:
            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
    
    
    # thanks to Mike Looijmans for this code
    def windows_shell(chan):
        '''
    
        :param chan:
        :return:
        '''
        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.decode())
                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
    interactive.py
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    import base64
    import getpass
    import os
    import socket
    import sys
    import traceback
    from paramiko.py3compat import input
    from database import create_table
    import redis
    import datetime
    import time
    
    import paramiko
    try:
        import interactive
    except ImportError:
        from . import interactive
    
    
    def ssh_login(user_obj, bind_host_obj, mysql_engine, log_recording):
        '''
        ssh登陆
        :param user_obj:
        :param bind_host_obj:
        :param mysql_engine: 连接数据库
        :param log_recording: 写日志记录
        :return:
        '''
        # now, connect and use paramiko Client to negotiate SSH2 across the connection
        try:
            client = paramiko.SSHClient()
            client.load_system_host_keys()
            client.set_missing_host_key_policy(paramiko.WarningPolicy())
            print('*** Connecting...')
            client.connect(bind_host_obj.host.ip,
                           bind_host_obj.host.port,
                           bind_host_obj.remote_user.username,
                           bind_host_obj.remote_user.password,
                           timeout=30)
            cmd_caches = []
            chan = client.invoke_shell()
            # print(repr(client.get_transport()))
            print('*** Here we go!
    ')
            # 连接redis
            pool = redis.ConnectionPool(host='192.168.84.66', port=6379)
            # 传一个命令列表给redis
            user_record = [user_obj.id, bind_host_obj.id, 'login',
                           time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]
            r = redis.Redis(connection_pool=pool)
            # 用用户名做key前缀,避免冲突
            key_name = str(user_obj.username)+'_login'
            r.lpush(key_name, user_record)
            interactive.interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording)
            chan.close()
            client.close()
            # 数据库写入操作
            login_record = r.lrange(key_name, 0, -1)
            login_redis_record = login_record[0].decode().replace('[', '').replace(']', '').split(',')
            log_item = create_table.AuditLog(user_id=login_redis_record[0],
                                       bind_host_id=login_redis_record[1],
                                       action_type='login',
                                       cmd='login',
                                       date=login_redis_record[3].replace("'", ''))
            cmd_caches.append(log_item)
            log_recording(user_obj, bind_host_obj, cmd_caches)
            user_record_cmd = user_obj.username+'_user_record'
            cmd_redis_record = r.lrange(user_record_cmd, 0, -1)
            for i in cmd_redis_record:
                cmd_caches = []
                v = i.decode().replace('[', '').replace(']', '').split(',')
                v2 = v[3].replace("'", '')
                # print(v[0], v[1], v[2], v[3], v[4])
                log_item = create_table.AuditLog(user_id=v[0],
                                           bind_host_id=v[1],
                                           action_type='cmd',
                                           cmd=v2, date=v[4].replace("'", ''))
                cmd_caches.append(log_item)
                log_recording(user_obj, bind_host_obj, cmd_caches)
            # 当退出的时候将redis的值写入到数据库并且清空redis
            logout_caches = []
            logout_caches.append(create_table.AuditLog(user_id=user_obj.id,
                                                 bind_host_id=bind_host_obj.id,
                                                 action_type='logout',
                                                 cmd='logout',
                                                 date=datetime.datetime.now()))
            log_recording(user_obj, bind_host_obj, logout_caches)
            # 清空keys
            r.delete(key_name)
            r.delete(user_record_cmd)
        except Exception as e:
            print('*** Caught exception: %s: %s' % (e.__class__, e))
            traceback.print_exc()
            try:
                client.close()
            except:
                pass
            sys.exit(1)
    ssh_login
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    import yaml
    try:
        from yaml import CLoader as Loader, CDumper as Dumper
    except ImportError:
        from yaml import Loader, Dumper
    
    
    def print_err(msg, quit=False):
        '''
    
        :param msg:
        :param quit:
        :return:
        '''
        output = "33[31;1mError: %s33[0m" % msg
        if quit:
            exit(output)
        else:
            print(output)
    
    def yaml_parser(yml_filename):
        '''
        yaml方法load yaml file and return
        :param yml_filename:
        :return:
        '''
        try:
            yaml_file = open(yml_filename, 'r')
            data = yaml.load(yaml_file)
            return data
        except Exception as e:
            print_err(e)
    utils
    #-*-coding:utf-8 -*-
    # Author: D.Gray
    from database import create_table
    from pymysql.err import IntegrityError
    from conf import setting
    from modules.utils import print_err, yaml_parser
    from modules.db_conn import engine, session
    from modules import ssh_login
    from modules import common_filters
    import codecs,os
    
    def syncdb(argvs):
        '''
        创建表结构方法
        :param argvs:
        :return:
        '''
        print("Syncing DB....")
        engine = create_table.create_engine(setting.CONN, echo=True)
        create_table.Base.metadata.create_all(engine)  # 创建所有表结构
    
    
    def create_hosts(argvs):
        '''
        create 主机
        :param argvs:
        :return:
        '''
        if '-f' in argvs:
            # 指定一个文件名否则报错
            hosts_file = argvs[argvs.index("-f") +1]
            host_path = os.path.join(setting.BASE_DESC,hosts_file)
            #print('hosts_path:',host_path)
        else:
            print_err("invalid usage, should be:
    create_hosts -f <the new hosts file>", quit=True)
        source = yaml_parser(host_path)  # 传文件回来
        if source:  # 循环字典
            print(source)
            for key, val in source.items():
                print(key, val)
                obj = create_table.Host(host_name=key, IP=val.get('ip'), port=val.get('port') or 22)
                # 添加到表
                try:
                    session.add(obj)
                except IntegrityError as e:
                    print('主机名和主机IP是唯一值已在数据库创建:',e)
                else:
                    session.commit()
    
    def create_remoteusers(argvs):
        '''
        create 远程用户数据
        :param argvs:
        :return:
        '''
        if '-f' in argvs:
            remoteusers_file = argvs[argvs.index("-f") +1]
            host_path = os.path.join(setting.BASE_DESC, remoteusers_file)
        else:
            print_err("invalid usage, should be:
    create_remoteusers -f <the new remoteusers file>", quit=True)
        source = yaml_parser(host_path)
        if source:
            for key, val in source.items():
                print(key, val)
                obj = create_table.RemUser(username=val.get('username'), auth_type=val.get('auth_type'),
                                        password=val.get('password'))
                session.add(obj)
            session.commit()
    
    def create_users(argvs):
        '''
        create 堡垒机用户数据
        create little_finger access user
        :param argvs:
        :return:
        '''
        if '-f' in argvs:
            user_file = argvs[argvs.index("-f") +1 ]
            host_path = os.path.join(setting.BASE_DESC, user_file)
        else:
            print_err("invalid usage, should be:
    createusers -f <the new users file>",quit=True)
    
        source = yaml_parser(host_path)
        if source:
            for key, val in source.items():
                print(key, val)
                obj = create_table.UserProfile(user_name=key, password=val.get('password'))
                if val.get('groups'):
                    groups = session.query(create_table.Group).
                        filter(create_table.Group.group_name.in_(val.get('groups'))).all()
                    if not groups:
                        print_err("none of [%s] exist in group table." % val.get('groups'), quit=True)
                    obj.groups = groups
                if val.get('bind_hosts'):
                    bind_hosts = common_filters.bind_hosts_filter(val)
                    obj.bind_hosts = bind_hosts
                #print(obj)
                session.add(obj)
            session.commit()
    
    def create_groups(argvs):
        '''
        create 组数据
        create groups
        :param argvs:
        :return:
        '''
        if '-f' in argvs:
            group_file = argvs[argvs.index("-f") + 1]
            host_path = os.path.join(setting.BASE_DESC, group_file)
        else:
            print_err("invalid usage, should be:
    creategroups -f <the new groups file>", quit=True)
        source = yaml_parser(host_path)
        if source:
            for key, val in source.items():
                print(key, val)
                obj = create_table.Group(group_name=key)
                if val.get('bind_hosts'):
                    bind_hosts = common_filters.bind_hosts_filter(val)
                    obj.bind_hosts = bind_hosts
    
                if val.get('user_profiles'):
                    user_profiles = common_filters.user_profiles_filter(val)
                    obj.user_profiles = user_profiles
                session.add(obj)
            session.commit()
    
    def create_bindhosts(argvs):
        '''
        create IP和远程用户关联数据
        create bind hosts
        :param argvs:
        :return:
        '''
        if '-f' in argvs:
            bindhosts_file = argvs[argvs.index("-f") + 1]
            host_path = os.path.join(setting.BASE_DESC, bindhosts_file)
        else:
            print_err("invalid usage, should be:
    create_hosts -f <the new bindhosts file>",quit=True)
        source = yaml_parser(host_path)
        if source:
            for key, val in source.items():
                print(key, val)
                # 获取到了主机
                host_obj = session.query(create_table.Host).
                    filter(create_table.Host.host_name == val.get('host_name')).first()
                # 取hostname
                assert host_obj  # 断言,必须存在
                for item in val['remote_users']:  # 判断
                    print(item)
                    assert item.get('auth_type')
                    if item.get('auth_type') == 'ssh-password':  # 判断认证password
                        remoteuser_obj = session.query(create_table.RemUser).filter(
                            create_table.RemUser.username == item.get('username'),
                            create_table.RemUser.password == item.get('password')
                                                        ).first()
                    else:
                        # 获取远程用户
                        remoteuser_obj = session.query(create_table.RemUser).filter(
                            create_table.RemUser.username == item.get('username'),
                            create_table.RemUser.auth_type == item.get('auth_type'),
                                                        ).first()
                    if not remoteuser_obj:  # 没取到,程序退出
                        print_err("RemoteUser obj %s does not exist." % item, quit=True)
                    bindhost_obj = create_table.BindHost(host_id=host_obj.host_id, remuser_id=remoteuser_obj.id)
                    session.add(bindhost_obj)  # 获取到关系后添加session
                    # for groups this host binds to
                    if source[key].get('groups'):  # 获取组
                        group_objs = session.query(create_table.Group).filter(create_table.Group.group_name.in_
                                                                            (source[key].get('groups'))).all()
                        assert group_objs
                        print('groups:', group_objs)
                        bindhost_obj.host_groups = group_objs
                    # for user_profiles this host binds to
                    if source[key].get('user_profiles'):  # 判断是否直接属于哪一台机器
                        userprofile_objs = session.query(create_table.UserProfile).
                            filter(create_table.UserProfile.user_name.in_(
                            source[key].get('user_profiles')
                        )).all()
                        assert userprofile_objs
                        print("userprofiles:", userprofile_objs)
                        bindhost_obj.user_profiles = userprofile_objs
                    # print(bindhost_obj)
            session.commit()
    
    def auth():
        '''
        用户验证
        do the user login authentication
        :return:
        '''
        count = 0
        while count < 3:
            username = input("33[32;1mUsername>>>:33[0m").strip()
            if len(username) == 0:
                continue
            password = input("33[32;1mPassword>>>:33[0m").strip()
            if len(password) == 0:
                continue
            user_obj = session.query(create_table.UserProfile).filter(create_table.UserProfile.user_name == username,
                                                                      create_table.UserProfile.password == password).first()
            if user_obj:
                return user_obj
            else:
                print("wrong username or password, you have %s more chances." % (3-count-1))
                count += 1
        else:
            print_err("too many attempts.")
    
    def welcome_msg(user):
        '''
        :param user: 接收start_session函数的user
        :return:
        '''
        WELCOME_MSG = '''33[32;1m
        ------------- Welcome [%s] login TinyServer -------------
        33[0m''' % user.user_name
        print(WELCOME_MSG)
    
    def start_session(argvs):
        '''
        开始远程登陆函数
        :param argvs:
        :return:
        '''
        print('going to start sesssion ')
        user = auth()               #调用auth认证函数 来判断输入的堡垒机用户是否存在
        if user:
            welcome_msg(user)
            # print(user.bind_hosts)
            # print(user.host_groups)
            exit_flag = False
            while not exit_flag:
                if user.profile_bind:
                    # 显示未分组的机器
                    print('33[32;1mz.	 z查看未分组主机列表/任意键查看已分组主机列表 (%s)33[0m' % len(user.profile_bind))
                for index, group in enumerate(user.profile_group):
                    print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
                # 用户输入
                choice = input("[%s]:" % user.user_name).strip()
                if len(choice) == 0:
                    continue
                # 如果是z 打印未分组机器
                if choice == 'z':
                    print("------ Group: 未分组主机 ------")
                    for index, bind_host in enumerate(user.profile_bind):
                        print("  %s.	%s@%s(%s)" % (index,
                                                    bind_host.bind_remusers.username,
                                                    bind_host.bind_hosts.host_name,
                                                    bind_host.bind_hosts.IP,
                                                    ))
                    print("----------- END -----------")
                elif choice.isdigit():  # 打印分组的机器
                    choice = int(choice)
                    if choice < len(user.profile_group):
                        print("------ Group: %s ------" % user.profile_group[choice].group_name)
                        for index, bind_host in enumerate(user.profile_group[choice].group_bind):
                            print("  %s.	%s@%s(%s)" % (index,
                                                        bind_host.bind_remusers.username,
                                                        bind_host.bind_hosts.host_name,
                                                        bind_host.bind_hosts.IP,
                                                        ))
                        print("----------- END -----------")
    
                        # host selection 选择机器去登陆
                        while not exit_flag:
                            user_option = input("[(b)back, (q)quit, select host to login]:").strip()
                            if len(user_option) == 0:
                                continue
                            if user_option == 'b':
                                break
                            if user_option == 'q':
                                exit_flag = True
                            if user_option.isdigit():
                                user_option = int(user_option)
                                if user_option < len(user.host_groups[choice].bind_hosts):
                                    print('host:', user.host_groups[choice].bind_hosts[user_option])
                                    # print('audit log:', user.host_groups[choice].bind_hosts[user_option].audit_logs)
                                    ssh_login.ssh_login(user,  # 传用户,用户组,连上对应的
                                                        user.host_groups[choice].bind_hosts[user_option],
                                                        session, log_recording)
                    else:
                        print("no this option..")
    
    def log_recording(user_obj, bind_host_obj, logs):
        '''
        flush user operations on remote host into DB
        :param user_obj:
        :param bind_host_obj:
        :param logs: list format [logItem1,logItem2,...]
        :return:
        '''
        # print("33[41;1m--logs:33[0m", logs)
        session.add_all(logs)
        session.commit()
    def user_record_cmd(argvs):
        '''
        查看操作记录方法
        :param argvs:
        :return:
        '''
        print('going to start view record')
        user = auth()
        # 默认root可以查所有人的记录
        if user.user_name == 'root':
            print('welcome 【%s】 ' % user.user_name)
            exit_flag = False
            # 用户对象
            user_obj = session.query(create_table.UserProfile).filter().all()
            # 循环查看堡垒机用户操作
            while not exit_flag:
                for user_profile_list in user_obj:
                    # 打印堡垒机用户,根据堡垒机用户ID选择其管辖的机器并打印日志
                    print("%s.	%s" % (user_profile_list.id, user_profile_list.user_name))
                choice = input("[%s]:" % user.user_name).strip()
                for user_profile_list in user_obj:
                    if str(choice) == str(user_profile_list.id):
                        if user_profile_list.profile_bind:
                            # 显示未分组的机器
                            print('33[32;1mz.	ungroupped hosts (%s)33[0m' % len(user_profile_list.profile_bind))
                        else:
                            print(' no binding groups ')
                        for index, group in enumerate(user_profile_list.profile_group):
                            print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
                        choice = input("[%s]:" % user.user_name).strip()
                        if choice.isdigit():  # 打印分组的机器
                            choice = int(choice)
                            if choice < len(user_profile_list.profile_group):
                                print("------ Group: %s ------" % user_profile_list.profile_group[choice].group_name)
                                for index, bind_host in enumerate(user_profile_list.profile_group[choice].group_bind):
                                    print("  %s.	%s@%s(%s)" % (index,
                                                                bind_host.remote_user.user_name,
                                                                bind_host.host.host_name,
                                                                bind_host.host.IP,
                                                                ))
                                print("----------- END -----------")
                                # host selection 选择机器去查看操作信息
                                while not exit_flag:
                                    user_option = input("[(b)back, (q)quit, select host to login]:").strip()
                                    if len(user_option) == 0:
                                        continue
                                    if user_option == 'b':
                                        break
                                    if user_option == 'q':
                                        exit_flag = True
                                    if user_option.isdigit():
                                        user_option = int(user_option)
                                        if user_option < len(user_profile_list.profile_group[choice].bind_hosts):
                                            # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
                                            data = 
                                                session.query(create_table.AuditLog).filter(
                                                    create_table.AuditLog.user_id == user_profile_list.id,
                                                    create_table.AuditLog.bind_host_id ==
                                                    user_profile_list.profile_group[choice].
                                                    group_bind[user_option].id).all()
                                            if data:
                                                for index, i in enumerate(data):
                                                    # redis 写入value的时候带有了	 
     等需要转义
                                                    # 第一个注释从数据库里读注释的这种不能转移	,
                                                    # 第二个和现行的俩种中文转义有些问题
                                                    # print(i.user_id, i.bind_host_id, i.action_type, i.cmd, i.date)
                                                    # print(i.user_id, i.bind_host_id, i.action_type,
                                                    #        codecs.getdecoder("unicode_escape")(i.cmd)[0], i.date)
                                                    # print(i.user_id, i.bind_host_id, i.action_type,
                                                    #       i.cmd.encode().decode('unicode-escape'), i.date)
                                                    print(index, i.date, i.cmd.encode().decode('unicode-escape'))
                                            else:
                                                print('no record in host:', user_profile_list.profile_group[choice].
                                                      group_bind[user_option])
        # 其他人只能查自己的操作记录
        else:
            exit_flag = False
            while not exit_flag:
                if user.profile_bind:
                    # 显示未分组的机器
                    print('33[32;1mz.	ungroupped hosts (%s)33[0m' % len(user.profile_bind))
                for index, group in enumerate(user.profile_group):
                    print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
                choice1 = input("[%s]:" % user.user_name).strip()
                # 查询选项
                if choice1 == 'z':
                    print("------ Group: ungroupped hosts ------")
                    for index, bind_host in enumerate(user.profile_bind):
                        print("  %s.	%s@%s(%s)" % (index,
                                                    bind_host.remote_user.user_name,
                                                    bind_host.host.host_name,
                                                    bind_host.host.IP,
                                                    ))
                    print("----------- END -----------")
                elif choice1.isdigit():  # 打印分组的机器
                    choice = int(choice1)
                    if choice < len(user.host_groups):
                        print("------ Group: %s ------" % user.profile_group[choice].name)
                        for index, bind_host in enumerate(user.profile_group[choice].group_bind):
                            print("  %s.	%s@%s(%s)" % (index,
                                                        bind_host.remote_user.user_name,
                                                        bind_host.host.host_name,
                                                        bind_host.host.IP,
                                                        ))
                        print("----------- END -----------")
    
                        # host selection 选择机器去查看操作信息
                        while not exit_flag:
                            user_option = input("[(b)back, (q)quit, select host to view record]:").strip()
                            if len(user_option) == 0:
                                continue
                            if user_option == 'b':
                                break
                            if user_option == 'q':
                                exit_flag = True
                            if user_option.isdigit():
                                user_option = int(user_option)
                                if user_option < len(user.profile_group[choice].group_bind):
                                    data = session.query(create_table.AuditLog)
                                        .filter(create_table.AuditLog.user_id == user.id,
                                                create_table.AuditLog.bind_host_id == user.profile_group[choice].
                                                group_bind[user_option].id).all()
                                    # print(user.host_groups[choice].bind_hosts[user_option].id)
                                    if data:
                                        for index, i in enumerate(data):
                                            print(index, i.date, i.cmd.encode().decode('unicode-escape'))
                                    else:
                                        print('no record in host:', user.profile_group[choice].group_bind[user_option])
                    else:
                        print("no this option..")
    views

    share目录

    bind1:
      host_name: server1
      remote_users:
        - user0:
          username: root
          auth_type: ssh-password
          password: 123456
      groups:
        - bj_group
      user_profiles:
        - sean
    
    bind2:
      host_name: server2
      remote_users:
        - user0:
          username: root
          auth_type: ssh-password
          password: 123456
      groups:
        - bj_group
        - sh_group
      user_profiles:
        - sean
        - jack
    
    bind3:
      host_name: server3
      remote_users:
        - user0:
          username: root
          auth_type: ssh-password
          password: 123456
      groups:
        - bj_group
        - sh_group
      user_profiles:
        - sean
        - jack
    
    bind4:
      host_name: server2
      remote_users:
        - user2:
          username: colin
          auth_type: ssh-password
          password: 123@123
      groups:
        - web_servers
      user_profiles:
        - root
    
    bind5:
      host_name: server3
      remote_users:
        - user3:
          username: web
          auth_type: ssh-password
          password: 12345678
        - user1:
          username: mysql
          auth_type: ssh-password
          password: 12345678
      groups:
        - web_servers
        - db_servers
      user_profiles:
        - root
    new_bindhost.yml
    bj_group:
      user_profiles:
        - sean
    
    sh_group:
      user_profiles:
        - jack
    
    db_servers:
      user_profiles:
      - root
    
    web_servers:
      user_profiles:
      - root
    new_groups.yml
    server1:
      ip: 192.168.111.128
      port: 22
    
    server2:
      ip: 192.168.111.129
      port: 22
    
    server3:
      ip: 192.168.111.130
      port: 22
    new_hosts
    user0:
      auth_type:  ssh-password
      username: root
      password: 123456
    
    user1:
      auth_type:  ssh-password
      username: mysql
      password: 12345678
    
    user2:
      auth_type:  ssh-password
      username: colin
      password: 123@123
    
    user3:
      auth_type:  ssh-password
      username: web
      password: 12345678
    
    user4:
      auth_type:  ssh-key
      username: root
    new_remoteusers.yml
    root:
      password: 123@456
    
    sean:
      password: 123456
    
    jack:
      password: 123456
    new_user.yml

    六、测试路程图

  • 相关阅读:
    IDEA设置显示行号和方法间的分隔符
    IDEA设置自动导包功能
    IDEA设置设置鼠标滚轮修改字体大小
    CA数字证书部署
    mysql数据库基础
    部署集群基础环境,MySQL-MMM架构部署,MySQL-MMM架构使用
    使用binlog日志, XtraBackup备份工具 ,MySQL AB复制
    实现MySQL读写分离,MySQL性能调优
    密码恢复及设置,用户授权及撤销,MySQL管理工具
    SQL数据导入/导出,操作表记录,查询及匹配条件
  • 原文地址:https://www.cnblogs.com/catepython/p/9177109.html
Copyright © 2020-2023  润新知