(转)用Python写堡垒机项目
原文:https://blog.csdn.net/ywq935/article/details/78816860
前言 
堡垒机是一种运维安全审计系统。主要的功能是对运维人员的运维操作进行审计和权限控制,风险规避。同时堡垒机还有账号集中管理,单点登陆的功能。
堡垒机有以下两个至关重要的功能: 
集中管理 
安全审计
当公司的服务器变的越来越多后,需要操作这些服务器的人就肯定不只是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操作业务系统,如果权限分配不当就会存在很大的安全风险,举几个场景例子: 
设想你们公司有300台Linux服务器,A开发人员需要登录其中5台WEB服务器查看日志或进行问题追踪等事务,同时对另外10台hadoop服务器有root权限,在有300台服务器规模的网络中,按常理来讲你是已经使用了ldap权限统一认证的,你如何使这个开发人员只能以普通用户的身份登录5台web服务器,并且同时允许他以管理员的身份登录另外10台hadoop服务器呢?并且同时他对其它剩下的200多台服务器没有访问权限
小型公司的运维团队为了方面,整个运维团队的运维人员还是共享同一套root密码,这样内部信任机制虽然使大家的工作方便了,但同时存在着极大的安全隐患,很多情况下,一个运维人员只需要管理固定数量的服务器,毕竟公司分为不同的业务线,不同的运维人员管理的业务线也不同,但如果共享一套root密码,其实就等于无限放大了每个运维人员的权限,也就是说,如果某个运维人员想干坏事的话,后果很严重。为了降低风险,于是有人想到,把不同业务线的root密码改掉就ok了么,也就是每个业务线的运维人员只知道自己的密码,这当然是最简单有效的方式,但问题是如果同时用了ldap,这样做又比较麻烦,即使设置了root不通过ldap认证,那新问题就是,每次有运维人员离职,他所在的业务线的密码都需要重新改一次。
因此,堡垒机的诞生就是为了规避这些高风险的问题,同时减少繁琐的重复性工作。
工作流程图:
一、需求分析
1.所有生产服务器配置iptables安全策略,只能通过堡垒机来登陆,用户首先登陆进堡垒机,再通过堡垒机跳转登陆target host. 
2.各IT组,按职能划分一个统一的可以真实登陆target host的账户 
3.组内的成员,使用自己的账号登陆堡垒机,再使用小组账号登陆target host. 
4.审计。记录下各人员的操作记录,出现问题时可以溯源
二、表结构设计
本着最少的字段数据冗余的原则,设计了如下几张表,如果各路大神有更好的设计思路,请指点一二~
表创建代码:
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from sqlalchemy import Table, Column, Enum,Integer,String, ForeignKey,UniqueConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from conf import config
Base = declarative_base()
user_m2m_group_bind_host = Table('user_m2m_group_bind_host', Base.metadata,
                        Column('id',Integer,autoincrement=True,primary_key=True),
                        Column('user_id', Integer, ForeignKey('user.id')),
                        Column('group_bind_host_id', Integer, ForeignKey('group_bind_host.id')),
                        )
user_m2m_group = Table('user_m2m_group', Base.metadata,
                               Column('user_id', Integer, ForeignKey('user.id')),
                               Column('group_id', Integer, ForeignKey('group.id')),
                               )
class Host(Base):
    __tablename__ = 'host'
    id = Column(Integer,primary_key=True)
    hostname = Column(String(64),unique=True)
    ip = Column(String(64),unique=True)
    port = Column(Integer,default=22)
    groups=relationship('Group',secondary='group_bind_host')
def __repr__(self):
        return self.hostname
class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True)
    login_passwd=Column(String(64))
    bind_hosts = relationship("Host",secondary='group_bind_host')
    users=relationship('User',secondary='user_m2m_group')
def __repr__(self):
        return self.name
class Group_Bind_Host(Base):
    __tablename__ = "group_bind_host"
    __table_args__ = (UniqueConstraint('group_id','host_id', name='_host_remoteuser_uc'),)
    id = Column(Integer, primary_key=True)
    group_id = Column(Integer, ForeignKey('group.id'))
    host_id = Column(Integer,ForeignKey('host.id'))
users=relationship('User',secondary='user_m2m_group_bind_host',backref='group_bind_hosts')
#host = relationship("Host")
    #host_group = relationship("Group",backref="bind_hosts")
    #group = relationship("Group")
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer,autoincrement=True,primary_key=True)
    username = Column(String(32))
    password = Column(String(128))
    groups = relationship("Group",secondary="user_m2m_group")
    bind_hosts = relationship("Group_Bind_Host", secondary='user_m2m_group_bind_host')
def __repr__(self):
        return self.username
class Log_audit(Base):
    __tablename__= 'log_audit'
    id = Column(Integer,autoincrement=True,primary_key=True)
    user_id = Column(Integer)
    user_name = Column(String(32))
    host_ip = Column(String(32))
    login_user = Column(String(32))
    action_type = Column(String(16))
    cmd=Column(String(128))
    date = Column(String(16))
if __name__ == "__main__":
    engine=create_engine(config.engine_param)
    Base.metadata.create_all(engine)  # 创建表结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
三、项目代码
1.整体结构:
2.功能说明: 
1.管理功能 
——表结构初始化创建 
——添加组 
——添加主机 
——添加用户 
——添加组-主机绑定
doc目录下提供了几个添加元素的example文档,使用yaml模块解析这些文档,解析为dict数据类型,将解析出的数据添加进数据库内,例如:
对应实现添加user功能的函数:
2.用户视图 
——查看属组 
—查看属组有权限登陆的主机 
——直接输入IP登陆主机 
—查看该IP主机是否有可用的有权限的账户可供登陆 
——开始会话连接,执行命令时向记录审计日志表添加item
ssh会话实现: 
使用了paramiko的demo模块,这个模块本身的交互功能是使用select模型来实现的,使用select监听会话句柄、sys.stdin(标准输入)的可读可写状态,实现字符在终端界面的输入输出。关于I/O多路复用几种模型的个人解读,可以翻看此前的博客:网络编程之I/O模型(以吃牛肉面为例)。 
对paramiko交互模块进行修改添加记录功能:当标准输入触发回车键时(代表一个命令输入完毕开始执行),记录下执行人、登陆用户、时间、命令内容,写入数据库。修改后的交互模块代码如下:
# 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
import datetime
from moudle import table_init
from moudle.db_conn import session
# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False
def interactive_shell(chan,user_obj,choose_host,choose_group):
    if has_termios:
        posix_shell(chan,user_obj,choose_host,choose_group)
    else:
        windows_shell(chan)
def posix_shell(chan,user_obj,choose_host,choose_group):
    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 ('\t','\r\n'):
                            #print('tab:',x)
                            cmd += x
                        tab_key = False
                    if len(x) == 0:
                        sys.stdout.write('\r\n*** EOF\r\n')
                        break
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
if sys.stdin in r:
                x = sys.stdin.read(1)
                if '\r' != x:
                    cmd +=x      #输入字符不包含回车,则命令还未输入完成,包含回车且输入字符长度大于0,则记录日志
                if '\r' == x and len(cmd)>0:
                    log_item = table_init.Log_audit(user_id=user_obj.id,
                                               user_name=user_obj.username,
                                          host_ip=choose_host.ip,
                                          login_user=choose_group.name,
                                          action_type='cmd',
                                          cmd=cmd ,
                                          date=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
                    session.add(log_item)
                    session.commit()
                    cmd = ''
if '\t' == 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):
    import threading
sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
def writeall(sock):
        while True:
            data = sock.recv(256)
            if not data:
                sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
                sys.stdout.flush()
                break
            sys.stdout.write(data)
            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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
四、运行效果:
在堡垒机上添加用户,在目标主机上添加一个对应登陆用户: 
在堡垒机上该用户环境变量配置文件中加入:
/usr/local/python3/bin/python3  /usr/local/packages/MyFortress/bin/user_interface.py
1
使其打开shell后自动运行堡垒机程序,效果如下:
查看数据库审计日志表,记录正常:
写了一个星期,终于能跑起来了,github链接: 
https://github.com/yinwenqin/MyFortress-Server
--------------------- 
作者:ywq935 
来源:CSDN 
原文:https://blog.csdn.net/ywq935/article/details/78816860?utm_source=copy 
版权声明:本文为博主原创文章,转载请附上博文链接!
(转)用Python写堡垒机项目的更多相关文章
- 基于python的堡垒机
		一 堡垒机的架构 堡垒机的核心架构通常如下图所示: 二.堡垒机的一般执行流程 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码) 用户登陆堡垒机,输入堡垒机用户名密码,显示当前用户 ... 
- 审计系统---堡垒机项目之用户交互+session日志写入数据库[完整版]
		2018-06-20 时隔一个多月,忘记了之前的所有操作,重拾起来还是听不容易的,想过放弃,但还是想坚持一下,加油. 世界杯今天葡萄牙1:0战胜摩洛哥,C 罗的一个头球拯救了时间,目前有4个射球,居2 ... 
- python作业堡垒机(第十三周)
		作业需求: 1. 所有的用户操作日志要保留在数据库中 2. 每个用户登录堡垒机后,只需要选择具体要访问的设置,就连接上了,不需要再输入目标机器的访问密码 3. 允许用户对不同的目标设备有不同的访问权限 ... 
- Python之堡垒机
		本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功 ... 
- python之堡垒机(第九天)
		本节作业: 通过使用paramiko和sqlalchemy实现堡垒机功能 主要功能实现: 1.用户登录堡垒机后,无需知道密码或密钥可以SSH登录远端服务器: 2.用户对一个组内所有主机批量执行指定命令 ... 
- 无监控不运维——使用 Python 写一个小小的项目监控
		在公司里做的一个接口系统,主要是对接第三方的系统接口,所以,这个系统里会和很多其他公司的项目交互.随之而来一个很蛋疼的问题,这么多公司的接口,不同公司接口的稳定性差别很大,访问量大的时候,有的不怎么行 ... 
- 用paramiko写堡垒机
		paramiko paramiko模块,基于SSH用于连接远程服务器并执行相关操作. 基本用法 SSHClient 基于用户名密码连接: 基础用法: import paramiko # 创建SSH对象 ... 
- python 有关堡垒机的那些事
		堡垒机为了保证系统或服务器的安全性,防止运维和开发人员胡乱操作服务器,导致不必要的损失,使用堡垒机来完成对运维和开发人员的授权.用户统一登录堡垒机账号来操作系统或服务器.堡垒机等于成了生产系统的SSO ... 
- 源码:自己用Python写的iOS项目自动打包脚本
		http://www.cocoachina.com/ios/20160307/15501.html 什么?又要测试包! 做iOS开发几年了,每天除了码代码,改Bug之外,最让我烦恼的莫过于测试的妹子跑 ... 
随机推荐
- 2018.08.22 NOIP模拟 shop(lower_bound+前缀和预处理)
			Shop 有 n 种物品,第 i 种物品的价格为 vi,每天最多购买 xi 个. 有 m 天,第 i 天你有 wi 的钱,你会不停购买能买得起的最贵的物品.你需要求出你每天会购买多少个物品. [输入格 ... 
- Django入门与实践-第19章:主题回复(完结)
			http://127.0.0.1:8000/boards/1/topics/1/reply/ http://127.0.0.1:8000/boards/1/topics/1/ #myproject/u ... 
- 破解Unity5.3.4f1
			破解的目的是将受限的个人版变为全功能的Pro版,破解后就可以使用所有功能了,界面也变成了黑色的主题. 破解网址(支持最新版的5.3.4f1): http://www.ceeger.com/forum/ ... 
- struts2 一些注解
			实现的JSP页面位置 web-root/jsp/user/add.jsp /update.jsp // /* @Namespace("/t") @AllowedMethods(va ... 
- (KMP 求循环节)The Minimum Length
			http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/F The Minimum Length Time Limit: ... 
- hbase使用MapReduce操作2(微博表实现)
			package com.yjsj.weibo; import java.io.IOException; import java.util.ArrayList; import java.util.Ite ... 
- 移动端与PC端的viewport
			第一种解析: 设备像素,就是我们直觉上觉得"靠谱"的像素,这些像素为所使用的各种设备提供了正规的分辨率,并且其值可以通过(通常情况下)从screen.width/height属性中 ... 
- Android-DateUtil工具类
			时间相关工具类 public class DateUtil { private DateUtil(){} /** * 枚举日期格式 */ public enum DatePattern{ /** * ... 
- boot分区剩余空间不足
			Linux boot分区用于存放内核文件以及Linux一些启动配置文件,一般情况下分区大小为500M足够使用,如果出现空间不足的问题可以使用以下方法来解决. 查看已经安装的内核 dpkg --ge ... 
- supervisor配置环境变量(PATH)
			app配置中增加: environment=PATH="/PATH/TO/anaconda3/bin" supervisord在linux中启动默认继承了linux的环境变量,在这 ... 
