python连接linux堡垒机_(转)用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

#

# 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

你可能感兴趣的:(python连接linux堡垒机_(转)用Python写堡垒机项目)