一、前景介绍
到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒机所具备的功能属性中的其中一项而已,下面我就给大家介绍一下堡垒机的重要性,以帮助大家参考自己公司的业务是否需要部署堡垒机。
堡垒机有以下两个至关重要的功能:
(一)权限管理
当你公司的服务器变的越来越多后,需要操作这些服务器的人就肯定不只是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操作业务系统,如果权限分配不当就会存在很大的安全风险,举几个场景例子:
-
设想你们公司有300台Linux服务器,A开发人员需要登录其中5台WEB服务器查看日志或进行问题追踪等事务,同时对另外10台hadoop服务器有root权限,在有300台服务器规模的网络中,按常理来讲你是已经使用了ldap权限统一认证的,你如何使这个开发人员只能以普通用户的身份登录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 没任何权限
- 分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限
设计表结构
ssh公钥登录过程
使用密码登录,每次都必须输入密码,非常麻烦。好在SSH还提供了公钥登录,可以省去输入密码的步骤。
所谓"公钥登录",原理很简单,就是用户将自己的公钥储存在远程主机上。登录的时候,远程主机会向用户发送一段随机字符串,用户用自己的私钥加密后,再发回来。远程主机用事先储存的公钥进行解密,如果成功,就证明用户是可信的,直接允许登录shell,不再要求密码。
这种方法要求用户必须提供自己的公钥。如果没有现成的,可以直接用ssh-keygen生成一个:
$ ssh-keygen
运行上面的命令以后,系统会出现一系列提示,可以一路回车。其中有一个问题是,要不要对私钥设置口令(passphrase),如果担心私钥的安全,这里可以设置一个。
运行结束以后,在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥。
这时再输入下面的命令,将公钥传送到远程主机host上面:
$ ssh-copy-id user@host
好了,从此你再登录,就不需要输入密码了。
s_it.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import os,sys
2 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
3 print(BASE_DIR)
4 sys.path.append(BASE_DIR)
5 if __name__ == '__main__':
6 from modules.actions import excute_from_command_line
7 excute_from_command_line(sys.argv)
action_registers.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from modules import views
2 actions = {
3 'start_session': views.start_session,
4 # 'stop': views.stop_server,
5 'syncdb': views.syncdb,
6 'create_users': views.create_users,
7 'create_groups': views.create_groups,
8 'create_hosts': views.create_hosts,
9 'create_bindhosts': views.create_bindhosts,
10 'create_remoteusers': views.create_remoteusers,
11 }
settiongs.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 Conn_Params = "mysql+pymysql://root:123456@192.168.211.129/JBSdb?charset=utf8"
models.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from sqlalchemy import Table, Column, Integer,String,Enum,DATE, ForeignKey,UniqueConstraint #Enum枚举
2 from sqlalchemy.orm import relationship
3 from sqlalchemy.ext.declarative import declarative_base
4 from sqlalchemy_utils import ChoiceType,PasswordType #PasswordType 可以md5,但不好使
5 from sqlalchemy import create_engine
6 # from sqlalchemy.orm import sessionmaker
7 Base = declarative_base() #基类
8 user_m2m_bindhost = Table('user_m2m_bindhost', Base.metadata,
9 Column('userprofile_id',Integer,ForeignKey('user_profile.id')),
10 Column('bindhost_id',Integer,ForeignKey('bind_host.id')),
11 )
12 bindhost_m2m_hostgroup = Table('bindhost_m2m_hostgroup', Base.metadata,
13 Column('bindhost_id',Integer,ForeignKey('bind_host.id')),
14 Column('hostgroup_id',Integer,ForeignKey('host_group.id')),
15 )
16 host_m2m_remoteuser = Table('host_m2m_remoteuser', Base.metadata,
17 Column('host_id',Integer,ForeignKey('host.id')),
18 Column('remoteuser_id',Integer,ForeignKey('remote_user.id')),
19 )
20 user_m2m_hostgroup = Table('userprofile_m2m_hostgroup', Base.metadata,
21 Column('userprofile_id',Integer,ForeignKey('user_profile.id')),
22 Column('hostgroup_id',Integer,ForeignKey('host_group.id')),
23 )
24 class Host(Base):
25 __tablename__="host"
26 id=Column(Integer,primary_key=True)
27 hostname=Column(String(64),unique=True)
28 ip=Column(String(64),unique=True)
29 port=Column(Integer,default=22)
30 remote_users=relationship("RemoteUser",secondary=host_m2m_remoteuser,backref="hosts")
31 def __repr__(self):
32 return self.hostname
33 class HostGroup(Base):
34 __tablename__ = "host_group"
35 id = Column(Integer, primary_key=True)
36 name = Column(String(64), unique=True)
37 bind_hosts=relationship("BindHost",secondary="bindhost_m2m_hostgroup",backref="host_groups")
38 def __repr__(self):
39 return self.name
40 class RemoteUser(Base):
41 __tablename__ = "remote_user"
42 __table_args__ = (UniqueConstraint('auth_type', 'username', 'password', name='_user_passwd_uc'),)#联合唯一
43 AuthTypes = [
44 ('ssh-password', 'SSH/Password'),#第一个是真正存到数据库的,第二个是显示给我们看的
45 ('ssh-key', 'SSH/KEY'),
46 ]
47 id = Column(Integer, primary_key=True)
48 auth_type = Column(ChoiceType(AuthTypes))
49 username = Column(String(32), nullable=False)
50 password=Column(String(128)) #没有md5
51 def __repr__(self):
52 return self.username
53 class BindHost(Base):
54 '''
55 192.168.1.11 web bj_group
56 192.168.1.11 mysql sh_group
57 '''
58 __tablename__ = "bind_host"
59 __table_args__ = (UniqueConstraint('host_id', 'remoteuser_id', name='host_remoteuser_uc'),)
60 id = Column(Integer, primary_key=True)
61 host_id = Column(Integer, ForeignKey('host.id'))
62 #group_id = Column(Integer, ForeignKey('group.id'))
63 remoteuser_id = Column(Integer, ForeignKey('remote_user.id'))
64 host=relationship("Host",backref="binf_hosts")
65 #host_group=relationship("HostGroup",backref="bind_hosts")
66 remote_user=relationship("RemoteUser",backref="binf_hosts")
67 def __repr__(self):
68 return "<%s -- %s >"%(self.host.ip,
69 self.remote_user.username,
70 )
71 class UserProfile(Base):
72 __tablename__ = "user_profile"
73 id = Column(Integer, primary_key=True)
74 username = Column(String(32), unique=True,nullable=False)
75 password = Column(String(128))
76 bind_hosts=relationship("BindHost",secondary="user_m2m_bindhost",backref="user_profiles")
77 host_groups=relationship("HostGroup",secondary="userprofile_m2m_hostgroup",backref="user_profiles")
78 def __repr__(self):
79 return self.username
80 # class AuditLog(Base):
81 # pass
models_v2.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from sqlalchemy import Table, Column, Integer,String,Enum,DATE, ForeignKey,UniqueConstraint #Enum枚举
2 from sqlalchemy.orm import relationship
3 from sqlalchemy.ext.declarative import declarative_base
4 from sqlalchemy_utils import ChoiceType,PasswordType #PasswordType 可以md5,但不好使
5 # from sqlalchemy import create_engine
6 # from sqlalchemy.orm import sessionmaker
7 Base = declarative_base() #基类
8 user_m2m_bindhost = Table('user_m2m_bindhost', Base.metadata,
9 Column('userprofile_id',Integer,ForeignKey('user_profile.id')),
10 Column('bindhost_id',Integer,ForeignKey('bind_host.id')),
11 )
12 host_m2m_remoteuser = Table('host_m2m_remoteuser', Base.metadata,
13 Column('host_id',Integer,ForeignKey('host.id')),
14 Column('remoteuser_id',Integer,ForeignKey('remote_user.id')),
15 )
16 class Host(Base):
17 __tablename__="host"
18 id=Column(Integer,primary_key=True)
19 hostname=Column(String(64),unique=True)
20 ip=Column(String(64),unique=True)
21 port=Column(Integer,default=22)
22 remote_users=relationship("RemoteUser",sencondary=host_m2m_remoteuser,backref="hosts")
23 def __repr__(self):
24 return self.hostname
25 class HostGroup(Base):
26 __tablename__ = "host_group"
27 id = Column(Integer, primary_key=True)
28 name = Column(String(64), unique=True)
29 def __repr__(self):
30 return self.name
31 class RemoteUser(Base):
32 __tablename__ = "romote_user"
33 __table_args__ = (UniqueConstraint('auth_type', 'username', 'password', name='_user_passwd_uc'),)#联合唯一
34 AuthTypes = [
35 ('ssh-passwd', 'SSH/Password'),#第一个是真正存到数据库的,第二个是显示给我们看的
36 ('ssh-key', 'SSH/KEY'),
37 ]
38 id = Column(Integer, primary_key=True)
39 auth_type = Column(ChoiceType(AuthTypes))
40 username = Column(String(32), unique=True)
41 password=Column(String(128)) #没有md5
42 def __repr__(self):
43 return self.username
44 class BindHost(Base):
45 '''
46 192.168.1.11 web bj_group
47 192.168.1.11 mysql sh_group
48 '''
49 __tablename__ = "bind_host"
50 __table_args__ = (UniqueConstraint('host_id', 'group_id', 'remoteuser_id', name='host_group_remoteuser_uc'),)
51 id = Column(Integer, primary_key=True)
52 host_id = Column(Integer, ForeignKey('host.id'))
53 group_id = Column(Integer, ForeignKey('group.id'))
54 remoteuser_id = Column(Integer, ForeignKey('remote_user.id'))
55 host=relationship("Host",backref="binf_hosts")
56 host_group=relationship("HostGroup",backref="binf_hosts")
57 remote_user=relationship("RemoteUser",backref="binf_hosts")
58 def __repr__(self):
59 return "<%s -- %s -- %s>"%(self.host.ip,
60 self.remote_user.username,
61 self.host_group.name)
62 class UserProfile(Base):
63 __tablename__ = "user_profile"
64 id = Column(Integer, primary_key=True)
65 username = Column(String(32), unique=True)
66 password = Column(String(128))
67 bind_hosts=relationship("BindHost",secondary="user_m2m_bindhost",backref="user_profiles")
68 def __repr__(self):
69 return self.username
70 class AuditLog(Base):
71 pass
actions.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from conf import settings
2 from conf import action_registers
3 from modules import utils
4 def help_msg():
5 '''
6 print help msgs
7 :return:
8 '''
9 print("\033[31;1mAvailable commands:\033[0m")
10 for key in action_registers.actions:
11 print("\t",key)
12 def excute_from_command_line(argvs):
13 if len(argvs) < 2:
14 help_msg()
15 exit()
16 if argvs[1] not in action_registers.actions:
17 utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True)
18 action_registers.actions[argvs[1]](argvs[1:])
common_filters.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from models import models
2 from modules.db_conn import engine,session
3 from modules.utils import print_err
4 def bind_hosts_filter(vals):
5 print('**>',vals.get('bind_hosts') )
6 bind_hosts = session.query(models.BindHost).filter(models.Host.hostname.in_(vals.get('bind_hosts'))).all()
7 if not bind_hosts:
8 print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'),quit=True)
9 return bind_hosts
10 def user_profiles_filter(vals):
11 user_profiles = session.query(models.UserProfile).filter(models.UserProfile.username.in_(vals.get('user_profiles'))
12 ).all()
13 if not user_profiles:
14 print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'),quit=True)
15 return user_profiles
db_conn.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from sqlalchemy import create_engine,Table
2 from sqlalchemy.orm import sessionmaker
3 from conf import settings
4 engine = create_engine(settings.Conn_Params)
5 #engine = create_engine(settings.DB_CONN,echo=True)
6 SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
7 session = SessionCls()
interactive.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import socket
2 import sys
3 from paramiko.py3compat import u
4 from models import models
5 import datetime
6 # windows does not have termios...
7 try:
8 import termios
9 import tty
10 has_termios = True
11 except ImportError:
12 has_termios = False
13 def interactive_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording):
14 if has_termios:
15 posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording)
16 else:
17 windows_shell(chan)
18 def posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording):
19 import select
20 oldtty = termios.tcgetattr(sys.stdin)
21 try:
22 tty.setraw(sys.stdin.fileno())
23 tty.setcbreak(sys.stdin.fileno())
24 chan.settimeout(0.0)
25 cmd = ''
26 tab_key = False
27 while True:
28 r, w, e = select.select([chan, sys.stdin], [], [])
29 if chan in r:
30 try:
31 x = u(chan.recv(1024))
32 if tab_key:
33 if x not in ('\x07' , '\r\n'):
34 #print('tab:',x)
35 cmd += x
36 tab_key = False
37 if len(x) == 0:
38 sys.stdout.write('\r\n*** EOF\r\n')
39 break
40 sys.stdout.write(x)
41 sys.stdout.flush()
42 except socket.timeout:
43 pass
44 if sys.stdin in r:
45 x = sys.stdin.read(1)
46 if '\r' != x:
47 cmd +=x
48 else:
49 print('cmd->:',cmd)
50 log_item = models.AuditLog(user_id=user_obj.id,
51 bind_host_id=bind_host_obj.id,
52 action_type='cmd',
53 cmd=cmd ,
54 date=datetime.datetime.now()
55 )
56 cmd_caches.append(log_item)
57 cmd = ''
58 if len(cmd_caches)>=10:
59 log_recording(user_obj,bind_host_obj,cmd_caches)
60 cmd_caches = []
61 if '\t' == x:
62 tab_key = True
63 if len(x) == 0:
64 break
65 chan.send(x)
66 finally:
67 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
68 # thanks to Mike Looijmans for this code
69 def windows_shell(chan):
70 import threading
71 sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
72 def writeall(sock):
73 while True:
74 data = sock.recv(256)
75 if not data:
76 sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
77 sys.stdout.flush()
78 break
79 sys.stdout.write(data)
80 sys.stdout.flush()
81 writer = threading.Thread(target=writeall, args=(chan,))
82 writer.start()
83 try:
84 while True:
85 d = sys.stdin.read(1)
86 if not d:
87 break
88 chan.send(d)
89 except EOFError:
90 # user hit ^Z or F6
91 pass
ssh_login.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import base64
2 import getpass
3 import os
4 import socket
5 import sys
6 import traceback
7 from paramiko.py3compat import input
8 from models import models
9 import datetime
10 import paramiko
11 try:
12 import interactive
13 except ImportError:
14 from . import interactive
15 def ssh_login(user_obj,bind_host_obj,mysql_engine,log_recording):
16 # now, connect and use paramiko Client to negotiate SSH2 across the connection
17 try:
18 client = paramiko.SSHClient()
19 client.load_system_host_keys()
20 client.set_missing_host_key_policy(paramiko.WarningPolicy())
21 print('*** Connecting...')
22 #client.connect(hostname, port, username, password)
23 client.connect(bind_host_obj.host.ip_addr,
24 bind_host_obj.host.port,
25 bind_host_obj.remoteuser.username,
26 bind_host_obj.remoteuser.password,
27 timeout=30)
28
29 cmd_caches = []
30 chan = client.invoke_shell()
31 print(repr(client.get_transport()))
32 print('*** Here we go!\n')
33 cmd_caches.append(models.AuditLog(user_id=user_obj.id,
34 bind_host_id=bind_host_obj.id,
35 action_type='login',
36 date=datetime.datetime.now()
37 ))
38 log_recording(user_obj,bind_host_obj,cmd_caches)
39 interactive.interactive_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording)
40 chan.close()
41 client.close()
42 except Exception as e:
43 print('*** Caught exception: %s: %s' % (e.__class__, e))
44 traceback.print_exc()
45 try:
46 client.close()
47 except:
48 pass
49 sys.exit(1)
utils.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 import yaml
2 try:
3 from yaml import CLoader as Loader, CDumper as Dumper
4 except ImportError:
5 from yaml import Loader, Dumper
6 def print_err(msg,quit=False):
7 output = "\033[31;1mError: %s\033[0m" % msg
8 if quit:
9 exit(output)
10 else:
11 print(output)
12 def yaml_parser(yml_filename):
13 '''
14 load yaml file and return
15 :param yml_filename:
16 :return:
17 '''
18 #yml_filename = "%s/%s.yml" % (settings.StateFileBaseDir,yml_filename)
19 try:
20 yaml_file = open(yml_filename,'r')
21 data = yaml.load(yaml_file)
22 return data
23 except Exception as e:
24 print_err(e)
views.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 from models import models
2 from conf import settings
3 from modules.utils import print_err,yaml_parser
4 from modules.db_conn import engine,session
5 from modules import common_filters
6 from modules import ssh_login
7 def auth():
8 '''
9 do the user login authentication
10 :return:
11 '''
12 count = 0
13 while count <3:
14 username = input("\033[32;1mUsername:\033[0m").strip()
15 if len(username) ==0:continue
16 password = input("\033[32;1mPassword:\033[0m").strip()
17 if len(password) ==0:continue
18 user_obj = session.query(models.UserProfile).filter(models.UserProfile.username==username,
19 models.UserProfile.password==password).first()
20 if user_obj:
21 return user_obj
22 else:
23 print("wrong username or password, you have %s more chances." %(3-count-1))
24 count +=1
25 else:
26 print_err("too many attempts.")
27 def welcome_msg(user):
28 WELCOME_MSG = '''\033[32;1m
29 ------------- Welcome [%s] login JBSdb -------------
30 \033[0m'''% user.username
31 print(WELCOME_MSG)
32 def log_recording(user_obj,bind_host_obj,logs):
33 '''
34 flush user operations on remote host into DB
35 :param user_obj:
36 :param bind_host_obj:
37 :param logs: list format [logItem1,logItem2,...]
38 :return:
39 '''
40 print("\033[41;1m--logs:\033[0m",logs)
41 session.add_all(logs)
42 session.commit()
43 def start_session(argvs):
44 print('going to start sesssion ')
45 user = auth()
46 if user:
47 welcome_msg(user)
48 print(user.bind_hosts)
49 print(user.host_groups)
50 exit_flag = False
51 while not exit_flag:
52 if user.bind_hosts:
53 print('\033[32;1mz.\tungroupped hosts (%s)\033[0m' %len(user.bind_hosts) )
54 for index,group in enumerate(user.host_groups):
55 print('\033[32;1m%s.\t%s (%s)\033[0m' %(index,group.name, len(group.bind_hosts)) )
56
57 choice = input("[%s]:" % user.username).strip()
58 if len(choice) == 0:continue
59 if choice == 'z':
60 print("------ Group: ungroupped hosts ------" )
61 for index,bind_host in enumerate(user.bind_hosts):
62 print(" %s.\t%s@%s(%s)"%(index,
63 bind_host.remote_user.username,
64 bind_host.host.hostname,
65 bind_host.host.ip,
66 ))
67 print("----------- END -----------" )
68 elif choice.isdigit():
69 choice = int(choice)
70 if choice < len(user.host_groups):
71 print("------ Group: %s ------" % user.host_groups[choice].name )
72 for index,bind_host in enumerate(user.host_groups[choice].bind_hosts):
73 print(" %s.\t%s@%s(%s)"%(index,
74 bind_host.remote_user.username,
75 bind_host.host.hostname,
76 bind_host.host.ip,
77 ))
78 print("----------- END -----------" )
79 #host selection
80 while not exit_flag:
81 user_option = input("[(b)back, (q)quit, select host to login]:").strip()
82 if len(user_option)==0:continue
83 if user_option == 'b':break
84 if user_option == 'q':
85 exit_flag=True
86 if user_option.isdigit():
87 user_option = int(user_option)
88 if user_option < len(user.host_groups[choice].bind_hosts) :
89 print('host:',user.host_groups[choice].bind_hosts[user_option])
90 print('audit log:',user.host_groups[choice].bind_hosts[user_option].audit_logs)
91 ssh_login.ssh_login(user,
92 user.host_groups[choice].bind_hosts[user_option],
93 session,
94 log_recording)
95 else:
96 print("no this option..")
97 def stop_server(argvs):
98 pass
99 def create_users(argvs):
100 '''
101 create little_finger access user
102 :param argvs:
103 :return:
104 '''
105 if '-f' in argvs:
106 user_file = argvs[argvs.index("-f") +1 ]
107 else:
108 print_err("invalid usage, should be:\ncreateusers -f <the new users file>",quit=True)
109 source = yaml_parser(user_file)
110 if source:
111 for key,val in source.items():
112 print(key,val)
113 obj = models.UserProfile(username=key,password=val.get('password'))
114 # if val.get('groups'):
115 # groups = session.query(models.Group).filter(models.Group.name.in_(val.get('groups'))).all()
116 # if not groups:
117 # print_err("none of [%s] exist in group table." % val.get('groups'),quit=True)
118 # obj.groups = groups
119 # if val.get('bind_hosts'):
120 # bind_hosts = common_filters.bind_hosts_filter(val)
121 # obj.bind_hosts = bind_hosts
122 # #print(obj)
123 session.add(obj)
124 session.commit()
125 def create_groups(argvs):
126 '''
127 create groups
128 :param argvs:
129 :return:
130 '''
131 if '-f' in argvs:
132 group_file = argvs[argvs.index("-f") +1 ]
133 else:
134 print_err("invalid usage, should be:\ncreategroups -f <the new groups file>",quit=True)
135 source = yaml_parser(group_file)
136 if source:
137 for key,val in source.items():
138 print(key,val)
139 obj = models.HostGroup(name=key)
140 # if val.get('bind_hosts'):
141 # bind_hosts = common_filters.bind_hosts_filter(val)
142 # obj.bind_hosts = bind_hosts
143 #
144 # if val.get('user_profiles'):
145 # user_profiles = common_filters.user_profiles_filter(val)
146 # obj.user_profiles = user_profiles
147 session.add(obj)
148 session.commit()
149 def create_hosts(argvs):
150 '''
151 create hosts
152 :param argvs:
153 :return:
154 '''
155 if '-f' in argvs:
156 hosts_file = argvs[argvs.index("-f") +1 ]
157 else:
158 print_err("invalid usage, should be:\ncreate_hosts -f <the new hosts file>",quit=True)
159 source = yaml_parser(hosts_file)
160 if source:
161 print(source)
162 for key,val in source.items():
163 print(key,val)
164 obj = models.Host(hostname=key,ip=val.get('ip'), port=val.get('port') or 22)
165 session.add(obj)
166 session.commit()
167 def create_bindhosts(argvs):
168 '''
169 create bind hosts
170 :param argvs:
171 :return:
172 '''
173 if '-f' in argvs:
174 bindhosts_file = argvs[argvs.index("-f") +1 ]
175 else:
176 print_err("invalid usage, should be:\ncreate_hosts -f <the new bindhosts file>",quit=True)
177 source = yaml_parser(bindhosts_file)
178 if source:
179 for key,val in source.items():
180 #print(key,val)
181 host_obj = session.query(models.Host).filter(models.Host.hostname==val.get('hostname')).first()
182 assert host_obj
183 for item in val['remote_users']:
184 print(item )
185 assert item.get('auth_type') #assert --必须存在,不存在报错
186 if item.get('auth_type') == 'ssh-password':
187 remoteuser_obj = session.query(models.RemoteUser).filter(
188 models.RemoteUser.username==item.get('username'),
189 models.RemoteUser.password==item.get('password')
190 ).first()
191 else:
192 remoteuser_obj = session.query(models.RemoteUser).filter(
193 models.RemoteUser.username==item.get('username'),
194 models.RemoteUser.auth_type==item.get('auth_type'),
195 ).first()
196 if not remoteuser_obj:
197 print_err("RemoteUser obj %s does not exist." % item,quit=True )
198 bindhost_obj = models.BindHost(host_id=host_obj.id,remoteuser_id=remoteuser_obj.id)
199 session.add(bindhost_obj)
200 #for groups this host binds to
201 if source[key].get('groups'):
202 group_objs = session.query(models.HostGroup).filter(models.HostGroup.name.in_(source[key].get('groups') )).all()
203 assert group_objs
204 print('groups:', group_objs)
205 bindhost_obj.host_groups = group_objs
206 #for user_profiles this host binds to
207 if source[key].get('user_profiles'):
208 userprofile_objs = session.query(models.UserProfile).filter(models.UserProfile.username.in_(
209 source[key].get('user_profiles')
210 )).all()
211 assert userprofile_objs
212 print("userprofiles:",userprofile_objs)
213 bindhost_obj.user_profiles = userprofile_objs
214 #print(bindhost_obj)
215 session.commit()
216 def create_remoteusers(argvs):
217 '''
218 create remoteusers
219 :param argvs:
220 :return:
221 '''
222 if '-f' in argvs:
223 remoteusers_file = argvs[argvs.index("-f") +1 ]
224 else:
225 print_err("invalid usage, should be:\ncreate_remoteusers -f <the new remoteusers file>",quit=True)
226 source = yaml_parser(remoteusers_file)
227 if source:
228 for key,val in source.items():
229 print(key,val)
230 obj = models.RemoteUser(username=val.get('username'),auth_type=val.get('auth_type'),password=val.get('password'))
231 session.add(obj)
232 session.commit()
233 def syncdb(argvs):
234 print("Syncing DB....")
235 engine = models.create_engine(settings.Conn_Params,
236 echo=True)
237 models.Base.metadata.create_all(engine) #创建所有表结构
new_bindhosts.yml
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 bind1:
2 hostname: ubuntu test
3 remote_users:
4 - user1:
5 username: root
6 auth_type: ssh-key
7 #password: 123
8 - user2:
9 username: jyh3
10 auth_type: ssh-password
11 password: jyh123
12 groups:
13 - bj_group
14 user_profiles:
15 - jyh
16 - jack
17 bind2:
18 hostname: server2
19 remote_users:
20 - user1:
21 username: root
22 auth_type: ssh-password
23 password: abc123
24 groups:
25 - bj_group
26 - sh_group
27 user_profiles:
28 - rain
new_groups.yml
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 bj_group:
2 #bind_hosts:
3 # - h1
4 # - h2
5 user_profiles:
6 - jyh
7 sh_group:
8 user_profiles:
9 - jack
10 - jyh
11 - rain
new_hosts.yml
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ubuntu test:
2 ip: 192.168.2.243
3 port: 22
4 server1:
5 ip: 192.168.2.100
6 port: 30000
7 server2:
8 ip: 10.4.4.22
new_remoteusers.yml
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 user0:
2 auth_type: ssh-password
3 username: root
4 password: abc123
5 user1:
6 auth_type: ssh-password
7 username: root
8 password: jyh123456
9 user2:
10 auth_type: ssh-key
11 username: root
12 #password: abc!23
13 user3:
14 auth_type: ssh-password
15 username: jyh3
16 password: jyh123
new_users.yml
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 jyh:
2 password: jyh123
3 # groups:
4 # - web_servers
5 # - db_servers
6 #bind_hosts:
7 # - h1
8 # - h2
9 # - h3
10 jack:
11 password: jack123
完整示例代码 https://github.com/triaquae/py3_training/tree/master/%E5%A0%A1%E5%9E%92%E6%9C%BA
重点是---设计过程,架构,表结构,交互
完善audit 如在action_registers.py中 "audit" : views.log_audit 。。。设计表结构---能执行,能记录,能看