首页 > 学习资料 > 编程知识

Python编程(二十一):堡垒机开发 2022-07-11 08:20:03

一、前景介绍

  到目前为止,对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒机所具备的功能属性中的其中一项而已,下面简单介绍一下堡垒机的重要性。

  堡垒机有以下两个至关重要的功能:

(一)权限管理

  的服务器变的越来越多后,需要操作这些服务器的人就肯定不只是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操作业务系统,如果权限分配不当就会存在很大的安全风险,举几个场景例子:

有300台Linux服务器,A开发人员需要登录其中5台WEB服务器查看日志或进行问题追踪等事务,同时对另外10台hadoop服务器有root权限,在有300台服务器规模的网络中,按常理来讲你是已经使用了ldap(集中式认证,在windows上叫 ad域)权限统一认证的,你如何使这个开发人员只能以普通用户的身份登录5台web服务器,并且同时允许他以管理员的身份登录另外10台hadoop服务器呢?并且同时他对其它剩下的200多台服务器没有访问权限。目前,可能的运维团队为了方便,整个运维团队的运维人员还是共享同一套root密码,这样内部信任机制虽然使大家的工作方便了,但同时存在着极大的安全隐患,很多情况下,一个运维人员只需要管理固定数量的服务器,分为不同的业务线,不同的运维人员管理的业务线也不同,但如果共享一套root密码,其实就等于无限放大了每个运维人员的权限,也就是说,如果某个运维人员想干坏事的话,他可以在几的业务停转,甚至数据都给删除掉。为了降低风险,于是有人想到,把不同业务线的root密码改掉就ok了么,也就是每个业务线的运维人员只知道自己的密码,这当然是最简单有效的方式,但问题是如果你同时用了ldap,这样做又比较麻烦,即使你设置了root不通过ldap认证,那新问题就是,每次有运维人员离职,他所在的业务线的密码都需要重新改一次。

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

  在回收了运维或开发人员直接登录远程服务器的权限后,生产系统的所有认证过程都通过堡垒机来完成了,堡垒机等于成了生产系统的SSO(single sign on)模块了。你只需要在堡垒机上添加几条规则就能实现以下权限控制了:

允许A开发人员通过普通用户登录5台web服务器,通过root权限登录10台hadoop服务器,但对其余的服务器无任务访问权限多个运维人员可以共享一个root账户,但是依然能分辨出分别是谁在哪些服务器上操作了哪些命令,因为堡垒机账户是每个人独有的,也就是说虽然所有运维人员共享了一同一个远程root账户,但由于他们用的堡垒账户都是自己独有的,因此依然可以通过堡垒机控制每个运维人员访问不同的机器。(二)审计管理

  审计管理其实很简单,就是把用户的所有操作都纪录下来,以备日后的审计或者事故后的追责。在纪录用户操作的过程中有一个问题要注意,就是这个纪录对于操作用户来讲是不可见的,什么意思?就是指,无论用户愿不愿意,他的操作都会被纪录下来,并且,他自己如果不想操作被纪录下来,或想删除已纪录的内容,这些都是他做不到的,这就要求操作日志对用户来讲是不可见和不可访问的,通过堡垒机就可以很好的实现。

二、堡垒机架构

  堡垒机的主要作用权限控制和用户行为审计,堡垒机就像一个城堡的大门,城堡里的所有建筑就是你不同的业务系统 , 每个想进入城堡的人都必须经过城堡大门并经过大门守卫的授权,每个进入城堡的人必须且只能严格按守卫的分配进入指定的建筑,且每个建筑物还有自己的权限访问控制,不同级别的人可以到建筑物里不同楼层的访问级别也是不一样的。还有就是,每个进入城堡的人的所有行为和足迹都会被严格的监控和纪录下来,一旦发生犯罪事件,城堡管理人员就可以通过这些监控纪录来追踪责任人。

  堡垒机要想成功完全记到他的作用,只靠堡垒机本身是不够的, 还需要一系列安全上对用户进行限制的配合,堡垒机部署后,同时要确保你的网络达到以下条件:

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

  

确保除了堡垒机管理员之外,所有其它人对堡垒机本身无任何操作权限,只有一个登录跳转功能确保用户的操作纪录不能被用户自己以任何方式获取到并篡改 三、堡垒机功能实现需求(一)业务需求:兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即使有100%把握,也要做好最坏的打算,想好故障预案(二)功能需求:所有的用户操作日志要保留在数据库中每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码允许用户对不同的目标设备有不同的访问权限,例:对10.0.2.34 有mysql 用户的权限对192.168.3.22 有root用户的权限对172.33.24.55 没任何权限分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限

  目前市面上的商业堡垒机有:齐治

  开源的堡垒机有:jumpserver。使用python语言写的堡垒机。

(三)设计表结构:

  表结构如下图所示

四、堡垒机实践

  下面用python语言开发自己的堡垒机

  (一)先了解Python的paramiko模块

  该模块其实也实现了简单的堡垒机功能

  在 >github.com上下载paramiko的源代码将源码包中的文件夹demos拷贝出来,在命令行下进入demos文件夹,执行命令:

  python demo.py

  提示输入主机名(IP地址),接下来输入密码和用户名登录主机,此时会出错这个源代码不支持python 3提示错误在:demosinteractive.py", line 87, in writeallTypeError: write() argument must be str, not bytes此时修改源代码,源代码是:sys.stdout.write(data)修改为:sys.stdout.write(data.decode())修改源代码后,此时可以正常连接Linux主机下面在Linux主机上进行操作使用sz和rz命令发送和接收文件解压文件:unzip paramiko- ** ster.zipcd paramiko- ** ster,cd demospython3 demo.py

  修改源文件interactive.py,的代码段如下:

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 == " ": # 修改 cmd_str="".join(cmd) # 修改 print("-->",cmd_str) # 修改 cmd = [] # 修改 else: # 修改 cmd.append(x) # 修改 chan.send(x)

  修改源代码后,重新执行

  python3 demo.py

  登录Linux主机,可从输出看见每条被执行的命令。例如登录后,执行命令ls,在输出命令执行结果前,输出:ls--> ls现在需要实现远程用户登录堡垒机,就执行demo.py文件远程连接需要连接的主机由于堡垒机上每个用户都有家目录,家目录下有个文件是.bashrc,将demo.py文件的绝对路径添加到用户家目录下的.bashrc文件中,在末尾添加这条命令:python3 /paramiko- ** ster/demos/demo.py这样每个用户登录堡垒机时就自动执行demo.py去远程连接其它主机source .bashrc # 使文件立即生效

  (二)使用Python开发自己的堡垒机

  下面编写自己的堡垒机,将每个用户执行的每个命令都记录在数据库中首先为项目建一个文件夹:testJumpServer在testJumpServer目录下分别创建python package目录:bin,conf,log,models,以及普通目录modules, shareexamples

  在bin目录下创建文件:test_it.py,该文件是主程序文件在conf目录下创建文件:settings.py, action_registers.py在models目录下创建文件:models.py文件,在存放表结构

  modules目录下的文件有:actions.py, db_conn.py,utils.py,views.py

  shareexamples目录下的文件有:new_bindhosts.yml, new_groups.yml, new_hosts.yml, new_remoteuser.yml, new_user.yml 在创建表结构之前,先了解需求:假设有堡垒机用户:michael,需要登录下面主机:michael 192.168.1.10 rootmichael 192.168.1.11 mysqljack 192.168.1.10 rootjack 10.10.22.11 mysqljack 10.10.22.11 web从上面需求可以看出:一个用户可以对应多个主机,一个主机可以对应多个用户,一个用户可以在同一台主机上有多个账户,首先创建一张表保存远程主机的信息:host表,字段:ip hostname port创建保存堡垒机上的用户名和密码信息:user_profile表:字段:username password再创建一张保存远程主机的用户名和密码信息:remote_user表:字段:username password auth_type(密码还是ssk_key) root abc ssh-password mysql ssh-key mysql abc ssh-password创建主机组表:host_group字段:name创建日志表:auditlog字段:date 堡垒机用户 cmd ip remote_user

  下面代码是 test_it.py 文件的代码:

Author: __Cheng__ import os,sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(BASE_DIR) sys.path.append(BASE_DIR) if __name__ == '__ ** in__': from modules.actions import excute_from_com ** nd_line excute_from_com ** nd_line(sys.argv)

  所有的源代码如下:

# filename:settings.py Author: __Cheng__ ConnParams = "mysql+pymysql://michael:michael123@192.168.0.50:3508/testJPdb?charset=utf8" # filename:action_registers.py Author: __Cheng__ from modules import views actions = { 'start_session': views.start_session, # 'stop': views.stop_server, 'syncdb': views.syncdb, 'create_users': views.create_users, 'create_groups': views.create_groups, 'create_hosts': views.create_hosts, 'create_bindhosts': views.create_bindhosts, 'create_remoteusers': views.create_remoteusers, 'audit': views.log_audit, } # filename:models.py Author: __Cheng__ # 定义表结构 from sqlalchemy import Table, Column, Enum,Integer,String,DATE, ForeignKey,UniqueConstraint from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base # 安装一个插件pip3 install sqlalchemy_utils # PasswordType方法可以把密码加密,但不好使 from sqlalchemy_utils import ChoiceType,PasswordType from sqlalchemy import create_engine # from sqlalchemy.orm import session ** ker Base = declarative_base() # 创建多对多关系,主机和远程用户的关联 user_m2m_bindhost = Table('user_m2m_bindhost', Base.metadata, Column('userprofile_id',Integer,ForeignKey('user_profile.id')), Column('bindhost_id',Integer,ForeignKey('bind_host.id')), ) bindhost_m2m_hostgroup = Table('bindhost_m2m_hostgroup', Base.metadata, Column('bindhost_id',Integer,ForeignKey('bind_host.id')), Column('hostgroup_id',Integer,ForeignKey('host_group.id')), ) user_m2m_hostgroup = Table('userprofile_m2m_hostgroup', Base.metadata, Column('userprofile_id',Integer,ForeignKey('user_profile.id')), Column('hostgroup_id',Integer,ForeignKey('host_group.id')), ) class Host(Base): __tablename__ = 'host' id = Column(Integer, pri ** ry_key=True) hostname = Column(String( ** ), unique=True) ip = Column(String( ** )) port = Column(Integer, default=22) #remote_users = relationship('RemoteUser', secondary=host_m2m_remoteuser, backref='Hosts') def __repr__(self): return self.hostname class HostGroup(Base): __tablename__ = 'host_group' id = Column(Integer, pri ** ry_key=True) name = Column(String( ** ), unique=True) bind_hosts = relationship("BindHost", secondary="bindhost_m2m_hostgroup", backref="host_groups") def __repr__(self): return self.name class RemoteUser(Base): __tablename__ = 'remote_user' # 用户名和密码要联合唯一,下面命令中指出'auth_type', 'username', 'password'三个字段要联合唯一,通过哈希算法存储在'_user_passwd_uc'中 __table_args__ = (UniqueConstraint('auth_type', 'username', 'password', name='_user_passwd_uc'),) id = Column(Integer, pri ** ry_key=True) # auth_type = Column(Enum(0, 1)) # 不采用这种方法 AuthTypes = [ ('ssh-password', 'SSH/Password'), ('ssh-key', 'SSH/KEY'), ] # 列表中有两个元组,每个元组的两个元素是映射关系,如:ssh-key是存入MYSQL的值,SSH/KEY是查询的显示,可以写中文 auth_type = Column(ChoiceType(AuthTypes)) username = Column(String(32)) password = Column(String(128)) def __repr__(self): return self.username class BindHost(Base): """ 存放下面的关系 192.168.1.10 web bj_group 192.168.1.11 mysql sh_group """ __tablename__='bind_host' __table_args__ = (UniqueConstraint('host_id', 'remoteuser_id', name='_host_remoteuser_uc'),) # 'group_id',不要了 id = Column(Integer, pri ** ry_key=True) host_id = Column(Integer, ForeignKey('host.id')) #group_id = Column(Integer, ForeignKey('group.id')) remoteuser_id = Column(Integer, ForeignKey('remote_user.id')) host = relationship('Host',backref="bind_hosts") #host_group = relationship("Guoup",backref="bind_hosts") remote_user = relationship("RemoteUser", backref="bind_hosts") def __repr__(self): return "<%s -- %s>" % (self.host.ip, self.remote_user.username) # self.host_group.name) class UserProfile(Base): """堡垒机账户""" __tablename__ = 'user_profile' id = Column(Integer, pri ** ry_key=True) username = Column(String(32), unique=True) password = Column(String(128)) bind_hosts = relationship("BindHost", secondary="user_m2m_bindhost", backref="user_profiles") host_groups = relationship("HostGroup", secondary="userprofile_m2m_hostgroup", backref="user_profiles") def __repr__(self): return self.username # class AuditLog(Base): # pass if __name__ == "__ ** in__": engine = create_engine("mysql+pymysql://michael:michael123@192.168.0.50:3508/good ** ndb?charset=utf8", encoding='utf-8') Base.metadata.create_all(engine) # 创建表结构 # filename: actions.py Author: __Cheng__ from conf import action_registers from modules import utils def help_msg(): ''' print help msgs :return: ''' print("33[31;1mAvailable com ** nds:33[0m") for key in action_registers.actions: print(" ",key) def excute_from_com ** nd_line(argvs): if len(argvs) < 2: help_msg() exit() if argvs[1] not in action_registers.actions: utils.print_err("Com ** nd [%s] does not exist!" % argvs[1], quit=True) action_registers.actions[argvs[1]](argvs[1:]) # filename: db_conn.py Author: __Cheng__ from sqlalchemy import create_engine,Table from sqlalchemy.orm import session ** ker from conf import settings engine = create_engine(settings.ConnParams) #engine = create_engine(settings.DB_CONN,echo=True) SessionCls = session ** ker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 session = SessionCls() # filename: utils.py Author: __Cheng__ 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): output = "33[31;1mError: %s33[0m" % msg if quit: exit(output) else: print(output) def yaml_parser(yml_filename): ''' load yaml file and return :param yml_filename: :return: ''' #yml_filename = "%s/%s.yml" % (settings.StateFileBaseDir,yml_filename) try: yaml_file = open(yml_filename,'r') data = yaml.load(yaml_file) return data except Exception as e: print_err(e) # filename:views.py Author: __Cheng__ from models import models from conf import settings from modules.utils import yaml_parser, print_err from modules.db_conn import session def syncdb(argvs): print("Syncing DB....") engine = models.create_engine(settings.ConnParams, encoding='utf-8') models.Base.metadata.create_all(engine) #创建所有表结构 def create_hosts(argvs): ''' 创建主机 create hosts :param argvs: :return: ''' if '-f' in argvs: hosts_file = argvs[argvs.index("-f") +1 ] # 让用户指定文件名 else: print_err("invalid usage, should be: create_hosts -f <the new hosts file>",quit=True) source = yaml_parser(hosts_file) # 传递文件名 if source: # print(source) # 先看source是什么,输出信息如下,就是一个大字典 # {'ubuntu test': {'ip': '192.168.0.10', 'port': 22}, 'server1': {'ip': '192.168.0.50', 'port': 30000}, 'server2': {'ip': '10.4.4.22'}} # 下面的循环语句就在数据库中插入上面的数据 for key,val in source.items(): print(key,val) obj = models.Host(hostname=key,ip=val.get('ip'), port=val.get('port') or 22) session.add(obj) session.commit() def create_remoteusers(argvs): ''' 创建远程用户 create remoteusers :param argvs: :return: ''' if '-f' in argvs: remoteusers_file = argvs[argvs.index("-f") +1 ] else: print_err("invalid usage, should be: create_remoteusers -f <the new remoteusers file>",quit=True) source = yaml_parser(remoteusers_file) if source: for key,val in source.items(): print(key,val) obj = models.RemoteUser(username=val.get('username'),auth_type=val.get('auth_type'),password=val.get('password')) session.add(obj) session.commit() def create_users(argvs): ''' 创建堡垒机用户 create little_finger access user :param argvs: :return: ''' if '-f' in argvs: user_file = argvs[argvs.index("-f") +1 ] else: print_err("invalid usage, should be: createusers -f <the new users file>",quit=True) source = yaml_parser(user_file) if source: for key,val in source.items(): print(key,val) obj = models.UserProfile(username=key,password=val.get('password')) # if val.get('groups'): # groups = session.query(models.Group).filter(models.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 groups :param argvs: :return: ''' if '-f' in argvs: group_file = argvs[argvs.index("-f") +1 ] else: print_err("invalid usage, should be: creategroups -f <the new groups file>",quit=True) source = yaml_parser(group_file) if source: for key,val in source.items(): print(key,val) obj = models.HostGroup(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 bind hosts :param argvs: :return: ''' if '-f' in argvs: bindhosts_file = argvs[argvs.index("-f") +1 ] else: print_err("invalid usage, should be: create_hosts -f <the new bindhosts file>",quit=True) source = yaml_parser(bindhosts_file) if source: for key,val in source.items(): #print(key,val) host_obj = session.query(models.Host).filter(models.Host.hostname==val.get('hostname')).first() assert host_obj # 要求主机必须存在 for item in val['remote_users']: print(item ) assert item.get('auth_type') if item.get('auth_type') == 'ssh-password': # 获取远程用户 remoteuser_obj = session.query(models.RemoteUser).filter( models.RemoteUser.username==item.get('username'), models.RemoteUser.password==item.get('password') ).first() else: remoteuser_obj = session.query(models.RemoteUser).filter( models.RemoteUser.username==item.get('username'), models.RemoteUser.auth_type==item.get('auth_type'), ).first() if not remoteuser_obj: print_err("RemoteUser obj %s does not exist." % item,quit=True ) bindhost_obj = models.BindHost(host_id=host_obj.id,remoteuser_id=remoteuser_obj.id) # 创建关系,通过查询到的结果来创建 session.add(bindhost_obj) #for groups this host binds to if source[key].get('groups'): # 如果有组,就把组里的所有数据取出来 group_objs = session.query(models.HostGroup).filter(models.HostGroup.name.in_(source[key].get('groups') )).all() assert group_objs print('groups:', group_objs) bindhost_obj.host_groups = group_objs # 这里的host_groups是models.py模块中HostGroup类中的relationship方法中的参数backref参数的值host_groups #for user_profiles this host binds to if source[key].get('user_profiles'): userprofile_objs = session.query(models.UserProfile).filter(models.UserProfile.username.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(models.UserProfile).filter(models.UserProfile.username==username, models.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 ** ny attempts.") def welcome_msg(user): WELCOME_MSG = '''33[32;1m ------------- Welcome [%s] login testJumpServer ------------- 33[0m'''% user.username print(WELCOME_MSG) def start_session(argvs): print('going to start sesssion ') user = auth() # 认证 if user: welcome_msg(user) print(user.bind_hosts) print(user.host_groups) exit_flag = False while not exit_flag: if user.bind_hosts: print('33[32;1mz. ungroupped hosts (%s)33[0m' %len(user.bind_hosts) ) # len(user.bind_hosts)显示有多少条未分组的主机 for index,group in enumerate(user.host_groups): print('33[32;1m%s. %s (%s)33[0m' %(index,group.name, len(group.bind_hosts)) ) # group.bind_hosts是因为models.py中的HostGroup类有bind_hosts实例 choice = input("[%s]:" % user.username).strip() # 获取用户输入,选择 if len(choice) == 0:continue if choice == 'z': # 如果输入的是'z',打印未分组的主机信息 print("------ Group: ungroupped hosts ------" ) for index,bind_host in enumerate(user.bind_hosts): print(" %s. %s@%s(%s)"%(index, bind_host.remote_user.username, bind_host.host.hostname, bind_host.host.ip, )) print("----------- END -----------" ) elif choice.isdigit(): # 如果输入的是一个整数 choice = int(choice) if choice < len(user.host_groups): # 先判断先选择的是哪个组,就打印哪个组 print("------ Group: %s ------" % user.host_groups[choice].name ) for index,bind_host in enumerate(user.host_groups[choice].bind_hosts): print(" %s. %s@%s(%s)"%(index, bind_host.remote_user.username, bind_host.host.hostname, 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.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) # 下面是关键部分, 其中log_recording负责把日志写入数据库 # 这里还需要ssh_login.py文件的ssh_login()方法和log_recording()方法,这个留在作业完成 ssh_login.ssh_login(user, user.host_groups[choice].bind_hosts[user_option], session, log_recording) else: print("no this option..")

  下面是需要写入数据库的文件,内容如下:

# filename: new_bindhosts.yml bind1: hostname: ubuntu test remote_users: - user1: username: root auth_type: ssh-key #password: 123 - user2: username: michael auth_type: ssh-password password: michael123456 groups: - bj_group user_profiles: - michael - jack bind2: hostname: server2 remote_users: - user1: username: root auth_type: ssh-password password: abc!1234 groups: - bj_group - sh_group user_profiles: - Tom # filename: new_groups.yml bj_group: #bind_hosts: # - h1 # - h2 user_profiles: - michael sh_group: user_profiles: - jack - michael - rain # filename: new_hosts.yml ubuntu test: ip: 192.168.0.10 port: 22 server1: ip: 192.168.0.50 port: 30000 server2: ip: 10.4.4.22 # filename: new_remoteusers.yml user0: auth_type: ssh-password username: root password: abc!1234 user1: auth_type: ssh-password username: root password: qwert123 user2: auth_type: ssh-key username: root #password: abc!23 user3: auth_type: ssh-password username: michael password: michael123456 # filename:new_user.yml michael: password: michael123 # groups: # - web_servers # - db_servers #bind_hosts: # - h1 # - h2 # - h3 jack: password: jack123在运行bin目录下的test_it.py代码前,先在数据库创建一个新的数据库testJPdbcreate database testJPdb charset utf8;此时切换到bin目录的命令行下,执行下面的命令python test_it.py # 执行成功则输出下面信息:[31;1mAvailable com ** nds:[0m syncdb再次执行下面命令:python test_it.py adjla;kdf # 输出提示信息如下:[31;1mError: Com ** nd [adjla;kdf] does not exist![0m再一次执行下面的命令:python test_it.py syncdb # 输出如下提示信息:Syncing DB.... # 此时数据表才真正的创建成功在执行上面的创建命令时,如提示权限问题,可在mysql服务器上加权限:grant all on *.* to "michael"@"%" identified by "michael123";flush privileges;在mysql服务器上验证:use testJPdb;show tables; # 输出如下信息:+---------------------------+| Tables_in_testJPdb |+---------------------------+| bind_host || bindhost_m2m_hostgroup || host || host_group || remote_user || user_m2m_bindhost || user_profile || userprofile_m2m_hostgroup |+---------------------------+数据表创建完成后,接下来就往数据表中插入数据下面使用pyyaml模块写入数据,先安装pyyaml模块安装:pip3 install pyyaml导入:import yaml准备工作做完后,在命令行下再次执行执行:python test_it.py # 后面不加参数,输出如下信息:提示有2个命令可用[31;1mAvailable com ** nds:[0m syncdb create_hosts接下来执行: python test_it.py creat_hosts # 提示要求输入文件名[31;1mError: invalid usage, should be:create_hosts -f <the new hosts file>[0m下面来modules下的views.py文件中的 create_hosts 方法的source变量的输出信息:执行:python test_it.py create_hosts -f ..shareexamples ew_hosts.yml # 提示如下信息:{'ubuntu test': {'ip_addr': '192.168.0.10', 'port': 22}, 'server1': {'ip_addr': '192.168.0.50', 'port': 30000}, 'server2': {'ip_addr': '10.4.4.22'}}提示信息就是一个大的字典再一次执行:python test_it.py create_hosts -f ..shareexamples ew_hosts.yml # 提示如下信息:数据写入成功ubuntu test {'ip': '192.168.0.10', 'port': 22}server1 {'ip': '192.168.0.50', 'port': 30000}server2 {'ip': '10.4.4.22'}此时在Mysql服务器上执行:select * from hosts; # 输出如下信息:+----+-------------+--------------+-------+| id | hostname | ip | port |+----+-------------+--------------+-------+| 1 | ubuntu test | 192.168.0.10 | 22 || 2 | server1 | 192.168.0.50 | 30000 || 3 | server2 | 10.4.4.22 | 22 |+----+-------------+--------------+-------+下面来创建远程用户,再和主机关联起来首先在shareexamples目录下创建yaml文件nes_remoteusers.yml, 写入相关的用户信息其中的user0, user1不用管,后面依次是类型、用户名、密码其次,在conf目录下的action_registers.py文件中开启create_remoteusers功能准备工作做完后,就来执行下面的命令:python test_it.py create_remoteusers -f ..shareexamples ew_remoteusers.yml命令执行成功后输出如下信息:user0 {'auth_type': 'ssh-password', 'username': 'root', 'password': 'abc!1234'}user1 {'auth_type': 'ssh-password', 'username': 'root', 'password': 'qwert123'}user2 {'auth_type': 'ssh-key', 'username': 'root'}user3 {'auth_type': 'ssh-password', 'username': 'michael', 'password': 'michael123456'}此时在数据库上执行:select * from remote_user; # 输出如下信息:+----+--------------+----------+---------------+| id | auth_type | username | password |+----+--------------+----------+---------------+| 11 | ssh-key | root | NULL || 12 | ssh-password | michael | michael123456 || 9 | ssh-password | root | abc!1234 || 10 | ssh-password | root | qwert123 |+----+--------------+----------+---------------+下面来创建堡垒机用户:先开启功能:create_users准备工作做完后,执行下面命令创建堡垒机用户:python test_it.py create_users -f ..shareexamples ew_user.yml创建成功,输出如下信息:michael {'password': 'michael123'}jack {'password': 'jack123'}此时在mysql服务器上执行 select * from user_profile; # 输出如下信息:+----+----------+------------+| id | username | password |+----+----------+------------+| 1 | michael | michael123 || 2 | jack | jack123 |+----+----------+------------+下面来创建主机组开户功能:create_groups准备工作完成后,执行下面的命令创建组:python test_it.py create_groups -f ..shareexamples ew_groups.yml输出如下信息,表示创建组成功:bj_group {'user_profiles': ['michael']}sh_group {'user_profiles': ['jack', 'michael', 'rain']}此时查询:select * from host_group; # 输出如下信息:+----+----------+| id | name |+----+----------+| 1 | bj_group || 2 | sh_group |+----+----------+下面创建关联关系首先开启功能:create_bindhosts在mysql上另外增加一个堡垒机用户:insert into user_profile (username, password) values("Tom","tom123");准备工作做完后,执行下面的命令创建关联关系python test_it.py create_bindhosts -f ..shareexamples ew_bindhosts.yml输出如下信息,表示创建关联关系成功:{'user1': None, 'username': 'root', 'auth_type': 'ssh-key'}groups: [bj_group]userprofiles: [michael, jack]{'user2': None, 'username': 'michael', 'auth_type': 'ssh-password', 'password': 'michael123456'}groups: [bj_group]userprofiles: [michael, jack]{'user1': None, 'username': 'root', 'auth_type': 'ssh-password', 'password': 'abc!1234'}groups: [bj_group, sh_group]userprofiles: [Tom]此时在Mysql上查询,有如下信息:select * from bind_host; # 输出如下信息:+----+---------+---------------+| id | host_id | remoteuser_id |+----+---------+---------------+| 4 | 1 | 11 || 5 | 1 | 12 || 6 | 3 | 9 |+----+---------+---------------+如果程序执行没问题,此时执行下面命令会有相应的输出信息:select * from bindhost_m2m_hostgroup; # 输出如下:+-------------+--------------+| bindhost_id | hostgroup_id |+-------------+--------------+| 4 | 1 || 5 | 1 || 6 | 1 || 6 | 2 |+-------------+--------------+select * from user_m2m_bindhost; # 输出如下:+----------------+-------------+| userprofile_id | bindhost_id |+----------------+-------------+| 1 | 4 || 2 | 4 || 1 | 5 || 2 | 5 || 3 | 6 |+----------------+-------------+如果bindhost_m2m_hostgroup表的关联信息没有创建成功,可以手动创建:desc bindhost_m2m_hostgroup; # 查看表结构+--------------+---------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+--------------+---------+------+-----+---------+-------+| bindhost_id | int(11) | YES | MUL | NULL | || hostgroup_id | int(11) | YES | MUL | NULL | |+--------------+---------+------+-----+---------+-------+插入记录:insert into host_group (name) values("cd_group");insert into bindhost_m2m_hostgroup (bindhost_id, hostgroup_id) values(5,3);此时再执行查夜命令,有如下输出信息:select * from bindhost_m2m_hostgroup;+-------------+--------------+| bindhost_id | hostgroup_id |+-------------+--------------+| 4 | 1 || 5 | 1 || 6 | 1 || 6 | 2 || 5 | 3 |+-------------+--------------+下面继续开启start_session功能在views.py文件增加auth(),welcome_msg(),start_session()三个函数此时在命令行下执行: python test_it.py # 会输出下面的信息:[31;1mAvailable com ** nds:[0m start_session syncdb create_users create_groups create_hosts create_bindhosts create_remoteusers再次执行命令:python test_it.py start_session # 提示输入用户名和密码,此时的用户和密码是堡垒机(user_profile表)的用户名和密码going to start sesssion[32;1mUsername:[0mmichael # 输入用户名[32;1mPassword:[0mmichael123 # 输入用户名对应的密码[32;1m ------------- Welcome [michael] login testJumpServer ------------- [0m[<192.168.0.10 -- root>, <192.168.0.10 -- michael>][][32;1mz. ungroupped hosts (2)[0m[michael]:从上面的输出可以看出,michael没有跟组关联,下面在数据库中查证一下:首先:show tables; # 输出如下信息+---------------------------+| Tables_in_testJPdb |+---------------------------+| bind_host || bindhost_m2m_hostgroup || host || host_group || remote_user || user_m2m_bindhost || user_profile || userprofile_m2m_hostgroup |+---------------------------+select * from userprofile_m2m_hostgroup; # 查证没有输出信息,确实还没有做关联下面来做用户和组的关联首先查看有哪些组:select * from hsot_group; # 输出如下:+----+----------+| id | name |+----+----------+| 1 | bj_group || 3 | cd_group || 2 | sh_group |+----+----------+看表结构:desc userprofile_m2m_hostgroup; # 输出如下:+----------------+---------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+----------------+---------+------+-----+---------+-------+| userprofile_id | int(11) | YES | MUL | NULL | || hostgroup_id | int(11) | YES | MUL | NULL | |+----------------+---------+------+-----+---------+-------+看堡垒机用户:select * from user_profile;+----+----------+------------+| id | username | password |+----+----------+------------+| 1 | michael | michael123 || 2 | jack | jack123 || 3 | Tom | tom123 |+----+----------+------------+现在要做michael用户和cd_group组的关联,用户id和组id分别是:1,3,下面来做关联insert into userprofile_m2m_hostgroup values(1,3); # 执行成功则插入成功现在重新执行命令:python test_it.py start_session,输入用户和密码后的显示结果:going to start sesssion[32;1mUsername:[0mmichael[32;1mPassword:[0mmichael123[32;1m ------------- Welcome [michael] login testJumpServer ------------- [0m[<192.168.0.10 -- root>, <192.168.0.10 -- michael>][cd_group][32;1mz. ungroupped hosts (2)[0m[32;1m0. cd_group (1)[0m[michael]:从上面的输出可以看出,用户和组已经做好关联,此时cd_group组已经关联了1个主机此时输入字母z和数字0,可分别看出相关信息:[michael]:z # 输入字母z------ Group: ungroupped hosts ------ 0. root@ubuntu test(192.168.0.10) 1. michael@ubuntu test(192.168.0.10)----------- END -----------[32;1mz. ungroupped hosts (2)[0m[32;1m0. cd_group (1)[0m[michael]:0 # 输入数字0------ Group: cd_group ------ 0. michael@ubuntu test(192.168.0.10)----------- END -----------[32;1mz. ungroupped hosts (2)[0m[32;1m0. cd_group (1)[0m[michael]:此时在mysql服务器上执行下面命令,给用户michael分配bj_groupinsert into userprofile_m2m_hostgroup values(1,1);此时在命令行退出,重新执行命令:python test_it.py start_session,输出如下信息:going to start sesssion[32;1mUsername:[0mmichael[32;1mPassword:[0mmichael123[32;1m ------------- Welcome [michael] login testJumpServer ------------- [0m[<192.168.0.10 -- root>, <192.168.0.10 -- michael>][bj_group, cd_group][32;1mz. ungroupped hosts (2)[0m[32;1m0. bj_group (3)[0m # 已经出现了bj_group[32;1m1. cd_group (1)[0m[michael]:此时输入数字0,输出如下信息:michael用户已经关联了不同的组,不同的主机[michael]:0------ Group: bj_group ------ 0. root@ubuntu test(192.168.0.10) 1. michael@ubuntu test(192.168.0.10) 2. root@server2(10.4.4.22)----------- END -----------[32;1mz. ungroupped hosts (2)[0m[32;1m0. bj_group (3)[0m[32;1m1. cd_group (1)[0m[michael]:上面的操作没有问题,下面就来让用户输入一个数字,选择要连接的主机首先取消注释views.py文件中的start_session()方法中的while循环语句后面的代码未完成,留在作业去完成。要求在action.registers.py文件增加一个功能:'audit': views.log_audit,该功能是想要查看哪个用户的操作记录,就输入相应人员的名字即可查询到相关的记录

相关标签:

<