认识堡垒机
拓展两个知识点:
1、traceback:出异常,会具体打印出哪一行
traceback.print_exc()
2、getpass模块获取用户名:
uson@ubuntu:~$ python3 Python 3.6.8 (default, Aug 20 2019, 17:12:48) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import getpass >>> getpass.getuser() 'uson'
3、Ubuntu配置用户的环境变量:
source:使当前shell读入路径为filepath的shell文件并依次执行文件中的所有语句,通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录
命令行输入:mysql -uuson -pakaedu改成source .bashrc执行 # 每个用户目录下都有一个.bashrc文件 #(1)vim .bashrc 在最后新增一行命令行输入的东西: mysql -uuson -pakaedu 或者 python3 .../.../..../.py # 保存文件的绝对路径abspath # (2)执行.bashrc source .bashrc
4、字典的用法补充:
val.get(kek) or str 获取字典key, 获取不到,选择or ---->val为字典
val.get(key)
5、assert host_obj 断言,即要求host_obj必须存在,否则报错
当断言条件为假时,抛出异常AssertionError
6、本例用到yaml模块,安装pyyaml模块
7、本例数据库建造时用到枚举类型ChoiceType(),需要用到一个插件
from sqlalchemy_utils import ChoiceType
堡垒机环节:
堡垒机的作用:
1、不需要提供root密码给运维
2、记录用户操作记录(审计)
3、用户权限控制
只有堡垒机管理员知道root密码
记住,表结构设计好之前,不要写代码
配置文件一般只写配置(类似变量类的赋值语句),不写动作(func())
正文:
******下载paramiko(本质上就是封装了ssh)文件,我们用到demo.py文件和interactive.py文件,并修改
业务逻辑结构:
代码示例:
bin目录:
1 操作流程: 2 堡垒机用户及主机组之间的数据实例化,和关联: 3 1、入口create_groups,不添加关联数据(注释bind_hosts/user_profiles):已实例化主机组 new_groups.yml 4 add_groups,不添加关联数据(注释bind_hosts/user_profiles),添加主机组数据一条 add_groups.yml 5 2、create_users,关联检测hostgroups,不关联bind_hosts数据,已实例化堡垒机用户 new_user2.yml 6 已自动关联hostgroup,userprofile 7 3、检测:create_groups中的user_profiles关联是否有问题 8 取消注释user_profiles,新加一个主机组反关联检测user_profiles add_groups2.yml 9 10 以上均无问题后,继续 11 bindhost关联所有 12 4、create_hosts new_hosts.yml 13 5、create_remoteusers new_remoteusers.yml 14 15 6、BindHost>>>
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 import os,sys 5 6 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 7 print(BASE_DIR) 8 9 sys.path.append(BASE_DIR) 10 11 from modules.actions import excute_from_command_line 12 13 if __name__ == '__main__': 14 #执行脚本命令行参数判断 python fuck_run.py syncdb 15 #argv[0]是脚本名fuck_run.py,argv[1]是第一个命令行参数syncdb 16 excute_from_command_line(sys.argv) #sys.argv #传进来的是列表形式
1 #dict: many val = list[ {} ] 2 #Chinese is not supported 3 4 bind1: 5 hostname: server1 6 remote_users: #dict{[-,-]} #{ [ {user1:...}, {user2:...} ] } 7 # [{'user1': None, 'auth_type': 'ssh-key', 'username': 'root'}, 8 # {'username': 'alex', 'password': 'alex3714', 'user2': None, 'auth_type': 'ssh-password'}] 9 - user1: #list[{:,:,}] #- [ {user1:None, username:root, ...}, {}, ...] 10 username: root 11 auth_type: ssh-key 12 #password: 123 13 - user2: #[] 14 username: alex 15 auth_type: ssh-password 16 password: alex3714 17 # remote_users: 18 # - user1: 19 # - username: root 20 # - auth_type: ssh-key 21 # - #password: 123 22 # - user2: 23 # - username: alex 24 # - auth_type: ssh-password 25 # - password: alex3714 26 groups: 27 - log_devteam 28 user_profiles: 29 - alex 30 31 bind2: 32 hostname: server2 33 remote_users: 34 - user1: 35 username: alex 36 auth_type: ssh-password 37 password: alex3714 38 # remote_users: dict{ [user1:[ username:alex, auth_type:ssh-password, ... ], [], ...] } 39 # - user1: 40 # - username: alex 41 # - auth_type: ssh-password 42 # - password: alex3714 43 #item: {'user1': [{'username': 'alex'}, {'auth_type': 'ssh-password'}, {'password': 'alex3714'}]} all is :key 44 #key: {'user1': [{'username': 'alex'}, {'auth_type': 'ssh-password'}, {'password': 'alex3714'}]} 45 groups: 46 - web_devteam 47 - mysql_devteam 48 49 user_profiles: 50 - rain
conf目录:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 5 from modules import views, views_add 6 7 ''' 8 本测试: 9 (1)创建主机组,不创建binhost/userprofile 10 (2)创建用户, 11 ''' 12 13 actions = { 14 'syncdb': views.syncdb, #连接数据库,创建数据表 #(第一步) 15 'create_hosts': views.create_hosts, # (第二步) 16 'create_remoteusers': views.create_remoteusers, # (第三步) 17 18 'create_users': views.create_users, #(第四步) 19 'create_groups': views.create_groups, #(第五步) 20 21 'create_bindhosts': views.create_bindhosts, #(第六步) 22 23 'start_session': views.start_session, #(第七步) 24 25 # 'stop': views.stop_server, #(第八步)暂未实现 26 27 'hand_modify': views.hand_modify, #暂未实现,只能创建不能修改 #一条一条手动输入修改,非文件提交 28 29 # 'audit': views.log_audit, #查看所有运维人员的记录,暂未实现 30 31 'add_groups': views_add.add_groups, #主机组添加数据,针对死循环,没有入口,不含关联数据 32 }
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 import os,sys 5 6 # BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 7 8 ConnParams = "mysql+pymysql://uson:akaedu@192.168.1.6/uson_fuckdb?charset=utf8" #只写配置不写动作
models目录:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 '''堡垒机用户userprofile与hostgroup多对多关联''' 5 from conf import settings 6 from sqlalchemy import create_engine 7 from sqlalchemy.ext.declarative import declarative_base 8 from sqlalchemy import Column, Integer, String, Date, Table, Enum 9 from sqlalchemy import ForeignKey, UniqueConstraint #联合唯一键 10 from sqlalchemy.orm import relationship 11 12 from sqlalchemy_utils import ChoiceType 13 14 engine = create_engine(settings.ConnParams) 15 Base = declarative_base() 16 17 # '''(2)删除:多对多关联:第三方表的建设''' 18 # Host_To_Remoteuser = Table( 19 # 'host_to_remoteuser', Base.metadata, #元数据,中介数据 == 实例化Metadata()后的metadata 20 # Column('host_id', Integer, ForeignKey('host.id')), #需要用,分隔 21 # Column('remoteuser_id', Integer, ForeignKey('remoteuser.id')), #需要用,分隔 22 # ) 23 24 '''(3)多对多关联:堡垒机用户第三方表的建设''' 25 Userprofile_To_Bindhost = Table( 26 'userprofile_to_bindhost', Base.metadata, 27 Column('userprofile_id', Integer, ForeignKey('userprofile.id')), 28 Column('bindhost_id', Integer, ForeignKey('bindhost.id')), 29 ) 30 '''(4)多对多关联:主机组第三方表的建设''' 31 Bindhost_To_Hostgroup = Table( 32 'bindhost_to_hostgroup', Base.metadata, 33 Column('hostgroup_id', Integer, ForeignKey('hostgroup.id')), 34 Column('bindhost_id', Integer, ForeignKey('bindhost.id')), 35 ) 36 '''(5)多对多关联:主机组第三方表的建设,堡垒机用户userprofile与hostgroup多对多关联''' 37 Userprofile_To_Hostgroup = Table( 38 'userprofile_to_hostgroup', Base.metadata, 39 Column('hostgroup_id', Integer, ForeignKey('hostgroup.id')), 40 Column('userprofile_id', Integer, ForeignKey('userprofile.id')), 41 ) 42 43 '''(2)新增一张表:存host,user, hostgroup''' 44 class BindHost(Base): 45 ''' 46 host user host_group 47 192.168.1.1 web bj_group 48 192.168.1.1 log sh_group 49 ''' 50 __tablename__ = 'bindhost' 51 52 '''联合唯一''' 53 __table_args__ = (UniqueConstraint('host_id', 'hostgroup_id', 'remoteuser_id'),) # 联合唯一键 54 55 id = Column(Integer, primary_key=True) 56 '''(2)不用第三张表的多对多方式:一条一个外键:多个外键多个关系''' 57 host_id = Column(Integer, ForeignKey('host.id')) 58 # hostgroup_id = Column(Integer, ForeignKey('hostgroup.id')) (4) 59 remoteuser_id = Column(Integer, ForeignKey('remoteuser.id')) 60 61 hosts = relationship("Host", backref = 'bind_hosts') 62 # hostgroups = relationship("HostGroup", backref = 'bind_hosts') (4) 63 remoteusers = relationship("RemoteUser", backref = 'bind_hosts') 64 def __repr__(self): 65 return "<%s - %s - %%s>" %(self.hosts.ip, 66 # self.hostgroups.name, (4) 67 self.remoteusers.username) 68 69 '''堡垒机表结构 www.processon.com''' 70 class Host(Base): #远程主机 71 __tablename__ = 'host' 72 id = Column(Integer, primary_key=True) #主键 73 hostname = Column(String(32), unique=True) #主机名(非ip)唯一 74 ip = Column(String(32), unique=True) #主机ip唯一 75 port = Column(Integer, default=22) #端口号默认22 76 77 # '''(2)删除:Host_To_Remoteuser''' 78 # remote_users = relationship('RemoteUser', secondary = Host_To_Remoteuser, backref = 'hosts') 79 80 def __repr__(self): 81 return self.hostname 82 class HostGroup(Base):#远程主机组 83 __tablename__ = 'hostgroup' 84 id = Column(Integer, primary_key=True) # 主键 85 name = Column(String(32), unique=True) #主机组名唯一 86 87 '''(4)建立关系''' 88 bindhosts = relationship("BindHost", seeondary = Bindhost_To_Hostgroup, backref = 'host_groups') 89 90 def __repr__(self): 91 return self.name 92 class RemoteUser(Base):#远程主机用户 93 __tablename__ = 'remoteuser' 94 95 '''用户名和密码多对多关系:要使用联合唯一''' 96 __table_args__ = (UniqueConstraint('auth_type', 'username', 'password'),)#联合唯一键 97 98 id = Column(Integer, primary_key=True) # 主键 99 100 # auth_type = Column(Enum(0,1)) 101 '''认证类型:列表枚举型,经典案例,用到sqlalchemy_utils模块下的ChoiceType''' 102 AuthTypes = [ 103 ('ssh-password', 'SSH/Password'), # 前者写入数据库,后者显示给用户 104 ('ssh-key', 'SSH/KEY'), 105 ] 106 auth_type = Column(ChoiceType(AuthTypes)) #从列表中选择类型 107 108 username = Column(String(32)) # 可以同用户名root,不同密码 109 password = Column(String(128)) # 可以同密码,不同用户名 110 def __repr__(self): 111 return self.username 112 class UserProfile(Base):#堡垒机用户 113 __tablename__ = 'userprofile' 114 id = Column(Integer, primary_key=True) # 主键 115 username = Column(String(32), unique=True) #堡垒机用户名唯一 116 password = Column(String(128)) 117 118 '''(3)堡垒机用户第三张表建设''' 119 bindhosts = relationship("BindHost", secondary = Userprofile_To_Bindhost, backref = 'user_profiles') 120 '''(5)''' 121 hostgroups = relationship("HostGroup", secondary = Userprofile_To_Hostgroup, backref = 'user_profiles') 122 def __repr__(self): 123 return self.username 124 class Auditlog(Base): #堡垒机操作日志 125 __tablename__ = 'host' 126 id = Column(Integer, primary_key=True) # 主键 127 def __repr__(self): 128 return self.id 129 130 # Base.metadata.create_all(engine) # 创建表结构
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 '''此例表结构与堡垒机案例表结构有出入,灵活运用''' 5 from sqlalchemy.ext.declarative import declarative_base 6 from sqlalchemy import Column, Integer, String, Date, Table, Enum, DateTime 7 from sqlalchemy import ForeignKey, UniqueConstraint #联合唯一键 8 from sqlalchemy.orm import relationship 9 from sqlalchemy_utils import ChoiceType 10 Base = declarative_base() 11 12 Userprofile_To_Bindhost = Table( 13 'userprofile_to_bindhost', Base.metadata, 14 Column('userprofile_id', Integer, ForeignKey('userprofile.id')), 15 Column('bindhost_id', Integer, ForeignKey('bindhost.id')), 16 ) 17 18 Bindhost_To_Hostgroup = Table( 19 'bindhost_to_hostgroup', Base.metadata, 20 Column('hostgroup_id', Integer, ForeignKey('hostgroup.id')), 21 Column('bindhost_id', Integer, ForeignKey('bindhost.id')), 22 ) 23 24 Userprofile_To_Hostgroup = Table( 25 'userprofile_to_hostgroup', Base.metadata, 26 Column('hostgroup_id', Integer, ForeignKey('hostgroup.id')), 27 Column('userprofile_id', Integer, ForeignKey('userprofile.id')), 28 ) 29 30 class BindHost(Base): 31 ''' 32 host user host_group 33 192.168.1.1 web x 34 192.168.1.1 log x 35 ''' 36 __tablename__ = 'bindhost' 37 __table_args__ = (UniqueConstraint('host_id', 'remoteuser_id', name='_host_remoteuser_uc'),) # 联合唯一键 38 39 id = Column(Integer, primary_key=True) 40 host_id = Column(Integer, ForeignKey('host.id')) 41 remoteuser_id = Column(Integer, ForeignKey('remoteuser.id')) 42 43 hosts = relationship("Host", backref = 'bind_hosts') 44 remoteusers = relationship("RemoteUser", backref = 'bind_hosts') 45 46 audit_logs = relationship('AuditLog') 47 48 # hostgroups = relationship("HostGroup", secondary = Bindhost_To_Hostgroup, backref = 'bind_hosts') 49 def __repr__(self): #循环ip地址,远程用户 50 return "<%s - %s - %s>" %(self.hosts.hostname, 51 self.remoteusers.username, 52 self.id) 53 54 class Host(Base): 55 __tablename__ = 'host' 56 id = Column(Integer, primary_key=True) 57 hostname = Column(String(32), unique=True) 58 ip = Column(String(32), unique=True) 59 port = Column(Integer, default=22) 60 61 def __repr__(self): 62 return self.hostname 63 class HostGroup(Base): 64 __tablename__ = 'hostgroup' 65 id = Column(Integer, primary_key=True) 66 name = Column(String(32), unique=True) 67 68 bindhosts = relationship("BindHost", secondary = Bindhost_To_Hostgroup, backref = 'host_groups') 69 70 def __repr__(self): 71 return self.name 72 class RemoteUser(Base):#远程主机用户 73 __tablename__ = 'remoteuser' 74 75 '''用户名和密码多对多关系:要使用联合唯一''' 76 __table_args__ = (UniqueConstraint('auth_type', 'username', 'password', name='_authtype_usernm_pwd_uc'),) 77 78 id = Column(Integer, primary_key=True) 79 AuthTypes = [ 80 ('ssh-password', 'SSH/Password'), # 前者写入数据库,后者显示给用户 81 ('ssh-key', 'SSH/KEY'), 82 ] 83 auth_type = Column(ChoiceType(AuthTypes)) #从列表中选择类型 (需要实例化) 84 85 username = Column(String(32)) # 可以同用户名root,不同密码 (需要实例化) 86 password = Column(String(128)) # 可以同密码,不同用户名 (可以为NUll,根据类型选择实例化) 87 def __repr__(self): 88 return self.username 89 90 class UserProfile(Base):#堡垒机用户 91 __tablename__ = 'userprofile' 92 id = Column(Integer, primary_key=True) # 主键 93 username = Column(String(32), unique=True) #堡垒机用户名唯一 94 password = Column(String(128)) 95 96 '''secondary='',写表名''' 97 '''secondary=,写开发者起的名字''' 98 bindhosts = relationship("BindHost", secondary = Userprofile_To_Bindhost, backref = 'user_profiles') 99 hostgroups = relationship("HostGroup", secondary = Userprofile_To_Hostgroup, backref = 'user_profiles') 100 def __repr__(self): 101 return self.username #[] 102 class AuditLog(Base): #堡垒机操作日志 103 __tablename__ = 'audit_log' 104 id = Column(Integer, primary_key=True) 105 user_id = Column(Integer, ForeignKey('userprofile.id')) #需要实例化 106 bind_host_id = Column(Integer, ForeignKey('bindhost.id')) #需要实例化 107 108 '''枚举类型''' 109 # action_choices = [ 110 # (0, 'CMD'), 111 # (1, 'Login'), 112 # (2, 'Logout'), 113 # (3, 'GetFile'), 114 # (4, 'SendFile'), 115 # (5, 'Exception'), 116 # ] 117 action_choices2 = [ 118 ('cmd', 'CMD'), #python3默认是Unicode,不需要加u 119 ('login', 'Login'), 120 ('logout', 'Logout'), 121 # (3,'GetFile'), 122 # (4,'SendFile'), 123 # (5,'Exception'), 124 ] 125 action_type = Column(ChoiceType(action_choices2)) #需要实例化 126 127 # action_type = Column(String(64)) 128 cmd = Column(String(255)) #可选的,可为Null,类似密码密钥,如果不是cmd操作,login/logout:可以是NUll 129 date = Column(DateTime) #需要实例化 130 131 user_profile = relationship("UserProfile") 132 bind_host = relationship("BindHost")
modules目录:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 from conf import action_registers 5 from modules import utils 6 def help_msg(): 7 ''' 8 print help msgs 9 :return: 10 ''' 11 print("\033[31;1m参数个数输入不合法:\033[0m") 12 '''循环打印配置中的命令字典''' 13 print("\033[32;1m请从以下选项中选择操作。\033[0m") 14 for key in action_registers.actions: 15 print('\t', key) 16 17 def excute_from_command_line(argvs): 18 if len(argvs) < 2: #python fuck_run.py 列表长度 19 help_msg() 20 exit() 21 if argvs[1] not in action_registers.actions: 22 '''打印错误信息''' 23 utils.print_err("【%s】命令不存在"%argvs[1], quit = True) 24 # utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True) 25 #命令存在的情况 26 # action_registers.actions[argvs[1]]() #可以不传参数 27 action_registers.actions[argvs[1]](argvs[1:]) #可以不传参数,但可以加个非固定参数(参数列表),增加函数的可拓展性
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 from models import models 5 from modules.db_conn import engine,session 6 from modules.utils import print_err 7 8 def bind_hosts_filter(vals): 9 print('**>',vals.get('bind_hosts') ) 10 11 '''只能用于反查询''' 12 # bind_hosts2 = session.query(models.BindHost).filter(models.BindHost.hosts.hostname.in_(vals.get('bind_hosts') )).all() 13 14 #仅返回hostname,无法关联 15 # bind_hosts = session.query(models.Host).filter(models.Host.hostname.in_(vals.get('bind_hosts'))).all() 16 17 bind_hosts = session.query(models.BindHost).filter(models.Host.hostname.in_(vals.get('bind_hosts'))).all() 18 print("1:2对比:", bind_hosts) 19 # print("1:2对比:", bind_hosts) 20 if not bind_hosts: 21 # print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'),quit=False) 22 print_err("未分组",quit=False) 23 return bind_hosts 24 25 def user_profiles_filter(vals): 26 user_profiles = session.query(models.UserProfile).filter(models.UserProfile.username.in_(vals.get('user_profiles')) 27 ).all() 28 if not user_profiles: 29 print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'),quit=True) 30 return user_profiles
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 from sqlalchemy import create_engine 5 from conf import settings 6 7 from sqlalchemy.orm import sessionmaker 8 9 engine = create_engine(settings.ConnParams) 10 11 Session_class = sessionmaker(bind=engine)#创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例 12 session = Session_class()
1 # Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> 2 # 3 # This file is part of paramiko. 4 # 5 # Paramiko is free software; you can redistribute it and/or modify it under the 6 # terms of the GNU Lesser General Public License as published by the Free 7 # Software Foundation; either version 2.1 of the License, or (at your option) 8 # any later version. 9 # 10 # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 # details. 14 # 15 # You should have received a copy of the GNU Lesser General Public License 16 # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 19 20 import socket 21 import sys 22 from paramiko.py3compat import u 23 from models import models 24 import datetime 25 26 # windows does not have termios... 27 try: 28 import termios 29 import tty 30 has_termios = True 31 except ImportError: 32 has_termios = False 33 34 35 def interactive_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording): 36 if has_termios: 37 posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording) 38 else: 39 # windows_shell(chan) 40 windows_shell(chan, user_obj,bind_host_obj,cmd_caches,log_recording) 41 42 43 def posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording): 44 import select 45 46 oldtty = termios.tcgetattr(sys.stdin) 47 try: 48 tty.setraw(sys.stdin.fileno()) 49 tty.setcbreak(sys.stdin.fileno()) 50 chan.settimeout(0.0) 51 cmd = '' 52 53 tab_key = False 54 while True: 55 r, w, e = select.select([chan, sys.stdin], [], []) 56 if chan in r: 57 try: 58 x = u(chan.recv(1024)) 59 if tab_key: 60 if x not in ('\x07' , '\r\n'): #tab自动补全功能 61 print('tab:',x) 62 cmd += x 63 tab_key = False 64 if len(x) == 0: 65 sys.stdout.write('\r\n*** EOF\r\n') 66 break 67 sys.stdout.write(x) 68 sys.stdout.flush() 69 except socket.timeout: 70 pass 71 if sys.stdin in r: 72 x = sys.stdin.read(1) 73 if '\r' != x: 74 cmd +=x 75 else: 76 77 print('cmd->:',cmd) 78 79 '''CMD记录实例化''' 80 log_item = models.AuditLog(user_id=user_obj.id, 81 bind_host_id=bind_host_obj.id, 82 action_type='cmd', 83 cmd=cmd , #ls 84 date=datetime.datetime.now() 85 ) 86 cmd_caches.append(log_item) 87 cmd = '' 88 89 if len(cmd_caches)>=10: 90 # log_recording(cmd_caches) #实际有用的就是cmd_caches,提交到数据库 91 log_recording(user_obj,bind_host_obj,cmd_caches) 92 cmd_caches = [] 93 if '\t' == x: 94 tab_key = True 95 if len(x) == 0: 96 break 97 chan.send(x) 98 99 finally: 100 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) 101 102 103 # thanks to Mike Looijmans for this code 104 # def windows_shell(chan): 105 # import threading 106 # 107 # sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") 108 # 109 # def writeall(sock): 110 # while True: 111 # data = sock.recv(256) 112 # if not data: 113 # sys.stdout.write('\r\n*** EOF ***\r\n\r\n') 114 # sys.stdout.flush() 115 # break 116 # sys.stdout.write(data.decode()) 117 # sys.stdout.flush() 118 # 119 # writer = threading.Thread(target=writeall, args=(chan,)) 120 # writer.start() 121 # 122 # try: 123 # while True: 124 # d = sys.stdin.read(1) 125 # if not d: 126 # break 127 # chan.send(d) 128 # except EOFError: 129 # # user hit ^Z or F6 130 # pass 131 132 def windows_shell(chan, user_obj,bind_host_obj,cmd_caches,log_recording): 133 import threading 134 135 sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") 136 137 def writeall(sock): 138 while True: 139 data = sock.recv(256) #我们不用记录输出结果 140 if not data: 141 sys.stdout.write('\r\n*** EOF ***\r\n\r\n') 142 sys.stdout.flush() 143 break 144 sys.stdout.write(data.decode()) #屏幕输出命令结果 145 sys.stdout.flush() 146 147 writer = threading.Thread(target=writeall, args=(chan, )) 148 writer.start() 149 150 '''Windows版审计,自己实现》》''' 151 cmd = '' 152 try: 153 while True: 154 d = sys.stdin.read(1) 155 # print("D:", d) 156 if not d: 157 break 158 cmd += d 159 # print("cmd:", cmd) 160 # if d == '' or d == '\r' or d == '\r\n' or d == '\n': 161 if d == '\n' or d == '\r\n': #windows回车显示是空格,但验证后,回车不等于'',而是'\n' 162 # print("cmd_cmd:", cmd) 163 '''CMD记录实例化''' 164 log_item = models.AuditLog(user_id=user_obj.id, 165 bind_host_id=bind_host_obj.id, 166 action_type='cmd', 167 cmd=cmd, # ls 168 date=datetime.datetime.now() 169 ) 170 cmd_caches.append(log_item) 171 # print("cmd_caches:", cmd_caches, len(cmd_caches)) 172 cmd = '' 173 174 if len(cmd_caches) >= 10 or cmd == 'exit' or cmd == '^Z': 175 # log_recording(cmd_caches) #实际有用的就是cmd_caches,提交到数据库 176 log_recording(user_obj, bind_host_obj, cmd_caches) 177 cmd_caches = [] 178 if sys.stdout.write('*** EOF ***'): 179 log_recording(user_obj, bind_host_obj, cmd_caches) 180 181 chan.send(d) 182 except EOFError: 183 # user hit ^Z or F6 184 185 '''退出记录审计''' 186 log_recording(user_obj, bind_host_obj, cmd_caches) 187 188 pass
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 5 import base64 6 import getpass 7 import os 8 import socket 9 import sys 10 import traceback 11 from paramiko.py3compat import input 12 from models import models 13 import datetime 14 15 import paramiko 16 try: 17 import interactive 18 except ImportError: 19 from . import interactive #同级目录下用. 20 21 def ssh_login(user_obj,bind_host_obj,mysql_engine,log_recording): 22 # now, connect and use paramiko Client to negotiate SSH2 across the connection 23 try: 24 client = paramiko.SSHClient() 25 client.load_system_host_keys() 26 client.set_missing_host_key_policy(paramiko.WarningPolicy()) 27 print('*** Connecting...') 28 #client.connect(hostname, port, username, password) 29 30 '''需要修改''' 31 client.connect(bind_host_obj.hosts.ip, 32 bind_host_obj.hosts.port, 33 bind_host_obj.remoteusers.username, 34 bind_host_obj.remoteusers.password, 35 timeout=30) 36 37 cmd_caches = [] 38 chan = client.invoke_shell() 39 print(repr(client.get_transport())) 40 print('*** Here we go!\n') 41 42 '''日志模块:login记录实例化''' 43 '''interactive.py:CMD记录实例化''' 44 cmd_caches.append(models.AuditLog(user_id=user_obj.id, #外键实例化 45 bind_host_id=bind_host_obj.id, #外键实例化 46 action_type='login', #cmd是Null 47 date=datetime.datetime.now() 48 )) 49 log_recording(user_obj,bind_host_obj,cmd_caches) 50 interactive.interactive_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording) 51 chan.close() 52 client.close() 53 54 except Exception as e: 55 print('*** Caught exception: %s: %s' % (e.__class__, e)) 56 traceback.print_exc() 57 try: 58 client.close() 59 except: 60 pass 61 sys.exit(1)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 5 import yaml 6 '''约定俗成''' 7 try: 8 from yaml import CLoader as Loader, CDumper as Dumper 9 except ImportError: 10 from yaml import Loader, Dumper 11 12 def print_err(msg, quit=False): 13 output = "\033[31;1mError:%s\033[0m" %msg 14 if quit: 15 exit(output) 16 else: 17 print(output) 18 19 def yaml_parser(yml_filename): 20 ''' 21 load yaml file and return 22 :param yml_filename: 23 :return: 24 ''' 25 ''' 26 #未创建hostgroup和bindhost,却给他们关联数据,会报错 27 #但会成功创建userprofile表 28 #'gbk' codec can't decode byte 0xaa in position 4: illegal multibyte sequence? 29 # yml 不支持添加中文注释 30 ''' 31 # yml_filename = "%s/%s.yml" % (settings.StateFileBaseDir,yml_filename) 32 try: 33 print("读取文件") 34 yaml_file = open(yml_filename, 'r') 35 data = yaml.load(yaml_file) 36 print("返回字典数据") 37 return data 38 except Exception as e: 39 print_err(e)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 ''' 5 表示数据库已有此数据 6 [SQL: INSERT INTO hostgroup (name) VALUES (%(name)s)] 7 [parameters: {'name': 'web_devteam'}] 8 (Background on this error at: http://sqlalche.me/e/gkpj) 9 ''' 10 11 from models import models 12 from modules.db_conn import engine, session 13 from modules import utils, ssh_login, common_filters 14 15 from models import models 16 17 def welcome_msg(user): 18 WELCOME_MSG = '''\033[32;1m 19 ------------- %s登录成功 ------------- 20 \033[0m''' % user.username 21 print(WELCOME_MSG) 22 23 def auth(): 24 ''' 25 do the user login authentication 26 :return: 27 ''' 28 count = 0 29 while count < 3: 30 username = input("\033[32;1m堡垒机用户名:\033[0m").strip() 31 if len(username) == 0: continue 32 password = input("\033[32;1m堡垒机登录密码:\033[0m").strip() 33 if len(password) == 0: continue 34 user_obj = session.query(models.UserProfile).filter( 35 models.UserProfile.username==username, 36 models.UserProfile.password==password).first() 37 #如果数据库存在用户账户,返回用户对象 38 print("User_obj>>", user_obj) 39 if user_obj: 40 return user_obj 41 else: 42 # print("错误的用户名或密码,您还有%s次输入机会。" % (3 - (count + 1))) 43 print("错误的用户名或密码,您还有%s次输入机会。" % (3 - count - 1)) 44 count += 1 45 else: 46 utils.print_err("登录错误次数过多,请稍后再试。") 47 48 def hand_modify(argvs): 49 ''' 50 格式:hand_modify -f 功能函数(create_remoteusers) modify_id modify_name modified_name 51 :param argvs: 52 :return: 53 ''' 54 if '-f' in argvs: 55 modify_table = argvs[argvs.index("-f") +1 ] 56 modify_id = int(argvs[argvs.index("-f") + 2]) 57 modify_name = argvs[argvs.index("-f") + 3] 58 modified_name = argvs[argvs.index("-f") + 4] 59 print("修改》》", modify_id, modify_table, modified_name, modify_name) 60 if modify_table == 'create_hosts': 61 pass 62 # from models.models import Host 63 # session.query(Host).filter(Host.modify_name) 64 elif modify_table == 'create_remoteusers': 65 # from models import models 66 print("modify_id:", modify_id) 67 obj = session.query(models.RemoteUser).filter( 68 models.RemoteUser.id==modify_id).first() 69 # print("obj:", obj) 70 # print("obj:") 71 if modify_name == 'password': 72 obj.password = modified_name 73 elif modify_name == 'auth_type': 74 print("auth_type>>>\n", obj.auth_type) 75 obj.auth_type = modified_name 76 session.add(obj) 77 elif modify_table == 'create_users': 78 from models.models import UserProfile 79 elif modify_table == 'create_groups': 80 from models.models import HostGroup 81 else: 82 utils.print_err("invalid usage, should be:\n" 83 "hand_modify -f 功能函数 modify_id modify_name modified_name",quit=True) 84 print("hostgroup正在创建……") 85 session.commit() 86 print("hostgroup创建成功!") 87 88 # source = utils.yaml_parser(modify_table) 89 # print("modify_file - source:", source) 90 # if source: 91 # for key,val in source.items(): 92 # print("key,val:", key,val) 93 # 94 # #判断key是否存在,存在,退出 95 # hostgroup = session.query(models.HostGroup).filter( 96 # models.HostGroup.name==key).first() 97 # if hostgroup: 98 # utils.print_err("该主机组已存在,不可重复添加。", quit = True) 99 # obj = models.HostGroup(name=key) #实例化主机组 100 # print("obj:", obj) 101 # session.add(obj) 102 # print("hostgroup正在创建……") 103 # session.commit() 104 # print("hostgroup创建成功!") 105 106 def stop_server(): 107 pass 108 109 def log_recording(user_obj,bind_host_obj,logs): 110 ''' 111 flush user operations on remote host into DB 112 :param user_obj: 113 :param bind_host_obj: 114 :param logs: list format [logItem1,logItem2,...] 115 :return: 116 ''' 117 print("\033[41;1m--logs:\033[0m",logs) 118 session.add_all(logs) 119 session.commit() 120 121 def start_session(argvs): #登录认证,开启堡垒机会话操作远程主机 122 print('堡垒机会话登录认证!') 123 user = auth() 124 '''返回用户登录对象user_obj''' 125 if user: 126 welcome_msg(user) #类user. #登录成功 127 '''打印主机分组信息''' 128 print("user.bindhosts:", user.bindhosts) #hosts.ip,remoteusers.username,remoteusers.auth_type 129 print(user.hostgroups) #name 130 exit_flag = False 131 while not exit_flag: 132 #未分组的主机列表 133 if user.bindhosts: 134 print('\033[32;1mz.未分组主机 (%s)\033[0m' %len(user.bindhosts)) 135 #分组的主机列表 136 for index, group in enumerate(user.hostgroups): 137 print('\033[32;1m%s.\t%s (%s)\033[0m' % 138 (index, group.name, len(group.bindhosts))) 139 print("选择编号查看主机列表》》") 140 choice = input("[%s]:" % user.username).strip() 141 if len(choice) == 0: continue 142 if choice == 'z': 143 print("------ 分组: 未分组主机 ------") 144 for index, bind_u_host in enumerate(user.bindhosts): #user.bindhosts是一个列表 145 print(" %s.\t%s@%s(%s)" % (index, 146 bind_u_host.remoteusers.username, #远程主机用户名 147 bind_u_host.hosts.hostname, #远程主机名 148 bind_u_host.hosts.ip, #远程主机ip地址 149 )) 150 print("----------- END -----------") 151 152 while not exit_flag: 153 user_option = input("[(b)返回, (q)退出, 选择远程主机进行登录]:").strip() 154 if len(user_option) == 0: continue 155 if user_option == 'b': break 156 if user_option == 'q': exit_flag = True 157 if user_option.isdigit(): 158 user_option = int(user_option) 159 # if type(choice) == str: 160 # choice = int(choice) 161 if user_option < len(user.bindhosts): 162 print('host:', user.bindhosts[user_option]) 163 '''日志表还没创建''' 164 print('audit log:', user.bindhosts[user_option].audit_logs) 165 '''接下来,登录远程主机操作''' 166 ssh_login.ssh_login(user, 167 user.bindhosts[user_option], 168 session, 169 log_recording) # 这里只是传函数地址过去,并没有执行函数 170 else: 171 print("没有这样的主机编号...") 172 173 elif choice.isdigit(): #输入的是主机分组编号 174 choice = int(choice) 175 if choice < len(user.hostgroups): 176 print("------ 分组: %s ------" % user.hostgroups[choice].name ) #第choice组 177 for index,bind_host in enumerate(user.hostgroups[choice].bindhosts): 178 print(" %s.\t%s@%s(%s)"%(index, 179 bind_host.remoteusers.username, 180 bind_host.hosts.hostname, 181 bind_host.hosts.ip, 182 )) 183 print("----------- END -----------" ) 184 185 #host selection 186 '''选择远程主机进行登录''' 187 while not exit_flag: 188 user_option = input("[(b)返回, (q)退出, 选择远程主机进行登录]:").strip() 189 if len(user_option)==0:continue 190 if user_option == 'b':break 191 if user_option == 'q':exit_flag=True 192 if user_option.isdigit(): 193 user_option = int(user_option) 194 # if type(choice) == str: 195 # choice = int(choice) 196 if user_option < len(user.hostgroups[choice].bindhosts): 197 print('host:',user.hostgroups[choice].bindhosts[user_option]) 198 '''日志表还没创建''' 199 print('audit log:',user.hostgroups[choice].bindhosts[user_option].audit_logs) 200 '''接下来,登录远程主机操作''' 201 ssh_login.ssh_login(user, 202 user.hostgroups[choice].bindhosts[user_option], 203 session, 204 log_recording) #这里只是传函数地址过去,并没有执行函数 205 else: 206 print("没有这样的主机编号...") 207 else: 208 print("没有这样的分组编号...") 209 210 '''后来的函数往上写''' 211 def create_bindhosts(argvs): 212 ''' 213 create bind hosts 214 :param argvs: 215 :return: 216 ''' 217 if '-f' in argvs: 218 bindhosts_file = argvs[argvs.index("-f") +1 ] 219 else: 220 utils.print_err("invalid usage, should be:\ncreate_hosts -f <the new bindhosts file>",quit=True) 221 source = utils.yaml_parser(bindhosts_file) 222 print("create_bindhosts - source:", source) 223 if source: 224 for key,val in source.items(): 225 print("key,val:\n", key,val) 226 host_obj = session.query(models.Host).filter(models.Host.hostname==val.get('hostname')).first() 227 assert host_obj #断言,host_obj必须存在 228 229 # print("get:\n",val.get('remote_users')) 230 # print("val:\n",val['remote_users']) #一模一样 231 # print("val2:\n",val['hostname']) 232 233 for item in val['remote_users']: #是循环列表,而不再是循环字典了new_bindhosts.yml 234 # for item in val.get('remote_users'): 235 print("item:", item ) #列表第一个元素[0]是字典类型 = 字典dict:item{} 236 assert item.get('auth_type') #item{} 237 if item.get('auth_type') == 'ssh-password': 238 remoteuser_obj = session.query(models.RemoteUser).filter( 239 models.RemoteUser.username==item.get('username'), 240 models.RemoteUser.password==item.get('password') 241 ).first() 242 else: 243 remoteuser_obj = session.query(models.RemoteUser).filter( 244 models.RemoteUser.username==item.get('username'), 245 models.RemoteUser.auth_type==item.get('auth_type'), 246 ).first() 247 if not remoteuser_obj: 248 utils.print_err("RemoteUser obj %s does not exist." % item, quit=True) 249 bindhost_obj = models.BindHost(host_id=host_obj.id,remoteuser_id=remoteuser_obj.id) 250 session.add(bindhost_obj) #还没完,还有两个需要一起绑定了,再提交 251 252 #for groups this host binds to 253 #判断source[key]与val是否相同 #是相同的 254 # print("source[key]:\n", source[key]) 255 # print("val:\n", val) 256 if source[key].get('groups'): 257 #获取HostGroup类下这个source[key].get('groups')列表中的任意一个,key下面多个元素,以列表形式存放 258 group_objs = session.query(models.HostGroup).filter( 259 models.HostGroup.name.in_(source[key].get('groups') )).all() 260 assert group_objs 261 print('group_objs>>', group_objs) #列表:[log_devteam] 262 bindhost_obj.host_groups = group_objs #反查,第三张表建立多对多关系,需要列表赋值,同Author与Book 263 #for user_profiles this host binds to 264 '''同上''' 265 if source[key].get('user_profiles'): 266 userprofile_objs = session.query(models.UserProfile).filter( 267 models.UserProfile.username.in_(source[key].get('user_profiles') )).all() 268 # print("userprofile_objs1:", userprofile_objs) # [] 269 assert userprofile_objs 270 print("userprofile_objs:",userprofile_objs) #[] 271 bindhost_obj.user_profiles = userprofile_objs #从BindHost反查堡垒机用户UserProfile 272 print(bindhost_obj) 273 print("bindhost正在创建……") 274 session.commit() 275 print("bindhost创建成功!") 276 277 def create_groups(argvs): 278 ''' 279 create groups 280 :param argvs: 281 :return: 282 ''' 283 if '-f' in argvs: 284 group_file = argvs[argvs.index("-f") +1 ] 285 else: 286 utils.print_err("invalid usage, should be:\ncreategroups -f <the new groups file>",quit=True) 287 source = utils.yaml_parser(group_file) 288 print("create_groups - source:", source) 289 if source: 290 for key,val in source.items(): 291 print(key,val) 292 obj = models.HostGroup(name=key) #实例化主机组 293 if val.get('bind_hosts'): 294 bind_hosts = common_filters.bind_hosts_filter(val) 295 obj.bind_hosts = bind_hosts 296 297 if val.get('user_profiles'): #add_groups2.yml 298 user_profiles = common_filters.user_profiles_filter(val) 299 obj.user_profiles = user_profiles 300 session.add(obj) 301 print("hostgroup正在创建……") 302 session.commit() 303 print("hostgroup创建成功!") 304 305 def create_users(argvs): #堡垒机用户创建 306 if '-f' in argvs: 307 user_file = argvs[argvs.index("-f") +1 ] 308 else: 309 utils.print_err("invalid usage, should be:\ncreate_users -f <the new users file>", quit=True) 310 source = utils.yaml_parser(user_file) 311 print("create_users - source:", source) 312 if source: 313 for key,val in source.items(): 314 print("key, val:", key,val) 315 print("key应该是类似:\nalex\nval应该是类似:\npassword: alex123\n" 316 # "groups:- web_servers\t- db_servers") 317 "groups:web_servers\tdb_servers") # - 被自动切除 318 obj = models.UserProfile(username=key, password=val.get('password')) #实例化堡垒机用户 319 #因为我是先创建的堡垒机用户,组还没有创建,所以第三张表Userprofile_To_Hostgroup无法关联 320 321 #堡垒机用户alex有主机组的情况下 322 if val.get('groups'): 323 groups_obj = session.query(models.HostGroup).filter( 324 models.HostGroup.name.in_(val.get('groups'))).all() 325 #获取HostGroup下所有主机组名,并判断是否在groups[]中,如果在,关联,不在报错 326 # 即判断groups下主机组名是否存在主机组表中,如果在,全部获取出来 327 print("所有主机组名 - groups_obj:", groups_obj)#[mysql_devteam, web_devteam] 328 if not groups_obj: #[]空 329 utils.print_err("none of [%s] exist in group table." % val.get('groups'), quit=True) 330 # 本次明显找不到主机组名,还没创建呢,怎么关联 331 obj.hostgroups = groups_obj #[]全部添加进去 332 print("主机组名不存在,直接退出,应该不会执行到这里。userprofile数据也不会被创建") 333 334 # 堡垒机用户alex有bindhost的情况下 335 if val.get('bind_hosts'): 336 bind_hosts = common_filters.bind_hosts_filter(val) 337 print("bind_hosts:", bind_hosts) 338 print("obj.bindhosts:", obj.bindhosts) 339 obj.bindhosts = bind_hosts 340 print("堡垒机用户obj:", obj) 341 342 session.add(obj) 343 print("user正在创建……") 344 session.commit() 345 print("user创建成功!") 346 347 def create_remoteusers(argvs):# 创建远程主机用户账户 348 if '-f' in argvs: 349 remoteusers_file = argvs[argvs.index("-f") +1 ] 350 else: 351 utils.print_err("invalid usage, should be:\ncreate_remoteusers -f <the new remoteusers file>", quit=True) 352 source = utils.yaml_parser(remoteusers_file) 353 print("create_remoteusers - source>>", source) # 已经把文件内容转成了字典格式 354 if source: 355 for key,val in source.items(): 356 print("key,val:", key, val) 357 obj = models.RemoteUser(username=val.get('username'),auth_type=val.get('auth_type'),password=val.get('password')) 358 # obj = models.RemoteUser(username=val.get('username'),auth_type=val.get('auth_type'), 359 # password=val.get('password') or '已设定密钥登录') 360 session.add(obj) #username 361 print("remoteusers OBJ:username>>>>>", obj) 362 print("remoteusers正在创建……") 363 session.commit() 364 print("remoteusers创建成功!") 365 366 def create_hosts(argvs):# 创建远程主机信息 367 if '-f' in argvs: 368 hosts_file = argvs[argvs.index("-f") +1 ] #-f hosts.yml主机信息字典文件 369 else: 370 utils.print_err("不合法的命令参数,应该这样写:\ncreate_hosts -f 新主机信息文件名", quit=True) 371 source = utils.yaml_parser(hosts_file) #把host文件名传过去 错误提示:因为判断else情况下,hosts_file不存在 372 print("create_hosts - source>>", source) #已经把文件内容转成了字典格式 373 if source: 374 for key,val in source.items(): #循环字典内容 375 print("key,val:", key,val) 376 #'''实例化远程主机''' 377 host_obj = models.Host(hostname=key, ip=val.get('ip_addr'), port=val.get('port') or 22) 378 print("实例化对象host_obj:", host_obj) #Host return - key: server1 379 session.add(host_obj) 380 print("hosts正在创建……") 381 session.commit() 382 print("hosts创建成功!") 383 else: 384 utils.print_err("host主机文件为空", quit = True) 385 386 def syncdb(argvs):#传来的是一个列表,所以不需要用非固定参数来了 387 # def syncdb(*args): #非固定参数用法 388 print("正在进行表结构创建....") 389 models.Base.metadata.create_all(engine) # 创建所有表结构 390 print("\033[32;1m表结构创建完成。\033[0m")
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Uson 4 from models import models 5 from modules.db_conn import engine, session 6 from modules import utils, ssh_login, common_filters 7 8 def add_groups(argvs): 9 ''' 10 add groups 11 :param argvs: 12 :return: 13 ''' 14 if '-f' in argvs: 15 group_file = argvs[argvs.index("-f") +1 ] 16 else: 17 utils.print_err("invalid usage, should be:\nadd_groups -f <the new groups file>",quit=True) 18 source = utils.yaml_parser(group_file) 19 print("add_groups - source:", source) 20 if source: 21 for key,val in source.items(): 22 print(key,val) 23 24 #判断key是否存在,存在,退出 25 hostgroup = session.query(models.HostGroup).filter( 26 models.HostGroup.name==key).first() 27 if hostgroup: 28 utils.print_err("该主机组已存在,不可重复添加。", quit = True) 29 obj = models.HostGroup(name=key) #实例化主机组 30 print("obj:", obj) 31 # if val.get('bind_hosts'): 32 # bind_hosts = common_filters.bind_hosts_filter(val) 33 # obj.bind_hosts = bind_hosts 34 # 35 # if val.get('user_profiles'): 36 # user_profiles = common_filters.user_profiles_filter(val) 37 # obj.user_profiles = user_profiles 38 session.add(obj) 39 print("hostgroup正在创建……") 40 session.commit() 41 print("hostgroup创建成功!")
结构分析构造:
1 阶段分析》》》 2 表结构1:models-v1.py 3 初始化表结构,确定之间的关系(多对多,一对多,一对一)及数量(几个类class) 4 涉及用户名密码联合唯一 5 实现主机到远程用户的多对多关联:models-v1-1.py 6 问题来了:主机和主机组实现多对多关联,如果我只想给alex(属于bj_group)192.168.1.3下的web访问权限 7 但是现在,多对多关联,bj_group下的用户对应多个主机下的多个远程用户登录信息 8 所以,主机和主机组不能直接关联 9 host host_group user 10 192.168.1.1 bj_group root 11 192.168.1.3 bj_group web 12 192.168.1.3 bj_group log 13 192.168.1.2 sh_group mysql 14 15 表结构4:models-v2.py 16 删除:主机到远程用户的多对多关联 17 一个主机ip对应多个不同账户name_pwd 18 一个账户name_pwd对应多个主机ip 19 一个主机ip对应多个主机组group 20 一个主机组group对应多个主机ip 21 双向一对多,就是多对多(三者联合唯一) 22 name pwd ip host_group 23 root abc 1.2.3.4 bj_group 24 mysql acd 1.2.3.4 sh_group 25 web aef 1.6.3.4 bj_group 26 root abc 1.4.7.7 sh_group 27 28 表结构5:models-v3.py 29 '''注意:外键关联:实现堡垒机用户的多对多关联(第三张表)''' 30 '''问题来了:每登录一台主机,都要带着一个主机分组,那么未分组主机就不能登录 31 未分组主机【10】 是不在该用户权限下的广州分组中的其中10个主机 32 北京分组【40】 33 上海分组【100】 34 --------------- 35 广州分组【200】 36 南京分组【30】 37 ''' 38 39 表结构5-5:models-v4.py (4) 40 Bindhost 与 hostgroup多对多关联 41 42 表结构6:models-v5.py (5)= models.py 43 堡垒机用户userprofile与hostgroup多对多关联
完整代码下载:暂无