Python自动化开发学习13-堡垒机开发
堡垒机介绍
很多人觉得,堡垒机就是跳板机,这是不全面的。跳板功能只是堡垒机所具备的功能的其中一项。堡垒机还有以下两个至关重要的功能:
权限管理 : 用户使用堡垒机账号登录堡垒机系统。不需要知道别的主机的账号和密码,都是由堡垒机系统管理的。用户可以登录的主机和每台主机上所拥有的权限也是由堡垒机系统管理的。
审计管理 : 用户的所有操作都是在堡垒机上执行的,所以都会被记录保存。其实对于操作的结果(就是屏幕上打印的结果),如果需要也是可以记录保存的。不过一般不那么做,而且数据量也会有点大。
开源堡垒机项目
号称没有好的开源项目,因为底层的SSH还是通过paramiko模块的长链接的ssh实现的。但是这个和原生的ssh比还是不太稳定,不太好用。不适合生产环境。要好用还是得去改原生的ssh,但是我们不会,我们只会改python。总之这章就是实现一个堡垒机的功能,真要做个好用的以后再说吧。
比较有名的大概是这个:jumpserver - 开源跳板机
paramiko模块的长连接
虽然上面说了,用这个模块不稳定,但是我们也没办法,毕竟只会这个。
paramiko之前已经学习过了,不过当时用的都是短连接。即:连接一次,执行一条命令,返回结果,断开。这里我们要用长连接。
长连接的代码在demos文件夹下的demo.py这个文件里,另外还要用到interactive.py这个文件。安装模块的时候不安装这个demos文件夹,所以我们去github上下下来:
https://github.com/paramiko/paramiko/tree/master/demos
利用demo.py程序,我们可以进行ssh的长连接。下面的开发都是基于这个程序为基础,在这个程序的基础上,修改该程序的源码添加上记录用户输入的代码,并写入数据库。于是审计管理便实现了。
堡垒机构架
任何人只能通过堡垒机登录设备,堡垒机可以链接管理所有的设备:
配置环境变量
还需要确保用户一登录堡垒机就进入你的程序,并且不能退出(一退出就退出整个堡垒机),就是只能在你的做的shell下运行,不能进入原生的shell。可以通过设置环境变量实现:
环境变量保存在这里: ~/.bashrc
,修改文件在最后加上这句: python3 /etc/myJunpServer.py
直接运行你的程序。首次设置完成后如果要让它立刻生效,可以执行这个命令:source .bashrc
加载最新的配置。
表结构设计
主机表:
id | 主机名 | 端口号 | 注释 |
---|---|---|---|
自增id | IP或域名 | 数值类型,默认22 | 注释信息 |
主机认证表:
id | 名称 | 用户名 | 密码 | 注释 |
---|---|---|---|---|
自增id | 唯一约束 | 用户名 | 明文的字符串 | 注释信息 |
主机认证绑定表(分别和主机表以及主机认证表建立一对一关联):
这张就是主机表和主机认证表的多对多关联的结合表,原本不需要单独建立的。但是这张表多加了一个自增id,id还需要去分别和账号表以及群组表建立多对多关联。总之,这样操作是可以实现的,也没有或者说不会3张表之间建立多对多关联的方法,实际试了一下也确实没实现,暂时只会这么搞。
主机id 和 认证id 要做联合唯一
id | 主机id | 认证id |
---|---|---|
自增id | 关联主机表的id | 关联主机认证表的id |
主机群组表(和主机认证绑定表多对多关联,和账号表多对多关联):
不同的群组里可能包含相同的机器,但是不同群组中同一台机器对应的权限可能不同。所以这里直接关联主机与认证的一条绑定关系。
id | 群组名 | 备注 |
---|---|---|
自增id | 唯一 | 备注信息 |
堡垒机账号表(和主机认证绑定表多对多关联,和主机群组表多对多关联):
id | 用户名 | 密码 |
---|---|---|
自增id | 唯一约束 | 加密存储 |
审计日志记录表:
通过绑定表的id,可以查询到关联的主机和关联的认证信息
id | 时间 | 用户id | 绑定表id | 操作消息 |
---|---|---|---|---|
自增id | 操作时间 | 关联账号表的id | 关联绑定表的id | 操作内容 |
创建表结构的代码如下:
from sqlalchemy import create_enginefrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import Column, Integer, String, Enum, DateTimefrom sqlalchemy import ForeignKey, Table, UniqueConstraintfrom sqlalchemy.orm import relationship engine = create_engine("mysql+pymysql://admin:admin123@192.168.3.103/week12", encoding='utf-8', echo=True) Base = declarative_base() group_to_bind = Table('group_to_bind', Base.metadata, Column('group_id', Integer, ForeignKey('host_group.id')), Column('bind_host_id', Integer, ForeignKey('bind_host.id')), ) # 主机群组和绑定表的结合表user_to_bind = Table('user_to_bind', Base.metadata, Column('user_id', Integer, ForeignKey('user.id')), Column('bind_host_id', Integer, ForeignKey('bind_host.id')), ) # 用户和绑定表的结合表user_to_group = Table('user_to_group', Base.metadata, Column('user_id', Integer, ForeignKey('user.id')), Column('group_id', Integer, ForeignKey('host_group.id')) ) # 用户和群组的结合表class Host(Base): """主机表 主机名:联合唯一,IP地址或域名 端口号:联合唯一,默认22 注释: """ __tablename__ = 'host' id = Column(Integer, primary_key=True) hostname = Column(String(32), nullable=False) port = Column(Integer, server_default='22') description = Column(String(32)) __table_args__ = (UniqueConstraint('hostname', 'port', name='hostname_port'),)class Authentication(Base): """认证表 用户名: 密码:明文的设备密码 注释: """ __tablename__ = 'authentication' id = Column(Integer, primary_key=True) authname = Column(String(32), nullable=False, unique=True) username = Column(String(32), nullable=False) password = Column(String(32), nullable=False) description = Column(String(32))class BindHost(Base): """主机和认证绑定表 分别和主机id 以及 认证id 联立一对一关联 主机id 和 认证id 要做联合唯一 """ __tablename__ = 'bind_host' id = Column(Integer, primary_key=True) host_id = Column(Integer, ForeignKey('host.id')) auth_id = Column(Integer, ForeignKey('authentication.id')) __table_args__ = (UniqueConstraint('host_id', 'auth_id', name='host_auth'),) host = relationship('Host', backref='bind_host') auth = relationship('Authentication', backref='bind_host')class HostGroup(Base): """主机群组表 群组名:唯一 注释: """ __tablename__ = 'host_group' id = Column(Integer, primary_key=True) groupname = Column(String(32), nullable=False, unique=True) description = Column(String(32)) bind_host = relationship('BindHost', secondary=group_to_bind, backref='host_group')class User(Base): """用户账号表 用户名:不唯一 密码:sha256的hexdigest(),需要64位 """ __tablename__ = 'user' id = Column(Integer, primary_key=True) username = Column(String(32), nullable=False, unique=True) password = Column(String(64), nullable=False) host_group = relationship('HostGroup', secondary=user_to_group, backref='user') bind_host = relationship('BindHost', secondary=user_to_bind, backref='user')class Audit(Base): """审计日志表""" __tablename__ = 'audit' id = Column(Integer, primary_key=True) time = Column(DateTime) user_id = Column(Integer, ForeignKey('user.id')) bind_id = Column(Integer, ForeignKey('bind_host.id')) msg = Column(String(255)) user = relationship('User', backref='audit') bind_host = relationship('BindHost', backref='audit') Base.metadata.create_all(engine) # 创建所有表结构__all__ = ['Host', 'Authentication', 'BindHost', 'HostGroup', 'User', 'Audit' ]
本文转自骑士救兵博客51CTO博客,如需转载,请自行联系原作者。
- 点赞
- 收藏
- 关注作者
评论(0)