• 堡垒机


     

    一、作业需求:

    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

    六、测试路程图

     
     
     
  • 相关阅读:
    前端面试攻略1------算法部分
    MongoDB学习
    MongoDB作为Windows服务来安装 错误1053:服务没有及时响应启动或控制请求
    Vue 入门之 Vuex 实战
    Vue 路由详解
    vue入门全局配置
    VSCode配合ESLint自动修复格式化
    vue入门之单文件组件
    Echarts图表-学习
    GoJS学习
  • 原文地址:https://www.cnblogs.com/gaodi2345/p/11412839.html
Copyright © 2020-2023  润新知