Python 13:程序:堡垒机开发

Python 13:程序:堡垒机开发


1、需求

2、表结构

3、readme

4、目录结构

5、代码

6、测试样图 


一、需求

功能需求:

1、所有的用户操作日志要保留在数据库中(先存到redis中防止数据断线丢失)
2、每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
3、允许用户对不同的目标设备有不同的访问权限,例:
  1、对192.168.1.113有mysql 用户的权限
  2、对192.168.1.105有root用户的权限
  3、对192.168.1.100没任何权限
4、分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限 

二、表结构

三、readme

 1 作者:zz
 2 版本: 堡垒机 示例版本 v0.1
 3 开发环境: python3.6
 4 
 5 程序介绍
 6 1、所有的用户操作日志要保留在数据库中(先存到redis中防止数据断线丢失)
 7 2、每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
 8 3、允许用户对不同的目标设备有不同的访问权限,例:
 9   1、对192.168.1.113有mysql 用户的权限
10   2、对192.168.1.105有root用户的权限
11   3、对192.168.1.100没任何权限
12 4、分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限 
13 
14 使用说明
15 python3 bin/run.py  start_session #启动
16 python3 bin/run.py  syncdb #创建表结构
17 python3 bin/run.py  create_users -f share/examples/new_user.yml #创建堡垒机账户
18 python3 bin/run.py  create_groups -f share/examples/new_groups.yml #创建用户组
19 python3 bin/run.py  create_hosts -f  share/examples/new_hosts.yml #创建远程主机
20 python3 bin/run.py  create_bindhosts -f share/examples/new_bindhosts.yml #创建绑定主机
21 python3 bin/run.py  create_remoteusers -f share/examples/new_remoteusers.yml #创建远程主机登录方式
22 python3 bin/run.py  view_user_record # 审计用户操作命令
readme

四、目录结构

五、代码

1、bin

1 import os,sys
2 
3 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
4 print(BASE_DIR)
5 sys.path.append(BASE_DIR)
6 
7 if __name__ == '__main__':
8     from modules.actions import excute_from_command_line
9     excute_from_command_line(sys.argv)
run.py

2、conf

 1 from modules import views
 2 
 3 actions = {
 4     'start_session': views.start_session, #启动s
 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     'view_user_record': views.user_record_cmd  # 审计用户操作命令
12 }
action_registers.py
1 import os,sys
2 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
3 DB_CONN ="mysql+pymysql://root:123456@192.168.1.113:3306/bljdb?charset=utf8"
settings.py

3、modules

 1 from conf import settings
 2 from conf import action_registers
 3 from modules import utils
 4 
 5 def help_msg():
 6     '''help'''
 7     print("33[31;1mAvailable commands:33[0m")
 8     for key in action_registers.actions:
 9         print("	",key)
10 
11 def excute_from_command_line(argvs):
12     if len(argvs) < 2:
13         help_msg()
14         exit()
15     if argvs[1] not in action_registers.actions:
16         utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True)
17     action_registers.actions[argvs[1]](argvs[1:])
actions.py
 1 from  modules import models
 2 from modules.db_conn import engine,session
 3 from modules.utils import print_err
 4 
 5 def bind_hosts_filter(vals):
 6     print('**>',vals.get('bind_hosts') )  #打印绑定主机信息
 7     bind_hosts = session.query(models.BindHost).filter(models.Host.hostname.in_(vals.get('bind_hosts'))).all()  #从数据库中查询对应绑定主机信息
 8     if not bind_hosts: #如果不存在相应信息
 9         print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'),quit=True) #打印报错
10     return bind_hosts #返回对应bind_hosts信息
11 
12 def user_profiles_filter(vals):
13     user_profiles = session.query(models.UserProfile).filter(models.UserProfile.username.in_(vals.get('user_profiles'))).all() #从数据库中查询堡垒机用户信息
14     if not user_profiles: #如果不存在相应信息
15         print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'),quit=True)  #打印报错
16     return  user_profiles  #返回对应用户信息
common_filters.py
1 from sqlalchemy import create_engine,Table
2 from  sqlalchemy.orm import sessionmaker
3 from conf import settings
4 engine = create_engine(settings.DB_CONN)
5 # engine = create_engine(settings.DB_CONN,echo=True)
6 SessionCls = sessionmaker(bind=engine) #创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
7 session = SessionCls()
db_conn.py
 1 #!/usr/bin/env python
 2 
 3 # Copyright (C) 2003-2007  Robey Pointer <robeypointer@gmail.com>
 4 #
 5 # This file is part of paramiko.
 6 #
 7 # Paramiko is free software; you can redistribute it and/or modify it under the
 8 # terms of the GNU Lesser General Public License as published by the Free
 9 # Software Foundation; either version 2.1 of the License, or (at your option)
10 # any later version.
11 #
12 # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
15 # details.
16 #
17 # You should have received a copy of the GNU Lesser General Public License
18 # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
19 # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
20 
21 import base64
22 import getpass
23 import os
24 import socket
25 import sys
26 import traceback
27 from paramiko.py3compat import input
28 import paramiko
29 try:
30     import interactive
31 except ImportError:
32     from . import interactive
33 # now, connect and use paramiko Client to negotiate SSH2 across the connection
34 try:
35     client = paramiko.SSHClient()
36     client.load_system_host_keys()
37     client.set_missing_host_key_policy(paramiko.WarningPolicy())
38     print('*** Connecting...')
39     client.connect(hostname, port, username, password)
40     chan = client.invoke_shell()
41     print(repr(client.get_transport()))
42     print('*** Here we go!
')
43     interactive.interactive_shell(chan)
44     chan.close()
45     client.close()
46 
47 except Exception as e:
48     print('*** Caught exception: %s: %s' % (e.__class__, e))
49     traceback.print_exc()
50     try:
51         client.close()
52     except:
53         pass
54     sys.exit(1)
demo_simple.py
  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  modules import models
 24 import datetime,time
 25 import redis
 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 def interactive_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording):
 34     '''
 35     :param chan:
 36     :param user_obj:
 37     :param bind_host_obj: 主机
 38     :param cmd_caches: 命令列表
 39     :param log_recording: 日志记录
 40     :return:
 41     '''
 42     # 判断是否是windows shell
 43     if has_termios:
 44         posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording)
 45     else:
 46         windows_shell(chan)
 47 def posix_shell(chan,user_obj,bind_host_obj,cmd_caches,log_recording):
 48     '''
 49     :param chan:
 50     :param user_obj:
 51     :param bind_host_obj:
 52     :param cmd_caches:
 53     :param log_recording:
 54     :return:
 55     '''
 56     import select
 57     oldtty = termios.tcgetattr(sys.stdin)
 58     try:
 59         tty.setraw(sys.stdin.fileno())
 60         tty.setcbreak(sys.stdin.fileno())
 61         chan.settimeout(0.0)
 62         cmd = ''
 63         tab_key = False
 64         while True:
 65             r, w, e = select.select([chan, sys.stdin], [], [])
 66             if chan in r:
 67                 try:
 68                     x = u(chan.recv(1024))
 69                     if tab_key:
 70                         if x not in ('x07' , '
'):
 71                             #print('tab:',x)
 72                             cmd += x
 73                         tab_key = False
 74                     if len(x) == 0:
 75                         sys.stdout.write('
*** EOF
')
 76                         # test for redis to mysql
 77                         break
 78                     sys.stdout.write(x)
 79                     sys.stdout.flush()
 80                 except socket.timeout:
 81                     pass
 82             if sys.stdin in r:
 83                 x = sys.stdin.read(1)
 84                 if '
' != x:
 85                     cmd +=x
 86                 else:
 87                     print('cmd->:',cmd)
 88                     user_record_cmd = user_obj.username + 'cmd'
 89                     pool = redis.ConnectionPool(host='192.168.1.100', port=6379)
 90                     user_record = [user_obj.id, bind_host_obj.id, 'cmd', cmd,
 91                                    time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]
 92                     r = redis.Redis(connection_pool=pool)
 93                     r.lpush(user_record_cmd, user_record)
 94                     cmd = ''  #清空cmd
 95                 if '	' == x:
 96                     tab_key = True
 97                 if len(x) == 0:
 98                     break
 99                 chan.send(x)
100     finally:
101         termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
102 
103 # thanks to Mike Looijmans for this code
104 def windows_shell(chan):
105     import threading
106     sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.

")
107     def writeall(sock):
108         while True:
109             data = sock.recv(256)
110             if not data:
111                 sys.stdout.write('
*** EOF ***

')
112                 sys.stdout.flush()
113                 break
114             sys.stdout.write(data.decode())
115             sys.stdout.flush()
116     writer = threading.Thread(target=writeall, args=(chan,))
117     writer.start()
118     try:
119         while True:
120             d = sys.stdin.read(1)
121             if not d:
122                 break
123             chan.send(d)
124     except EOFError:
125         # user hit ^Z or F6
126         pass
interactive.py
  1 from sqlalchemy import create_engine,Table
  2 from sqlalchemy.ext.declarative import declarative_base
  3 from sqlalchemy import Column, Integer, String,ForeignKey,UniqueConstraint,UnicodeText,DateTime
  4 from sqlalchemy.orm import relationship
  5 from sqlalchemy import or_,and_
  6 from sqlalchemy import func
  7 from sqlalchemy_utils import ChoiceType,PasswordType
  8 
  9 Base = declarative_base() #生成一个ORM 基类
 10 
 11 #多对多关联
 12 #多对多关联:绑定主机和用户组
 13 BindHost2Group = Table('bindhost_2_group',Base.metadata,
 14     Column('bindhost_id',ForeignKey('bind_host.id'),primary_key=True),
 15     Column('group_id',ForeignKey('groups.id'),primary_key=True),
 16 )
 17 #多对多关联:绑定主机和堡垒机用户
 18 BindHost2UserProfile = Table('bindhost_2_userprofile',Base.metadata,
 19     Column('bindhost_id',ForeignKey('bind_host.id'),primary_key=True),
 20     Column('uerprofile_id',ForeignKey('user_profile.id'),primary_key=True),
 21 )
 22 #多对多关联:用户组和堡垒机用户
 23 Group2UserProfile = Table('group_2_userprofile',Base.metadata,
 24     Column('userprofile_id',ForeignKey('user_profile.id'),primary_key=True),
 25     Column('group_id',ForeignKey('groups.id'),primary_key=True),
 26 )
 27 
 28 class BindHost(Base):
 29     '''绑定主机(IP和用户联合唯一),
 30        192.168.1.1 mysql
 31        10.5.1.6    root'''
 32     __tablename__ = 'bind_host'  #表名
 33     id = Column(Integer,primary_key=True,autoincrement=True)
 34     host_id = Column(Integer,ForeignKey('host.id'))  #外键关联host.id
 35     remoteuser_id = Column(Integer,ForeignKey('remote_user.id'))  #外键关联remote_user.id
 36 
 37     host = relationship("Host")  #通过host字段查询host表中相关数据
 38     remoteuser = relationship("RemoteUser")  #通过remoteuser字段查询remote_user表中相关数据
 39     groups = relationship("Group",secondary=BindHost2Group,backref='bind_hosts') #group与bind_host多对多对应并且可以互查,对应关系存在表BindHost2Group中
 40     user_profiles = relationship("UserProfile",secondary=BindHost2UserProfile,backref='bind_hosts')  #user_profile与bind_host多对多对应并且可以互查,对应关系存在表BindHost2Group中
 41     audit_logs = relationship('AuditLog') #通过audit_logs字段查询auditLog表中相关数据
 42     __table_args__ = (UniqueConstraint('host_id', 'remoteuser_id', name='_bindhost_and_user_uc'),)   # 联合唯一
 43 
 44     def __repr__(self):
 45         return "<BindHost(id='%s',name='%s',user='%s')>" % (self.id,self.host.hostname,self.remoteuser.username)
 46 
 47 
 48 class UserProfile(Base):
 49     '''堡垒机用户'''
 50     __tablename__ = 'user_profile'
 51     id = Column(Integer,primary_key=True,autoincrement=True)
 52     username = Column(String(32),unique=True,nullable=False)
 53     password = Column(String(128),unique=True,nullable=False)
 54     groups = relationship('Group',secondary=Group2UserProfile,backref='user_profiles') #group与user_profile多对多对应并且可以互查,对应关系存在表Group2UserProfile中
 55     audit_logs = relationship('AuditLog') #通过audit_logs字段查询auditLog表中相关数据
 56 
 57     def __repr__(self):
 58         return "<UserProfile(id='%s',username='%s')>" % (self.id,self.username)
 59 
 60 class Group(Base):
 61     '''用户组'''
 62     __tablename__ = 'groups'
 63     id = Column(Integer,primary_key=True)
 64     name = Column(String(64),nullable=False,unique=True)
 65     def __repr__(self):
 66         return "<HostGroup(id='%s',name='%s')>" % (self.id,self.name)
 67 
 68 class Host(Base):
 69     '''远程主机'''
 70     __tablename__ = 'host'
 71     id = Column(Integer,primary_key=True,autoincrement=True)
 72     hostname = Column(String(64),unique=True,nullable=False)
 73     ip_addr = Column(String(128),unique=True,nullable=False)
 74     port = Column(Integer,default=22)
 75     bind_hosts = relationship("BindHost")  #通过bind_host字段查询bind_host表中相关数据
 76     def __repr__(self):
 77         return "<Host(id='%s',hostname='%s')>" % (self.id,self.hostname)
 78 
 79 class RemoteUser(Base):
 80     '''远程用户'''
 81     __tablename__ = 'remote_user'
 82     AuthTypes = [
 83         ('ssh-passwd','SSH/Password'),# 第一个是存在数据库里的,第二个具体的值
 84         ('ssh-key','SSH/KEY'),
 85     ]
 86     id = Column(Integer,primary_key=True,autoincrement=True)
 87     auth_type = Column(ChoiceType(AuthTypes))
 88     username = Column(String(64),nullable=False)
 89     password = Column(String(255))
 90 
 91     __table_args__ = (UniqueConstraint('auth_type', 'username','password', name='_user_passwd_uc'),) #联合唯一
 92 
 93     def __repr__(self):
 94         return "<RemoteUser(id='%s',auth_type='%s',user='%s')>" % (self.id,self.auth_type,self.username)
 95 
 96 
 97 class AuditLog(Base):
 98     '''用户操作日志'''
 99     __tablename__ = 'audit_log'
100     id = Column(Integer,primary_key=True)
101     user_id = Column(Integer,ForeignKey('user_profile.id'))
102     bind_host_id = Column(Integer,ForeignKey('bind_host.id'))
103     action_choices = [
104         (0,'CMD'),
105         (1,'Login'),
106         (2,'Logout'),
107         (3,'GetFile'),
108         (4,'SendFile'),
109         (5,'Exception'),
110     ]
111     action_choices2 = [
112         (u'cmd',u'CMD'),
113         (u'login',u'Login'),
114         (u'logout',u'Logout'),
115     ]
116     action_type = Column(ChoiceType(action_choices2))
117     #action_type = Column(String(64))
118     cmd = Column(String(255))
119     date = Column(DateTime)
120     user_profile = relationship("UserProfile")
121     bind_host = relationship("BindHost")
122     '''def __repr__(self):
123         return "<user=%s,host=%s,action=%s,cmd=%s,date=%s>" %(self.user_profile.username,
124                                                       self.bind_host.host.hostname,
125                                                               self.action_type,
126                                                               self.date)
127     '''
models.py
import base64
import getpass
import os
import socket
import sys
import traceback
from paramiko.py3compat import input
from  modules import models
import datetime,time
import redis
import paramiko
try:
    import interactive
except ImportError:
    from . import interactive


def ssh_login(user_obj,bind_host_obj,mysql_engine,log_recording):
    # now, connect and use paramiko Client to negotiate SSH2 across the connection
    '''
    ssh登陆
    :param user_obj:
    :param bind_host_obj:
    :param mysql_engine: 连接数据库
    :param log_recording: 写日志记录
    :return:
    '''
    try:
        client = paramiko.SSHClient()
        client.load_system_host_keys()
        client.set_missing_host_key_policy(paramiko.WarningPolicy())
        print('*** Connecting...')
        #client.connect(hostname, port, username, password)
        client.connect(bind_host_obj.host.ip_addr,
                       bind_host_obj.host.port,
                       bind_host_obj.remoteuser.username,
                       bind_host_obj.remoteuser.password,
                       timeout=30)

        cmd_caches = []
        chan = client.invoke_shell()
        print(repr(client.get_transport()))
        print('*** Here we go!
')

        # 连接redis
        pool = redis.ConnectionPool(host='192.168.1.100', port=6379)
        # 传一个命令列表给redis
        r = redis.Redis(connection_pool=pool)
        user_record = [user_obj.id, bind_host_obj.id, 'login',
                       time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]

        # 用户名做key前缀,避免冲突
        key_name = str(user_obj.username)+'_login'
        r.lpush(key_name, user_record)
        interactive.interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording)
        chan.close()
        client.close()

        # 数据库写入操作
        login_record = r.lrange(key_name, 0, -1)
        login_redis_record = login_record[0].decode().replace('[', '').replace(']', '').split(',')
        log_item = models.AuditLog(user_id=login_redis_record[0],
                                   bind_host_id=login_redis_record[1],
                                   action_type='login',
                                   cmd='login',
                                   date=login_redis_record[3].replace("'", ''))
        cmd_caches.append(log_item)
        log_recording(user_obj, bind_host_obj, cmd_caches)
        user_record_cmd = user_obj.username+'cmd'
        cmd_redis_record = r.lrange(user_record_cmd, 0, -1)
        for i in cmd_redis_record:
            cmd_caches = []
            v = i.decode().replace('[', '').replace(']', '').split(',')
            v2 = v[3].replace("'", '')
            print(v[0], v[1], v[2], v[3], v[4])
            log_item = models.AuditLog(user_id=v[0],
                                       bind_host_id=v[1],
                                       action_type='cmd',
                                       cmd=v2, date=v[4].replace("'", ''))
            cmd_caches.append(log_item)
            log_recording(user_obj, bind_host_obj, cmd_caches)

        # 当退出的时候将redis的值写入到数据库并且清空redis
        logout_caches = []
        logout_caches.append(models.AuditLog(user_id=user_obj.id,
                                             bind_host_id=bind_host_obj.id,
                                             action_type='logout',
                                             cmd='logout',
                                             date=datetime.datetime.now()))
        log_recording(user_obj, bind_host_obj, logout_caches)
        # 清空keys
        r.delete(key_name)
        r.delete(user_record_cmd)

    except Exception as e:
        print('*** Caught exception: %s: %s' % (e.__class__, e))
        traceback.print_exc()
        try:
            client.close()
        except:
            pass
        sys.exit(1)
ssh_login.py
 1 from conf import settings
 2 import yaml
 3 try:
 4     from yaml import CLoader as Loader, CDumper as Dumper
 5 except ImportError:
 6     from yaml import Loader, Dumper
 7 
 8 def print_err(msg,quit=False):
 9     output = "33[31;1mError: %s33[0m" % msg
10     if quit:
11         exit(output)
12     else:
13         print(output)
14 
15 
16 def yaml_parser(yml_filename):
17     '''
18     load yaml file and return
19     :param yml_filename:
20     :return:
21     '''
22     #yml_filename = "%s/%s.yml" % (settings.StateFileBaseDir,yml_filename)
23     try:
24         yaml_file = open(yml_filename,'r')
25         data = yaml.load(yaml_file)
26         return data
27     except Exception as e:
28         print_err(e)
utils.py
  1 from modules import models
  2 from modules.db_conn import engine,session
  3 from modules.utils import print_err,yaml_parser
  4 from modules import common_filters
  5 from modules import ssh_login
  6 def auth():
  7     '''登陆验证模块'''
  8     count = 0
  9     while count <3: #允许用户尝试次数
 10         username = input("33[32;1mUsername:33[0m").strip()
 11         if len(username) ==0:continue
 12         password = input("33[32;1mPassword:33[0m").strip()
 13         if len(password) ==0:continue
 14         user_obj = session.query(models.UserProfile).filter(models.UserProfile.username==username,
 15                                                             models.UserProfile.password==password).first()  #匹配用户名密码
 16         if user_obj: #账号密码正确
 17             return user_obj
 18         else:  #账号密码错误
 19             print("wrong username or password, you have %s more chances." %(3-count-1))
 20             count +=1
 21     else:
 22         print_err("too many attempts.")
 23 
 24 def welcome_msg(user):
 25     '''欢迎界面'''
 26     WELCOME_MSG = '''33[32;1m
 27     ------------- Welcome [%s] login LittleFinger -------------
 28     33[0m'''%  user.username
 29     print(WELCOME_MSG)
 30 
 31 def log_recording(user_obj,bind_host_obj,logs):
 32     '''将用户操作存入数据库'''
 33     print("33[41;1m--logs:33[0m",logs)
 34     session.add_all(logs)
 35     session.commit()
 36 def start_session(argvs):
 37     '''启动模块'''
 38     print('going to start sesssion ')
 39     user = auth() #登录验证
 40     if user:
 41         welcome_msg(user)
 42         # print(user.bind_hosts)
 43         # print(user.groups)
 44         exit_flag = False
 45         while not exit_flag:
 46             if user.bind_hosts: #若用户关联了绑定主机
 47                 print('33[32;1mz.	ungroupped hosts (%s)33[0m' %len(user.bind_hosts) ) #打印用户绑定的未分组的主机个数
 48             for index,group in enumerate(user.groups):  #打印用户绑定的用户组
 49                 print('33[32;1m%s.	%s (%s)33[0m' %(index,group.name,  len(group.bind_hosts)) )
 50             choice = input("[%s]:" % user.username).strip()
 51             if len(choice) == 0:continue
 52             if choice == 'z': #打印未分组的绑定主机
 53                 print("------ Group: ungroupped hosts ------" )
 54                 for index,bind_host in enumerate(user.bind_hosts): #提取主机名和ip
 55                     print("  %s.	%s@%s(%s)"%(index,
 56                                               bind_host.remoteuser.username,
 57                                               bind_host.host.hostname,
 58                                               bind_host.host.ip_addr,
 59                                               ))
 60                 print("----------- END -----------" )
 61                 while not exit_flag:
 62                     user_option = input("[(b)back, (q)quit, select host to login]:").strip() #选择功能
 63                     if len(user_option)==0:continue
 64                     if user_option == 'b':break
 65                     if user_option == 'q':
 66                         exit_flag=True
 67                     if user_option.isdigit(): #判断用户输入是否为数字
 68                         user_option = int(user_option)
 69                         print(user.bind_hosts)
 70                         print(len(user.bind_hosts))
 71                         if user_option < len(user.bind_hosts) : #判断用户输入数字是否超出界限
 72                             print('host:',user.bind_hosts[user_option]) #打印host
 73                             print('audit log:',user.bind_hosts[user_option].audit_logs) #添加登陆日志
 74                             ssh_login.ssh_login(user,user.bind_hosts[user_option],session,log_recording) #调用ssh登陆模块登陆
 75             elif choice.isdigit(): #打印绑定的用户组包含的主机
 76                 choice = int(choice) #输入为数字
 77                 if choice < len(user.groups): #判断输入是否超出界限
 78                     print("------ Group: %s ------"  % user.groups[choice].name ) #打印包含的主机信息
 79                     for index,bind_host in enumerate(user.groups[choice].bind_hosts): #提取主机名和ip
 80                         print("  %s.	%s@%s(%s)"%(index,
 81                                                   bind_host.remoteuser.username,
 82                                                   bind_host.host.hostname,
 83                                                   bind_host.host.ip_addr,
 84                                                   ))
 85                     print("----------- END -----------" )
 86                 else:
 87                     print("no this option..")
 88                 while not exit_flag:
 89                     user_option = input("[(b)back, (q)quit, select host to login]:").strip()#选择功能
 90                     if len(user_option)==0:continue
 91                     if user_option == 'b':break
 92                     if user_option == 'q':
 93                         exit_flag=True
 94                     if user_option.isdigit(): #判断用户输入是否为数字
 95                         user_option = int(user_option)
 96                         if user_option < len(user.groups[choice].bind_hosts) :
 97                             print('host:',user.groups[choice].bind_hosts[user_option])
 98                             print('audit log:',user.groups[choice].bind_hosts[user_option].audit_logs)#添加登陆日志
 99                             ssh_login.ssh_login(user,user.groups[choice].bind_hosts[user_option],session,log_recording) #调用ssh登陆模块登陆
100 
101 def stop_server(argvs):
102     pass
103 
104 def create_users(argvs):
105     '''创建堡垒机用户'''
106     if '-f' in argvs:  #create_users -f .yml 通过yaml文件创建
107         user_file  = argvs[argvs.index("-f") +1 ] #提取文件
108     else:
109         print_err("invalid usage, should be:
createusers -f <the new users file>",quit=True)  #格式不正确
110     source = yaml_parser(user_file) #将文件转化为字典
111     if source: #非空
112         for key,val in source.items():#提取值
113             print(key,val) #key:用户名 val:字典
114             obj = models.UserProfile(username=key,password=val.get('password')) #添加用户信息到数据库
115             if val.get('groups'): #若用户关联了用户组
116                 groups = session.query(models.Group).filter(models.Group.name.in_(val.get('groups'))).all()  #提取关联用户组信息
117                 if not groups:  #若组不存在
118                     print_err("none of [%s] exist in group table." % val.get('groups'),quit=True)  #打印信息
119                 obj.groups = groups  #添加用户与组关联
120             if val.get('bind_hosts'): #若存在堡垒机用户与绑定主机对应关系
121                 bind_hosts = common_filters.bind_hosts_filter(val) #调用modules下的common_filters下的bind_hosts_filter函数来添加对应关系
122                 obj.bind_hosts = bind_hosts #添加绑定主机对应关系
123             print(obj)
124             session.add(obj)  #添加
125         session.commit()  #提交
126 
127 def create_groups(argvs):
128     '''创建用户组'''
129     if '-f' in argvs:  #create_groups -f .yml 通过yaml文件创建
130         group_file  = argvs[argvs.index("-f") +1 ] #提取文件
131     else:
132         print_err("invalid usage, should be:
creategroups -f <the new groups file>",quit=True)  #格式不正确
133     source = yaml_parser(group_file) #将文件转化为字典
134     if source:  #非空
135         for key,val in source.items():#提取值
136             print(key,val) #key:组名 val:关联关系
137             obj = models.Group(name=key)  #添加组名到数据库
138             if val.get('bind_hosts'):  #若存在绑定主机与用户组的对应关系
139                 bind_hosts = common_filters.bind_hosts_filter(val) #调用modules下的common_filters下的bind_hosts_filter函数来添加对应关系
140                 obj.bind_hosts = bind_hosts  #添加绑定主机对应关系
141 
142             if val.get('user_profiles'):
143                 user_profiles = common_filters.user_profiles_filter(val)  #调用modules下的common_filters下的user_profiles_filter函数来添加对应关系
144                 obj.user_profiles = user_profiles #添加堡垒机用户和用户组的对应关系
145             session.add(obj) #添加
146         session.commit() #提交
147 
148 def create_hosts(argvs):
149     '''创建远程主机'''
150     if '-f' in argvs:   #create_hosts -f .yml 通过yaml文件创建
151         hosts_file  = argvs[argvs.index("-f") +1 ] #提取文件
152     else:
153         print_err("invalid usage, should be:
create_hosts -f <the new hosts file>",quit=True)  #格式不正确
154     source = yaml_parser(hosts_file) #将文件转化为字典
155     if source:  #非空
156         for key,val in source.items(): #提取值
157             print(key,val) #key:主机名 val:字典
158             obj = models.Host(hostname=key,ip_addr=val.get('ip_addr'), port=val.get('port') or 22) #添加入数据库
159             session.add(obj)
160         session.commit() #提交
161 
162 def create_bindhosts(argvs):
163     '''创建绑定主机'''
164     if '-f' in argvs: #create_bindhosts -f .yml 通过yaml文件创建
165         bindhosts_file  = argvs[argvs.index("-f") +1 ] #提取文件
166     else:
167         print_err("invalid usage, should be:
create_hosts -f <the new bindhosts file>",quit=True) #格式不正确
168     source = yaml_parser(bindhosts_file) #将文件转化为字典
169     if source:  #非空
170         for key,val in source.items(): #提取值
171             print(key,val)  #key:bind val:字典
172             host_obj = session.query(models.Host).filter(models.Host.hostname==val.get('hostname')).first()  #提取hostname
173             assert host_obj  #断言
174             for item in val['remote_users']: #提取用户信息
175                 print(item )
176                 assert item.get('auth_type') #断言非空
177                 if item.get('auth_type') == 'ssh-passwd': #判断登录方式
178                     remoteuser_obj = session.query(models.RemoteUser).filter(
179                                                         models.RemoteUser.username==item.get('username'),
180                                                         models.RemoteUser.password==item.get('password'),
181                                                     ).first()  #提取用户信息
182                 else:
183                     remoteuser_obj = session.query(models.RemoteUser).filter(
184                                                         models.RemoteUser.username==item.get('username'),
185                                                         models.RemoteUser.auth_type==item.get('auth_type'),
186                                                     ).first()  #提取用户信息
187                 if not remoteuser_obj:  #若用户不存在
188                     print_err("RemoteUser obj %s does not exist." % item,quit=True ) #打印报错
189                 bindhost_obj = models.BindHost(host_id=host_obj.id,remoteuser_id=remoteuser_obj.id) #添加绑定主机对应信息到数据库
190                 session.add(bindhost_obj)  #添加
191                 #for groups this host binds to
192                 if source[key].get('groups'):  #若存在绑定主机与用户组的对应关系
193                     group_objs = session.query(models.Group).filter(models.Group.name.in_(source[key].get('groups') )).all()  #提取组信息
194                     assert group_objs  #若组存在
195                     print('groups:', group_objs)
196                     bindhost_obj.groups = group_objs  #添加绑定用户和组的对应关系
197                 #for user_profiles this host binds to
198                 if source[key].get('user_profiles'):  #若存在绑定主机和堡垒机用户的对应关系
199                     print(source[key].get('user_profiles')) #提取堡垒机用户信息
200                     userprofile_objs = session.query(models.UserProfile).filter(models.UserProfile.username.in_(source[key].get('user_profiles'))).all() #提取对应用户信息
201                     print(userprofile_objs)
202                     assert userprofile_objs  #若用户信息存在
203                     print("userprofiles:",userprofile_objs)
204                     bindhost_obj.user_profiles = userprofile_objs  #添加用户信息与绑定主机对应关系
205                 #print(bindhost_obj)
206         session.commit() #提交
207 
208 def create_remoteusers(argvs):
209     '''创建远程主机登录方式'''
210     if '-f' in argvs: #create_remoteusers -f .yml 通过yaml文件创建
211         remoteusers_file  = argvs[argvs.index("-f") +1 ] #提取文件
212     else:
213         print_err("invalid usage, should be:
create_remoteusers -f <the new remoteusers file>",quit=True)  #格式不正确
214     source = yaml_parser(remoteusers_file) #将文件转化为字典
215     if source:  #非空
216         for key,val in source.items():  #提取值
217             print(key,val) #key:用户 val:字典
218             obj = models.RemoteUser(username=val.get('username'),auth_type=val.get('auth_type'),password=val.get('password')) #添加入数据库
219             session.add(obj)
220         session.commit()   #提交
221 
222 def syncdb(argvs):
223     print("Syncing DB....")
224     models.Base.metadata.create_all(engine) #创建所有表结构
225 
226 def user_record_cmd(argvs):
227     '''查看操作记录 '''
228     print('going to start view record')
229     user = auth()
230     # 默认root可以查所有人的记录
231     if user.username == 'root':
232         print('welcome %s ' % user.username)
233         exit_flag = False
234         # 用户对象
235         user_obj = session.query(models.UserProfile).filter().all()
236         # 循环查看堡垒机用户操作
237         while not exit_flag:
238             for user_profile_list in user_obj:
239                 # 打印堡垒机用户,根据堡垒机用户ID选择其管辖的机器并打印日志
240                 print("%s.	%s" % (user_profile_list.id, user_profile_list.username))
241             choice = input("[%s]:" % user.username).strip()
242             for user_profile_list in user_obj:
243                 if str(choice) == str(user_profile_list.id):
244                     if user_profile_list.bind_hosts:
245                         # 显示未分组的机器
246                         print('33[32;1mz.	ungroupped hosts (%s)33[0m' % len(user_profile_list.bind_hosts))
247                     else:
248                         print(' no binding groups ')
249                     for index, group in enumerate(user_profile_list.groups):
250                         print('33[32;1m%s.	%s (%s)33[0m' % (index, group.name, len(group.bind_hosts)))
251                     choice = input("[%s]:" % user.username).strip()
252                     if len(choice) == 0:continue
253                     if choice == 'z': #打印未分组的绑定主机
254                         print("------ Group: ungroupped hosts ------" )
255                         for index,bind_host in enumerate(user_profile_list.bind_hosts): #提取主机名和ip
256                             print("  %s.	%s@%s(%s)"%(index,
257                                               bind_host.remoteuser.username,
258                                               bind_host.host.hostname,
259                                               bind_host.host.ip_addr,
260                                               ))
261                         print("----------- END -----------" )
262                         while not exit_flag:
263                             user_option = input("[(b)back, (q)quit, select host to login]:").strip()
264                             if len(user_option) == 0:
265                                 continue
266                             if user_option == 'b':
267                                 break
268                             if user_option == 'q':
269                                 exit_flag = True
270                             if user_option.isdigit():
271                                 user_option = int(user_option)
272                                 if user_option < len(user_profile_list.bind_hosts):
273                                     # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
274                                     data = 
275                                         session.query(models.AuditLog).filter(
276                                             models.AuditLog.user_id == user_profile_list.id,
277                                             models.AuditLog.bind_host_id == user_profile_list.
278                                             bind_hosts[user_option].id).all()
279                                     if data:
280                                         for index, i in enumerate(data):
281                                             # print(index, i.date, i.cmd)
282                                             print(index, i.date, i.cmd.encode().decode('unicode-escape'))
283                                     else:
284                                         print('no record in host:', user_profile_list.groups[choice].
285                                               bind_hosts[user_option])
286                     elif choice.isdigit():  # 打印分组的机器
287                         choice = int(choice)
288                         if choice < len(user_profile_list.groups):
289                             print("------ Group: %s ------" % user_profile_list.groups[choice].name)
290                             for index, bind_host in enumerate(user_profile_list.groups[choice].bind_hosts):
291                                 print("  %s.	%s@%s(%s)" % (index,
292                                                             bind_host.remoteuser.username,
293                                                             bind_host.host.hostname,
294                                                             bind_host.host.ip_addr,
295                                                             ))
296                             print("----------- END -----------")
297                             # host selection 选择机器去查看操作信息
298                             while not exit_flag:
299                                 user_option = input("[(b)back, (q)quit, select host to login]:").strip()
300                                 if len(user_option) == 0:
301                                     continue
302                                 if user_option == 'b':
303                                     break
304                                 if user_option == 'q':
305                                     exit_flag = True
306                                 if user_option.isdigit():
307                                     user_option = int(user_option)
308                                     if user_option < len(user_profile_list.groups[choice].bind_hosts):
309                                         # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
310                                         data = 
311                                             session.query(models.AuditLog).filter(
312                                                 models.AuditLog.user_id == user_profile_list.id,
313                                                 models.AuditLog.bind_host_id == user_profile_list.groups[choice].
314                                                 bind_hosts[user_option].id).all()
315                                         if data:
316                                             for index, i in enumerate(data):
317                                                 # redis 写入value的时候带有了	 
 等需要转义
318                                                 # 第一个注释从数据库里读注释的这种不能转移	,
319                                                 # 第二个和现行的俩种中文转义有些问题
320                                                 # print(i.user_id, i.bind_host_id, i.action_type, i.cmd, i.date)
321                                                 # print(i.user_id, i.bind_host_id, i.action_type,
322                                                 #        codecs.getdecoder("unicode_escape")(i.cmd)[0], i.date)
323                                                 # print(i.user_id, i.bind_host_id, i.action_type,
324                                                 #       i.cmd.encode().decode('unicode-escape'), i.date)
325                                                 print(index, i.date, i.cmd)
326                                                 # print(index, i.date, i.cmd.encode().decode('unicode-escape'))
327                                         else:
328                                             print('no record in host:', user_profile_list.groups[choice].
329                                                   bind_hosts[user_option])
330     # 其他人只能查自己的操作记录
331     else:
332         exit_flag = False
333         while not exit_flag:
334             if user.bind_hosts: #若用户关联了绑定主机
335                 print('33[32;1mz.	ungroupped hosts (%s)33[0m' %len(user.bind_hosts) ) #打印用户绑定的未分组的主机个数
336             for index,group in enumerate(user.groups):  #打印用户绑定的用户组
337                 print('33[32;1m%s.	%s (%s)33[0m' %(index,group.name,  len(group.bind_hosts)) )
338 
339             choice = input("[%s]:" % user.username).strip()
340             if len(choice) == 0:continue
341             if choice == 'z': #打印未分组的绑定主机
342                 print("------ Group: ungroupped hosts ------" )
343                 for index,bind_host in enumerate(user.bind_hosts): #提取主机名和ip
344                     print("  %s.	%s@%s(%s)"%(index,
345                                       bind_host.remoteuser.username,
346                                       bind_host.host.hostname,
347                                       bind_host.host.ip_addr,
348                                       ))
349                 print("----------- END -----------" )
350                 while not exit_flag:
351                     user_option = input("[(b)back, (q)quit, select host to login]:").strip()
352                     if len(user_option) == 0:
353                         continue
354                     if user_option == 'b':
355                         break
356                     if user_option == 'q':
357                         exit_flag = True
358                     if user_option.isdigit():
359                         user_option = int(user_option)
360                         if user_option < len(user.bind_hosts):
361                             # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
362                             data = 
363                                 session.query(models.AuditLog).filter(
364                                     models.AuditLog.user_id == user.id,
365                                     models.AuditLog.bind_host_id == user.bind_hosts[user_option].id).all()
366                             if data:
367                                 for index, i in enumerate(data):
368                                     # print(index, i.date, i.cmd)
369                                     print(index, i.date, i.cmd.encode().decode('unicode-escape'))
370                             else:
371                                 print('no record in host:', user.groups[choice].
372                                       bind_hosts[user_option])
373 
374             elif choice.isdigit():  # 打印分组的机器
375                 choice = int(choice)
376                 if choice < len(user.groups):
377                     print("------ Group: %s ------" % user.groups[choice].name)
378                     for index, bind_host in enumerate(user.groups[choice].bind_hosts):
379                         print("  %s.	%s@%s(%s)" % (index,
380                                                     bind_host.remoteuser.username,
381                                                     bind_host.host.hostname,
382                                                     bind_host.host.ip_addr,
383                                                     ))
384                     print("----------- END -----------")
385                     # host selection 选择机器去查看操作信息
386                     while not exit_flag:
387                         user_option = input("[(b)back, (q)quit, select host to login]:").strip()
388                         if len(user_option) == 0:
389                             continue
390                         if user_option == 'b':
391                             break
392                         if user_option == 'q':
393                             exit_flag = True
394                         if user_option.isdigit():
395                             user_option = int(user_option)
396                             if user_option < len(user.groups[choice].bind_hosts):
397                                 # print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
398                                 data = 
399                                     session.query(models.AuditLog).filter(
400                                         models.AuditLog.user_id == user.id,
401                                         models.AuditLog.bind_host_id == user.groups[choice].
402                                         bind_hosts[user_option].id).all()
403                                 if data:
404                                     for index, i in enumerate(data):
405                                         # print(index, i.date, i.cmd)
406                                         print(index, i.date, i.cmd.encode().decode('unicode-escape'))
407                                 else:
408                                     print('no record in host:', user.groups[choice].
409                                           bind_hosts[user_option])
view.py

4、share/examples

 1 bind1:
 2   hostname: server1
 3   remote_users:
 4     - user2:
 5       username: root
 6       auth_type: ssh-key
 7       #password: 123
 8     - user1:
 9       username: mysql
10       auth_type: ssh-passwd
11       password: 123456
12   groups:
13     - bj_group
14   user_profiles:
15     - aa
16 
17 bind2:
18   hostname: server2
19   remote_users:
20     - user0:
21       username: root
22       auth_type: ssh-passwd
23       password: 123456
24   groups:
25     - bj_group
26     - sh_group
27 
28   user_profiles:
29     - bb
new_bindhosts.yml
 1 web_servers:
 2   bind_hosts:
 3     - h1
 4     - h2
 5   user_profiles:
 6     - alex
 7 
 8 db_servers:
 9   user_profiles:
10     - jack
11     - alex
12     - rain
new_groups.yml
 1 h1:
 2   ip_addr: 192.168.1.1
 3   port: 20022
 4 
 5 h2:
 6   ip_addr: 192.168.1.100
 7   port: 22
 8 
 9 
10 mysql:
11   ip_addr: 192.168.43.5
12   port: 3306
13 
14 server1:
15   ip_addr: 192.168.1.1
16   port: 30000
17 
18 server2:
19   ip_addr: 10.4.4.22
20 
21 root:
22   ip_addr: 192.168.1.112
new_hosts.yml
 1 user0:
 2   auth_type:  ssh-passwd
 3   username: root
 4   password: 123456
 5 
 6 user1:
 7   auth_type:  ssh-passwd
 8   username: mysql
 9   password: 123456
10 
11 user2:
12   auth_type:  ssh-key
13   username: root
14   #password: abc!23
new_remoteusers.yml
 1 aa:
 2   password: 111
 3   groups:
 4     - web_servers
 5     - db_servers
 6   bind_hosts:
 7     - h1
 8     - h2
 9     - h3
10 bb:
11   password: 222
12 
13 cc:
14   password: 333
15   bind_hosts:
16     - h1
17     - h3
new_user.yml

六、测试样图

1、创建表结构

2、添加数据

 

3、登陆操作

4、命令审计

a、root用户(审计所有用户)

b、个人用户(审计当前用户操作命令)

原文地址:https://www.cnblogs.com/hy0822/p/9427179.html