堡垒机

 

一、作业需求:

1.业务需求

    兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难
    保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即使有100%把握,也要做好最坏的打算,想好故障预案

2.功能需求

    所有的用户操作日志要保留在数据库中
    每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
    允许用户对不同的目标设备有不同的访问权限,例:
        对10.0.2.34 有mysql 用户的权限
        对192.168.3.22 有root用户的权限
        对172.33.24.55 没任何权限
    分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限

二、表结构图

 三、

一、作业需求:
1.业务需求
兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难
保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即使有100%把握,也要做好最坏的打算,想好故障预案
2.功能需求
所有的用户操作日志要保留在数据库中
每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码
允许用户对不同的目标设备有不同的访问权限,例:
对10.0.2.34 有mysql 用户的权限
对192.168.3.22 有root用户的权限
对172.33.24.55 没任何权限
分组管理,即可以对设置进行分组,允许用户访问某组机器,但对组里的不同机器依然有不同的访问权限
二、博客地址:https://www.cnblogs.com/catepython/p/9177109.html
三、运行环境
操作系统:Win10
Python:3.6.4rcl
Pycharm:2017.3.4
readme

四、程序架构图

五、核心模块

bin目录

#-*-coding:utf-8 -*-
# Author: D.Gray
import os,sys
BASE_DESC = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#print(BASE_DESC)
sys.path.append(BASE_DESC)
from modules.actions import excute_from_command_line
excute_from_command_line(sys.argv)
start

conf目录

#-*-coding:utf-8 -*-
# Author: D.Gray
from modules import views
actions = {
'start_session': views.start_session, # 连接server
# 'stop': views.stop_server,
'syncdb': views.syncdb, # 同步数据
'create_users': views.create_users, # 创建users
'create_groups': views.create_groups, # 创建组
'create_hosts': views.create_hosts, # 创建主机
'create_bindhosts': views.create_bindhosts, # 创建绑定关系
'create_remoteusers': views.create_remoteusers, # 创建远程用户
'view_user_record': views.user_record_cmd # 查看用户操作命令
}
action_registers
#-*-coding:utf-8 -*-
# Author: D.Gray
import sqlalchemy
import os,sys
BASE_DESC = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#print(BASE_DESC)
sys.path.append(BASE_DESC)
CONN = 'mysql+pymysql://root:admin1988@localhost/mychine?charset=utf8'
setting

databases目录

#-*-coding:utf-8 -*-
# Author: D.Gray
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy_utils import ChoiceType
from sqlalchemy import Column,Integer,String,ForeignKey,UniqueConstraint,Table,Text,DateTime
from conf.setting import CONN
Base = declarative_base()
user_m2m_bind = Table(
'user_m2m_bind',Base.metadata,
Column('user_profile_id',Integer,ForeignKey('user_profile.id')),
Column('bind_host_id',Integer,ForeignKey('bind_host.id'))
)
bind_m2m_group = Table(
'bind_m2m_group',Base.metadata,
Column('group_id',Integer,ForeignKey('group.group_id')),
Column('bind_host_id',Integer,ForeignKey('bind_host.id'))
)
user_m2m_group = Table(
'user_m2m_group',Base.metadata,
Column('group_id',Integer,ForeignKey('group.group_id')),
Column('user_id',Integer,ForeignKey('user_profile.id'))
)
class Host(Base):
__tablename__ = 'host'
host_id = Column(Integer,primary_key=True)
host_name = Column(String(32),unique=True)
IP = Column(String(32),nullable=False,unique=True)
port = Column(Integer,default=22)
def __repr__(self):
return '<名称:【%s】 IP:【%s】 port:【%s】>'%(self.host_name,self.IP,self.port)
class Group(Base):
__tablename__ = 'group'
group_id = Column(Integer,primary_key=True)
group_name = Column(String(32),nullable=False,unique=True)
group_bind = relationship('BindHost',secondary = 'bind_m2m_group',backref = 'groups_key')
def __repr__(self):
return '<组名:【%s】>'%(self.group_name)
class RemUser(Base):
'''
远程登录用户
'''
__tablename__ = 'rem_user'
__table_args__ = (UniqueConstraint('auth_type','username','password',name = 'rems_uc'),)
id = Column(Integer,primary_key=True)
Auth_types = [
('ssh-password','SSH/Password'),
('ssh-key','SSH/Key'),
]
auth_type = Column(ChoiceType(Auth_types))
username = Column(String(32),nullable=False)
password = Column(String(32))
def __repr__(self):
return '<名称:【%s】 密码:【%s】 验证方式:【%s】>'%(self.username,self.password,self.auth_type)
class BindHost(Base):
'''
此表是用来实现操控主机IP 和 登录用户 之间的绑定关系
IP 远程登录名
192.168.111.128 root
192.168.111.129 admin_kyo
host_id remuser_id
'''
__tablename__ = 'bind_host'
__table_args__ = (UniqueConstraint('remuser_id','host_id',name = 'binds_uc'),)
id = Column(Integer,primary_key=True)
remuser_id = Column(Integer,ForeignKey('rem_user.id'),nullable=False)
host_id = Column(Integer,ForeignKey('host.host_id'),nullable=False)
bind_hosts = relationship('Host',backref = 'bind_hosts')
bind_remusers = relationship('RemUser',backref = 'bind_remusers')
def __repr__(self):
return '<IP:【%s】 远程登录名:【%s】>'
%(self.bind_hosts.IP,self.bind_remusers.username)
class UserProfile(Base):
'''
堡垒机用户
'''
__tablename__ = 'user_profile'
id = Column(Integer,primary_key=True)
user_name = Column(String(32),nullable=False)
password = Column(String(32),nullable=False)
profile_bind = relationship('BindHost',secondary = 'user_m2m_bind',backref = 'user_profiles')
profile_group = relationship('Group',secondary = 'user_m2m_group',backref = 'profile_groups')
def __repr__(self):
return '<名称:【%s】 密码:【%s】>'
%(self.user_name,self.password)
class AuditLog(Base):
'''
用户操作日志表
'''
__tablename__ = 'audit_log'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('user_profile.id'))
bind_host_id = Column(Integer, ForeignKey('bind_host.id'))
action_choices = [
(u'cmd', u'CMD'),
(u'login', u'Login'),
(u'logout', u'Logout'),
]
action_type = Column(ChoiceType(action_choices))
# 命令可能存的数值更大
# cmd = Column(String(255))
cmd = Column(Text(65535))
date = Column(DateTime)
user_profile = relationship("UserProfile")
bind_host = relationship("BindHost")
create_table

modules目录

#-*-coding:utf-8 -*-
# Author: D.Gray
from conf import action_registers
from modules import utils
def help_msg():
'''
帮助函数
print help msgs
:return:
'''
print("33[31;1mAvailable commands:33[0m")
for key in action_registers.actions:
print("	", key)
def excute_from_command_line(argvs):
'''
命令执行函数
print
:param argvs:
:return:
'''
if len(argvs) < 2:
help_msg()
exit()
if argvs[1] not in action_registers.actions:
utils.print_err("Command [%s] does not exist!" % argvs[1], quit=True)
# utils 工具箱
action_registers.actions[argvs[1]](argvs[1:])
actions
#-*-coding:utf-8 -*-
# Author: D.Gray
from database import create_table
from modules.db_conn import engine, session
from modules.utils import print_err
def bind_hosts_filter(vals):
'''
:param vals:
:return:
'''
print('**>', vals.get('bind_hosts'))
bind_hosts = session.query(create_table.BindHost).
filter(create_table.Host.host_name.in_(vals.get('bind_hosts'))).all()
if not bind_hosts:
print_err("none of [%s] exist in bind_host table." % vals.get('bind_hosts'), quit=True)
return bind_hosts
def user_profiles_filter(vals):
'''
:param vals:
:return:
'''
user_profiles = session.query(create_table.UserProfile).filter(create_table.UserProfile.user_name.
in_(vals.get('user_profiles'))).all()
if not user_profiles:
print_err("none of [%s] exist in user_profile table." % vals.get('user_profiles'), quit=True)
return user_profiles
common_filters
#-*-coding:utf-8 -*-
# Author: D.Gray
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from conf import setting
engine = create_engine(setting.CONN)
# 创建与数据库的会话session class ,注意,这里返回给session的是个class,不是实例
SessionCls = sessionmaker(bind=engine)
session = SessionCls()
db_conn
#-*-coding:utf-8 -*-
# Author: D.Gray
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
#
# This file is part of paramiko.
#
# Paramiko is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import socket
import sys
from paramiko.py3compat import u
from database import create_table
# from modules.views import log_recording
import datetime
import redis
import time
# windows does not have termios...
try:
import termios
import tty
has_termios = True
except ImportError:
has_termios = False
def interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording):
'''
:param chan:
:param user_obj:
:param bind_host_obj: 主机
:param cmd_caches: 命令列表
:param log_recording: 日志记录
:return:
'''
# 判断是否是windows shell
if has_termios:
posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording)
else:
windows_shell(chan)
def posix_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording):
'''
:param chan:
:param user_obj:
:param bind_host_obj:
:param cmd_caches:
:param log_recording:
:return:
'''
import select
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
chan.settimeout(0.0)
cmd = ''
tab_key = False
while True:
r, w, e = select.select([chan, sys.stdin], [], [])
if chan in r:
try:
x = u(chan.recv(1024))
if tab_key:
if x not in ('x07', '
'):
# print('tab:',x)
cmd += x
tab_key = False
if len(x) == 0:
sys.stdout.write('
*** EOF
')
# test for redis to mysql
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = sys.stdin.read(1)
if '
' != x:
cmd += x
else:
user_record_cmd = user_obj.username + '_user_record'
pool = redis.ConnectionPool(host='localhost', port=22)
user_record = [user_obj.id, bind_host_obj.id, 'cmd', cmd,
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]
r = redis.Redis(connection_pool=pool)
r.lpush(user_record_cmd, user_record)
cmd = ''
# 最后用户退出的时候取出来log_item 列表循环写入数据库
if '	' == x:
tab_key = True
if len(x) == 0:
break
chan.send(x)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
# thanks to Mike Looijmans for this code
def windows_shell(chan):
'''
:param chan:
:return:
'''
import threading
sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.

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

')
sys.stdout.flush()
break
sys.stdout.write(data.decode())
sys.stdout.flush()
writer = threading.Thread(target=writeall, args=(chan,))
writer.start()
try:
while True:
d = sys.stdin.read(1)
if not d:
break
chan.send(d)
except EOFError:
# user hit ^Z or F6
pass
interactive.py
#-*-coding:utf-8 -*-
# Author: D.Gray
import base64
import getpass
import os
import socket
import sys
import traceback
from paramiko.py3compat import input
from database import create_table
import redis
import datetime
import time
import paramiko
try:
import interactive
except ImportError:
from . import interactive
def ssh_login(user_obj, bind_host_obj, mysql_engine, log_recording):
'''
ssh登陆
:param user_obj:
:param bind_host_obj:
:param mysql_engine: 连接数据库
:param log_recording: 写日志记录
:return:
'''
# now, connect and use paramiko Client to negotiate SSH2 across the connection
try:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
print('*** Connecting...')
client.connect(bind_host_obj.host.ip,
bind_host_obj.host.port,
bind_host_obj.remote_user.username,
bind_host_obj.remote_user.password,
timeout=30)
cmd_caches = []
chan = client.invoke_shell()
# print(repr(client.get_transport()))
print('*** Here we go!
')
# 连接redis
pool = redis.ConnectionPool(host='192.168.84.66', port=6379)
# 传一个命令列表给redis
user_record = [user_obj.id, bind_host_obj.id, 'login',
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())]
r = redis.Redis(connection_pool=pool)
# 用用户名做key前缀,避免冲突
key_name = str(user_obj.username)+'_login'
r.lpush(key_name, user_record)
interactive.interactive_shell(chan, user_obj, bind_host_obj, cmd_caches, log_recording)
chan.close()
client.close()
# 数据库写入操作
login_record = r.lrange(key_name, 0, -1)
login_redis_record = login_record[0].decode().replace('[', '').replace(']', '').split(',')
log_item = create_table.AuditLog(user_id=login_redis_record[0],
bind_host_id=login_redis_record[1],
action_type='login',
cmd='login',
date=login_redis_record[3].replace("'", ''))
cmd_caches.append(log_item)
log_recording(user_obj, bind_host_obj, cmd_caches)
user_record_cmd = user_obj.username+'_user_record'
cmd_redis_record = r.lrange(user_record_cmd, 0, -1)
for i in cmd_redis_record:
cmd_caches = []
v = i.decode().replace('[', '').replace(']', '').split(',')
v2 = v[3].replace("'", '')
# print(v[0], v[1], v[2], v[3], v[4])
log_item = create_table.AuditLog(user_id=v[0],
bind_host_id=v[1],
action_type='cmd',
cmd=v2, date=v[4].replace("'", ''))
cmd_caches.append(log_item)
log_recording(user_obj, bind_host_obj, cmd_caches)
# 当退出的时候将redis的值写入到数据库并且清空redis
logout_caches = []
logout_caches.append(create_table.AuditLog(user_id=user_obj.id,
bind_host_id=bind_host_obj.id,
action_type='logout',
cmd='logout',
date=datetime.datetime.now()))
log_recording(user_obj, bind_host_obj, logout_caches)
# 清空keys
 r.delete(key_name)
r.delete(user_record_cmd)
except Exception as e:
print('*** Caught exception: %s: %s' % (e.__class__, e))
traceback.print_exc()
try:
client.close()
except:
pass
sys.exit(1)
ssh_login
#-*-coding:utf-8 -*-
# Author: D.Gray
import yaml
try:
from yaml import CLoader as Loader, CDumper as Dumper
except ImportError:
from yaml import Loader, Dumper
def print_err(msg, quit=False):
'''
:param msg:
:param quit:
:return:
'''
output = "33[31;1mError: %s33[0m" % msg
if quit:
exit(output)
else:
print(output)
def yaml_parser(yml_filename):
'''
yaml方法load yaml file and return
:param yml_filename:
:return:
'''
try:
yaml_file = open(yml_filename, 'r')
data = yaml.load(yaml_file)
return data
except Exception as e:
print_err(e)
utils
#-*-coding:utf-8 -*-
# Author: D.Gray
from database import create_table
from pymysql.err import IntegrityError
from conf import setting
from modules.utils import print_err, yaml_parser
from modules.db_conn import engine, session
from modules import ssh_login
from modules import common_filters
import codecs,os
def syncdb(argvs):
'''
创建表结构方法
:param argvs:
:return:
'''
print("Syncing DB....")
engine = create_table.create_engine(setting.CONN, echo=True)
create_table.Base.metadata.create_all(engine) # 创建所有表结构
def create_hosts(argvs):
'''
create 主机
:param argvs:
:return:
'''
if '-f' in argvs:
# 指定一个文件名否则报错
hosts_file = argvs[argvs.index("-f") +1]
host_path = os.path.join(setting.BASE_DESC,hosts_file)
#print('hosts_path:',host_path)
else:
print_err("invalid usage, should be:
create_hosts -f <the new hosts file>", quit=True)
source = yaml_parser(host_path) # 传文件回来
if source: # 循环字典
print(source)
for key, val in source.items():
print(key, val)
obj = create_table.Host(host_name=key, IP=val.get('ip'), port=val.get('port') or 22)
# 添加到表
try:
session.add(obj)
except IntegrityError as e:
print('主机名和主机IP是唯一值已在数据库创建:',e)
else:
session.commit()
def create_remoteusers(argvs):
'''
create 远程用户数据
:param argvs:
:return:
'''
if '-f' in argvs:
remoteusers_file = argvs[argvs.index("-f") +1]
host_path = os.path.join(setting.BASE_DESC, remoteusers_file)
else:
print_err("invalid usage, should be:
create_remoteusers -f <the new remoteusers file>", quit=True)
source = yaml_parser(host_path)
if source:
for key, val in source.items():
print(key, val)
obj = create_table.RemUser(username=val.get('username'), auth_type=val.get('auth_type'),
password=val.get('password'))
session.add(obj)
session.commit()
def create_users(argvs):
'''
create 堡垒机用户数据
create little_finger access user
:param argvs:
:return:
'''
if '-f' in argvs:
user_file = argvs[argvs.index("-f") +1 ]
host_path = os.path.join(setting.BASE_DESC, user_file)
else:
print_err("invalid usage, should be:
createusers -f <the new users file>",quit=True)
source = yaml_parser(host_path)
if source:
for key, val in source.items():
print(key, val)
obj = create_table.UserProfile(user_name=key, password=val.get('password'))
if val.get('groups'):
groups = session.query(create_table.Group).
filter(create_table.Group.group_name.in_(val.get('groups'))).all()
if not groups:
print_err("none of [%s] exist in group table." % val.get('groups'), quit=True)
obj.groups = groups
if val.get('bind_hosts'):
bind_hosts = common_filters.bind_hosts_filter(val)
obj.bind_hosts = bind_hosts
#print(obj)
 session.add(obj)
session.commit()
def create_groups(argvs):
'''
create 组数据
create groups
:param argvs:
:return:
'''
if '-f' in argvs:
group_file = argvs[argvs.index("-f") + 1]
host_path = os.path.join(setting.BASE_DESC, group_file)
else:
print_err("invalid usage, should be:
creategroups -f <the new groups file>", quit=True)
source = yaml_parser(host_path)
if source:
for key, val in source.items():
print(key, val)
obj = create_table.Group(group_name=key)
if val.get('bind_hosts'):
bind_hosts = common_filters.bind_hosts_filter(val)
obj.bind_hosts = bind_hosts
if val.get('user_profiles'):
user_profiles = common_filters.user_profiles_filter(val)
obj.user_profiles = user_profiles
session.add(obj)
session.commit()
def create_bindhosts(argvs):
'''
create IP和远程用户关联数据
create bind hosts
:param argvs:
:return:
'''
if '-f' in argvs:
bindhosts_file = argvs[argvs.index("-f") + 1]
host_path = os.path.join(setting.BASE_DESC, bindhosts_file)
else:
print_err("invalid usage, should be:
create_hosts -f <the new bindhosts file>",quit=True)
source = yaml_parser(host_path)
if source:
for key, val in source.items():
print(key, val)
# 获取到了主机
host_obj = session.query(create_table.Host).
filter(create_table.Host.host_name == val.get('host_name')).first()
# 取hostname
assert host_obj # 断言,必须存在
for item in val['remote_users']: # 判断
print(item)
assert item.get('auth_type')
if item.get('auth_type') == 'ssh-password': # 判断认证password
remoteuser_obj = session.query(create_table.RemUser).filter(
create_table.RemUser.username == item.get('username'),
create_table.RemUser.password == item.get('password')
).first()
else:
# 获取远程用户
remoteuser_obj = session.query(create_table.RemUser).filter(
create_table.RemUser.username == item.get('username'),
create_table.RemUser.auth_type == item.get('auth_type'),
).first()
if not remoteuser_obj: # 没取到,程序退出
print_err("RemoteUser obj %s does not exist." % item, quit=True)
bindhost_obj = create_table.BindHost(host_id=host_obj.host_id, remuser_id=remoteuser_obj.id)
session.add(bindhost_obj) # 获取到关系后添加session
# for groups this host binds to
if source[key].get('groups'): # 获取组
group_objs = session.query(create_table.Group).filter(create_table.Group.group_name.in_
(source[key].get('groups'))).all()
assert group_objs
print('groups:', group_objs)
bindhost_obj.host_groups = group_objs
# for user_profiles this host binds to
if source[key].get('user_profiles'): # 判断是否直接属于哪一台机器
userprofile_objs = session.query(create_table.UserProfile).
filter(create_table.UserProfile.user_name.in_(
source[key].get('user_profiles')
)).all()
assert userprofile_objs
print("userprofiles:", userprofile_objs)
bindhost_obj.user_profiles = userprofile_objs
# print(bindhost_obj)
 session.commit()
def auth():
'''
用户验证
do the user login authentication
:return:
'''
count = 0
while count < 3:
username = input("33[32;1mUsername>>>:33[0m").strip()
if len(username) == 0:
continue
password = input("33[32;1mPassword>>>:33[0m").strip()
if len(password) == 0:
continue
user_obj = session.query(create_table.UserProfile).filter(create_table.UserProfile.user_name == username,
create_table.UserProfile.password == password).first()
if user_obj:
return user_obj
else:
print("wrong username or password, you have %s more chances." % (3-count-1))
count += 1
else:
print_err("too many attempts.")
def welcome_msg(user):
'''
:param user: 接收start_session函数的user
:return:
'''
WELCOME_MSG = '''33[32;1m
------------- Welcome [%s] login TinyServer -------------
33[0m''' % user.user_name
print(WELCOME_MSG)
def start_session(argvs):
'''
开始远程登陆函数
:param argvs:
:return:
'''
print('going to start sesssion ')
user = auth() #调用auth认证函数 来判断输入的堡垒机用户是否存在
if user:
welcome_msg(user)
# print(user.bind_hosts)
# print(user.host_groups)
exit_flag = False
while not exit_flag:
if user.profile_bind:
# 显示未分组的机器
print('33[32;1mz.	 z查看未分组主机列表/任意键查看已分组主机列表 (%s)33[0m' % len(user.profile_bind))
for index, group in enumerate(user.profile_group):
print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
# 用户输入
choice = input("[%s]:" % user.user_name).strip()
if len(choice) == 0:
continue
# 如果是z 打印未分组机器
if choice == 'z':
print("------ Group: 未分组主机 ------")
for index, bind_host in enumerate(user.profile_bind):
print(" %s.	%s@%s(%s)" % (index,
bind_host.bind_remusers.username,
bind_host.bind_hosts.host_name,
bind_host.bind_hosts.IP,
))
print("----------- END -----------")
elif choice.isdigit(): # 打印分组的机器
choice = int(choice)
if choice < len(user.profile_group):
print("------ Group: %s ------" % user.profile_group[choice].group_name)
for index, bind_host in enumerate(user.profile_group[choice].group_bind):
print(" %s.	%s@%s(%s)" % (index,
bind_host.bind_remusers.username,
bind_host.bind_hosts.host_name,
bind_host.bind_hosts.IP,
))
print("----------- END -----------")
# host selection 选择机器去登陆
while not exit_flag:
user_option = input("[(b)back, (q)quit, select host to login]:").strip()
if len(user_option) == 0:
continue
if user_option == 'b':
break
if user_option == 'q':
exit_flag = True
if user_option.isdigit():
user_option = int(user_option)
if user_option < len(user.host_groups[choice].bind_hosts):
print('host:', user.host_groups[choice].bind_hosts[user_option])
# print('audit log:', user.host_groups[choice].bind_hosts[user_option].audit_logs)
ssh_login.ssh_login(user, # 传用户,用户组,连上对应的
 user.host_groups[choice].bind_hosts[user_option],
session, log_recording)
else:
print("no this option..")
def log_recording(user_obj, bind_host_obj, logs):
'''
flush user operations on remote host into DB
:param user_obj:
:param bind_host_obj:
:param logs: list format [logItem1,logItem2,...]
:return:
'''
# print("33[41;1m--logs:33[0m", logs)
 session.add_all(logs)
session.commit()
def user_record_cmd(argvs):
'''
查看操作记录方法
:param argvs:
:return:
'''
print('going to start view record')
user = auth()
# 默认root可以查所有人的记录
if user.user_name == 'root':
print('welcome 【%s】 ' % user.user_name)
exit_flag = False
# 用户对象
user_obj = session.query(create_table.UserProfile).filter().all()
# 循环查看堡垒机用户操作
while not exit_flag:
for user_profile_list in user_obj:
# 打印堡垒机用户,根据堡垒机用户ID选择其管辖的机器并打印日志
print("%s.	%s" % (user_profile_list.id, user_profile_list.user_name))
choice = input("[%s]:" % user.user_name).strip()
for user_profile_list in user_obj:
if str(choice) == str(user_profile_list.id):
if user_profile_list.profile_bind:
# 显示未分组的机器
print('33[32;1mz.	ungroupped hosts (%s)33[0m' % len(user_profile_list.profile_bind))
else:
print(' no binding groups ')
for index, group in enumerate(user_profile_list.profile_group):
print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
choice = input("[%s]:" % user.user_name).strip()
if choice.isdigit(): # 打印分组的机器
choice = int(choice)
if choice < len(user_profile_list.profile_group):
print("------ Group: %s ------" % user_profile_list.profile_group[choice].group_name)
for index, bind_host in enumerate(user_profile_list.profile_group[choice].group_bind):
print(" %s.	%s@%s(%s)" % (index,
bind_host.remote_user.user_name,
bind_host.host.host_name,
bind_host.host.IP,
))
print("----------- END -----------")
# host selection 选择机器去查看操作信息
while not exit_flag:
user_option = input("[(b)back, (q)quit, select host to login]:").strip()
if len(user_option) == 0:
continue
if user_option == 'b':
break
if user_option == 'q':
exit_flag = True
if user_option.isdigit():
user_option = int(user_option)
if user_option < len(user_profile_list.profile_group[choice].bind_hosts):
# print('host:', user_profile_list.host_groups[choice].bind_hosts[user_option])
data = 
session.query(create_table.AuditLog).filter(
create_table.AuditLog.user_id == user_profile_list.id,
create_table.AuditLog.bind_host_id ==
user_profile_list.profile_group[choice].
group_bind[user_option].id).all()
if data:
for index, i in enumerate(data):
# redis 写入value的时候带有了	 
 等需要转义
# 第一个注释从数据库里读注释的这种不能转移	,
# 第二个和现行的俩种中文转义有些问题
# print(i.user_id, i.bind_host_id, i.action_type, i.cmd, i.date)
# print(i.user_id, i.bind_host_id, i.action_type,
# codecs.getdecoder("unicode_escape")(i.cmd)[0], i.date)
# print(i.user_id, i.bind_host_id, i.action_type,
# i.cmd.encode().decode('unicode-escape'), i.date)
print(index, i.date, i.cmd.encode().decode('unicode-escape'))
else:
print('no record in host:', user_profile_list.profile_group[choice].
group_bind[user_option])
# 其他人只能查自己的操作记录
else:
exit_flag = False
while not exit_flag:
if user.profile_bind:
# 显示未分组的机器
print('33[32;1mz.	ungroupped hosts (%s)33[0m' % len(user.profile_bind))
for index, group in enumerate(user.profile_group):
print('33[32;1m%s.	%s (%s)33[0m' % (index, group.group_name, len(group.group_bind)))
choice1 = input("[%s]:" % user.user_name).strip()
# 查询选项
if choice1 == 'z':
print("------ Group: ungroupped hosts ------")
for index, bind_host in enumerate(user.profile_bind):
print(" %s.	%s@%s(%s)" % (index,
bind_host.remote_user.user_name,
bind_host.host.host_name,
bind_host.host.IP,
))
print("----------- END -----------")
elif choice1.isdigit(): # 打印分组的机器
choice = int(choice1)
if choice < len(user.host_groups):
print("------ Group: %s ------" % user.profile_group[choice].name)
for index, bind_host in enumerate(user.profile_group[choice].group_bind):
print(" %s.	%s@%s(%s)" % (index,
bind_host.remote_user.user_name,
bind_host.host.host_name,
bind_host.host.IP,
))
print("----------- END -----------")
# host selection 选择机器去查看操作信息
while not exit_flag:
user_option = input("[(b)back, (q)quit, select host to view record]:").strip()
if len(user_option) == 0:
continue
if user_option == 'b':
break
if user_option == 'q':
exit_flag = True
if user_option.isdigit():
user_option = int(user_option)
if user_option < len(user.profile_group[choice].group_bind):
data = session.query(create_table.AuditLog)
.filter(create_table.AuditLog.user_id == user.id,
create_table.AuditLog.bind_host_id == user.profile_group[choice].
group_bind[user_option].id).all()
# print(user.host_groups[choice].bind_hosts[user_option].id)
if data:
for index, i in enumerate(data):
print(index, i.date, i.cmd.encode().decode('unicode-escape'))
else:
print('no record in host:', user.profile_group[choice].group_bind[user_option])
else:
print("no this option..")
views

share目录

bind1:
host_name: server1
remote_users:
- user0:
username: root
auth_type: ssh-password
password: 123456
groups:
- bj_group
user_profiles:
- sean
bind2:
host_name: server2
remote_users:
- user0:
username: root
auth_type: ssh-password
password: 123456
groups:
- bj_group
- sh_group
user_profiles:
- sean
- jack
bind3:
host_name: server3
remote_users:
- user0:
username: root
auth_type: ssh-password
password: 123456
groups:
- bj_group
- sh_group
user_profiles:
- sean
- jack
bind4:
host_name: server2
remote_users:
- user2:
username: colin
auth_type: ssh-password
password: 123@123
groups:
- web_servers
user_profiles:
- root
bind5:
host_name: server3
remote_users:
- user3:
username: web
auth_type: ssh-password
password: 12345678
- user1:
username: mysql
auth_type: ssh-password
password: 12345678
groups:
- web_servers
- db_servers
user_profiles:
- root
new_bindhost.yml
bj_group:
user_profiles:
- sean
sh_group:
user_profiles:
- jack
db_servers:
user_profiles:
- root
web_servers:
user_profiles:
- root
new_groups.yml
server1:
ip: 192.168.111.128
port: 22
server2:
ip: 192.168.111.129
port: 22
server3:
ip: 192.168.111.130
port: 22
new_hosts
user0:
auth_type: ssh-password
username: root
password: 123456
user1:
auth_type: ssh-password
username: mysql
password: 12345678
user2:
auth_type: ssh-password
username: colin
password: 123@123
user3:
auth_type: ssh-password
username: web
password: 12345678
user4:
auth_type: ssh-key
username: root
new_remoteusers.yml
root:
password: 123@456
sean:
password: 123456
jack:
password: 123456
new_user.yml

六、测试路程图

 
 
 
原文地址:https://www.cnblogs.com/gaodi2345/p/11412839.html