💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

推荐:Linux运维老纪的首页,持续学习,不断总结,共同进步,活到老学到老
 导航剑指大厂系列:全面总结 运维核心技术:系统基础、数据库、网路技术、系统安全、自动化运维、容器技术、监控工具、脚本编程、云服务等。
 常用运维工具系列:常用的运维开发工具, zabbix、nagios、docker、k8s、puppet、ansible等
 数据库系列:详细总结了常用数据库 mysql、Redis、MongoDB、oracle 技术点,以及工作中遇到的 mysql 问题等
 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂
 非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨
开发运维之堡垒机
技能目标:
 
 -  
 了解堡垒机特点及架构  
 
 
 -  
 理解堡垒机应用场景  
 
 
 -  
 掌握使用  
 Python  
 编写堡垒机的方法  
 
8.1 案例分析
8.1.1 案例概述
 
 网络安全是互联网企业重要关注的一个问题,随着信息化步伐加快而变得越来越重要,  
 
 
 对网络安全的认知和防范也要重视起来。创鑫公司的  
 IT  
 系统出现问题,当地的  
 IT  
 运维人员  
 
 
 无法解决时,就会向总部发起求助。而此时,总部的技术工程师根本无法获悉最原始的问题,  
 
 
 因为原来的问题在经过分部的运维工程师的操作后,已经面目全非,还可能引入了新的问题,  
 
 
 整个过程没有记录,没有管控,找不到解决问题的线索。所以总部工程师迫切希望知道,从  
 
 
 一开始问题的表象,到分支机构的运维人员的运维操作,都是什么情况。数据实际是存储在  
 
 
 服务器上的,因此,保障服务器安全性是最后一道关口。本章节将从服务器入口增加一道防  
 
 
 御手段 
 -- 
 堡垒机。  
 
8.1.2 案例前置知识点
1.什么是堡垒机
 
 运维堡垒机的理念起源于跳板机。在早期,有一定运维规模的公司为了对运维人员的远  
 
 
 程登录进行集中管理,会单独在一台服务器上部署跳板机。运维人员在维护服务器时,先登  
 
 
 录到这台跳板机上,然后再从这台服务器再登录到目标服务器上进行维护。  
 
2.堡垒机特点
 
   
 
 
 任何操作都有记录,有依据可追溯;  
 
 
   
 
 
 根据系统账户区分工作角色,人员流动不影响;  
 
 
   
 
 
 约束执行命令,避免人为失误。  
 
3. 堡垒机主要作用
(1)权限管理
 
 当公司服务器越来越多后,操作这些服务器的人肯定就不只是一个运维人员,同时也可  
 
 
 能包括多个开发人员。目前,不少公司现状是多个运维共用一个  
 root  
 账户和密码,这就放 
 
 
 大了运维权限和泄露风险。如果某个运维心情不好随时能把业务停止,甚至删除数据,很难  
 
 
 追查谁干的。  
 
 
 (2)审计管理  
 
 
 审计是把用户的所有操作记录下来,以备日后追查问题提供依据。  
 
 
 针对上面问题,使用堡垒机很简单就可以实现:  
 
 
   
 
 
 即使多个运维人员共享一个  
 root  
 账户,但依然能分辨谁再哪台服务器上操作了哪些命  
 
 
 令,因为每个人登录堡垒机的账户是独有的。  
 
 
   
 
 
 对登录的堡垒机账户以基准做记录。  
 
 
 4.堡垒机架构  
 
 
 如图 8.1 所示,堡垒机在服务器前面增加一层。 
  
 
 
 
 
 
 图  
 8.1  
 堡垒机架构  
 
 
 堡垒机的主要作用是权限控制和用户行为审计,例如一个住宅小区,小区里的房屋就是  
 
 
 不同的业务服务器,外部人员要想访问业主,就必须经过小区门口安保人员的授权,他们就  
 
 
 像堡垒机一样;而进入小区的人的所有行为和足迹都会被摄像头监控和记录下来。一旦发生  
 
 
 犯罪事件,小区安保人员就可以通过这些监控记录追踪到人。  
 
 
 要想使用堡垒机也要满足以下条件:  
 
 
   
 
 
 回收所有人员已有的访问权限,做到除了堡垒机管理员,没有人知道业务服务器的登录  
 
 
 权限。 
 
 
   
 
 
 网络上限制所有人员只能通过堡垒机才能访问业务服务器。  
 
 
   
 
 
 服务器创建共享账户。  
 
8.1.3 案例环境
1.本案例环境
 
 本案例环境如表  
 8-1  
 所示。  
 
 
 表  
 8-1  
 创建并管理堡垒机案例环境 
 
 
 
 
| 主机 | 操作系统 | 主机名/IP 地址 | 主要软件及版本 | 
| 堡垒机 | CentOS 7.3 | BLJ /192.168.0.10 | MySQL、Python3.6 | 
| 服务器 | CentOS 7.3 | Server1 /192.168.0.11 | / | 
| 服务器 | CentOS 7.3 | Server1 /192.168.0.12 | / | 
具体的拓扑如图 8.2 所示。
 
 
 
 图 8.2 案例拓扑 
 
2.案例需求
 
 ( 
 1 
 )所有的用户操作日志保留在  
 MySQL  
 数据库中。  
 
 
 ( 
 2 
 )用户人员在维护过程中,首先连接到堡垒机,然后选择要访问的服务器(只能查  
 
 
 看自己权限内的服务器),不需要再输入目标机器的访问密码。  
 
 
 ( 
 3 
 )每个用户对不同的服务器有不同的访问权限,例如:对  
 192.168.0.10  
 有普通用户  
 
 
 权限,对  
 192.168.0.11  
 有  
 sudo  
 权限,对  
 192.168.0.12  
 没有任何权限。  
 
 
 ( 
 4 
 )管理员登录堡垒机,可查看其它账户操作记录。  
 
 
 ( 
 5 
 )确保堡垒机管理员除外,其他用户只提供堡垒机登录跳转功能。  
 
3.实现思路
 
 ( 
 1 
 )通过  
 paramiko  
 模块远程登录服务器交互执行命令,通过堡垒机权限检查。  
 
 
 ( 
 2 
 )通过  
 sqlalchemy  
 模块操作  
 MySQL  
 数据库存放操作记录。  
 
 
 ( 
 3 
 )利用  
 Python  
 数据类型处理数据。  
 
8.2 案例实施
8.2.1 设计表结构
 
 本案例需要在  
 MySQL  
 实例中创建  
 test  
 数据库, 
 test  
 数据库中有创建  
 user  
 表(用户信  
 
 
 息表)、 
 host  
 表(主机信息表)和  
 cmd_log  
 表(工作日志表)。三张表没有主外键关系, 
 user  
 
 
 表主要功能是保存授权用户对应的授权主机, 
 host  
 表用来保存被管理主机  
 ip  
 对应的端口号,  
 
 
 cmd_log  
 表是用于保存授权用户在操作授权主机时所产生的记录比如操作命令、时间等。  
 
 
 表  
 8-2  
 是  
 user  
 表的结构, 
 
 
 
| 序号 | 字段名称 | 字段说明 | 类型 | 长度 | 备注 | 
| 1 | id | 编号 | int | 自动编号,主键 | |
| 2 | username | 用户名 | varchar | 50 | 非空 | 
| 3 | hosts | 授权的主机 | text | 
 
 表  
 8-3 host  
 表 
 
 
 
 
 
| 序号 | 字段名称 | 字段说明 | 类型 | 长度 | 备注 | 
| 1 | id | 编号 | 自动编号,主键 | ||
| 2 | ip | IP 地址 | varchar | 50 | 非空 | 
表 8-4 cmd_log 表
| 序号 | 字段名称 | 字段说明 | 类型 | 长度 | 备注 | 
| 1 | id | 编号 | int | 自动编号,主键 | |
| 2 | login_user | 登录用户名 | varchar | 16 | feikong | 
| 3 | share_user | 目标主机共享账户 | varchar | 16 | 非空 | 
| 4 |  
      server_ip 
      |  
      目标主机 IP 地址 
      | varchar | 50 | 非空 | 
| 5 |  
      shell_command 
      |  
      用户操作的 Shell 命令 
      | varchar | 255 | 非空 | 
| 6 |  
      datetime 
      |  
      操作命令日期时间 
      | varchar | 30 | 非空 | 
8.2.2 功能模块规划
 
 实现本案例需求,需要规划  
 Python  
 目录结构:  
 
 
 ├── bin  
 
 
 │ ├── __init__.py  
 
 
 │ ├── manager_menu.py  
 
 
 │ └── user_menu.py 
 
 
 ├── config  
 
 
 │ ├── config.py  
 
 
 │ ├── __init__.py  
 
 
 └── modules  
 
 
 ├── db_conn.py  
 
 
 ├── __init__.py  
 
 
 ├── interactive.py  
 
 
 ├── manager_handle.py  
 
 
 ├── ssh_login.py  
 
 
 └── table_init.py  
 
 
 在每个目录下有一个空的 
 __init__.py  
 文件,说明这个目录是一个包,可导入。 
 __init__.py  
 
 
 文件定义了包的属性和方法。其实它可以什么也不定义,它可以只是一个空文件,但是必须  
 
 
 存在。如果  
 __init__.py  
 不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被  
 
 
 导入或者包含其它的模块和嵌套包。  
 
 
 表  
 8-5 Python  
 工程目录结构中所包含的模块  
 
|  
      目录 
      |  
      模块名  
       
      描述 
      |  
      描述 
      | 
|  
      bin 
      |  
      manager_menu 
      |  
      管理菜单 
      | 
|  
      bin 
      |  
      user_menu 
      |  
      用户菜单 
      | 
|  
      config 
      |  
      confg 
      |  
      数据库配置文件 
      | 
|  
      modules 
      |  
      table_init.py 
      |  
      数据库表创建与映射 
      | 
| modules |  
      db_conn 
      |  
      数据库连接 
      | 
| modules |  
      interactive 
      |  
      SSH 交互式 
      | 
| modules |  
      ssh_login 
      |  
      SSH 登录 
      | 
| modules |  
      manager_handle 
      |  
      管理菜单处理程序 
      | 
8.2.3 功能实现
 
 通常,开发项目先从依赖的关系写,例如 
 : 
 用到数据库,需要先将数据库相关逻辑弄清  
 
 
 楚,方便写功能时调用调试。  
 
 
 本案例编写模块顺序: 
 config -> modules -> bin 
 。  
 
1. 数据库配置模块
 
 [root@BLJ baoleiji]#  
 vim config/config.py  
 
 
 database='test'  
 
 
 ip='127.0.0.1'  
 
 
 port=3306  
 
 
 user='root'  
 
 
 password='123456'  
 
 
 engine_param = 'mysql+pymysql://%s:%s@%s:%d/%s?charset=utf8' %(user, password, ip, port,  
 
 
 database)  
 
2. 数据库连接模块
 
 [root@BLJ baoleiji]#  
 vim modules/db_conn.py  
 
 
 from sqlalchemy import create_engine  
 
 
 from sqlalchemy.orm import sessionmaker  
 
 
 from modules.table_init import *  
 
 
 from config.config import engine_param  
 
 
 engine=create_engine(engine_param)  
 
 
 #  
 创建会话  
 
 
 Session = sessionmaker(bind=engine)  
 
 
 Session = sessionmaker()  
 
 
 Session.configure(bind=engine)  
 
 
 session = Session()  
 
3. 创建表并映射模块
 
 [root@BLJ baoleiji]#  
 vim modules/table_init.py  
 
 
 #!/usr/local/bin/python3.6  
 
 
 import os,sys  
 
 
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 sys.path.append(BASE_DIR)  
 
 
 from sqlalchemy import create_engine  
 
 
 from sqlalchemy.ext.declarative import declarative_base  
 
 
 from sqlalchemy import Column, Integer, String, Text  
 
 
 from config import config  
 
 
 #  
 创建对象的基类  
 
 
 Base = declarative_base()  
 
 
 #  
 定义  
 User  
 对象  
 
 
 class User(Base):  
 
 
 __tablename__ = 'user'  
 
 
 id = Column(Integer, autoincrement=True, primary_key=True)  
 
 
 username = Column(String(50))  
 
 
 hosts = Column(Text, default="{'sudo': [], 'no-sudo': []}")  
 
 
 class Host(Base):  
 
 
 __tablename__ = 'host'  
 
 
 id = Column(Integer, autoincrement=True, primary_key=True)  
 
 
 ip = Column(String(50))  
 
 
 port = Column(Integer, default=22)  
 
 
 class Cmd_log(Base):  
 
 
 __tablename__ = 'cmd_log'  
 
 
 id = Column(Integer, autoincrement=True, primary_key=True)  
 
 
 login_user = Column(String(16))  
 
 
 share_user = Column(String(16))  
 
 
 server_ip = Column(String(50))  
 
 
 shell_command = Column(String(255))  
 
 
 datetime = Column(String(30))  
 
 
 if __name__ == "__main__":  
 
 
 engine=create_engine(config.engine_param)  
 
  Base.metadata.create_all(engine) # 创建表结构 
 
4. SSH 交互式处理和登录模块
 
 [root@BLJ baoleiji]#  
 vim modules/interactive.py  
 
 
 import os,sys  
 
 
 #BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 #sys.path.append(BASE_DIR)  
 
 
 import socket  
 
 
 import termios  
 
 
 import tty  
 
 
 from datetime import datetime  
 
 
 from modules.table_init import *  
 
 
 from modules.db_conn import session  
 
 
 def interactive_shell(channel, login_user, share_user, server_ip):  
 
 
 import select  
 
 
 oldtty = termios.tcgetattr(sys.stdin)  
 
 
 try:  
 
 
 tty.setraw(sys.stdin.fileno())  
 
 
 tty.setcbreak(sys.stdin.fileno())  
 
 
 channel.settimeout(0.0)  
 
 
 cmd = ''  
 
 
 tab_key = False  
 
 
 while True:  
 
 
 r, w, e = select.select([channel, sys.stdin], [], [])  
 
 
 if channel in r:  
 
 
 try:  
 
 
 x = channel.recv(1024).decode()  
 
 
 if tab_key:  
 
 
 if x not in ('\t','\r\n'):  
 
 
 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:  
 
 
 add_log = Cmd_log(login_user=login_user,  
 
 
 share_user=share_user,  
 
 
 server_ip=server_ip,  
 
 
 shell_command=cmd,  
 
 
 datetime=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))  
 
 
 session.add(add_log)  
 
 
 session.commit()  
 
 
 cmd = ''  
 
 
 if '\t' == x:  
 
 
 tab_key = True  
 
 
 channel.send(x)  
 
 
 finally:  
 
 
 termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)  
 
 
 [root@BLJ baoleiji]#  
 vim modules/ssh_login.py  
 
 
 import os,sys  
 
 
 #BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 #sys.path.append(BASE_DIR)  
 
 
 import paramiko  
 
 
 from modules.interactive import interactive_shell  
 
 
 def ssh_login(login_user, share_user, server_ip, server_port, password):  
 
 
 # 
 建立  
 ssh  
 连接  
 
 
 ssh=paramiko.SSHClient()  
 
 
 ssh.load_system_host_keys()  
 
 
 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())  
 
 
 ssh.connect(server_ip, server_port, share_user, password)  
 
 
 # 
 建立交互式  
 shell  
 连接  
 
 
 channel=ssh.invoke_shell()  
 
 
 # 
 建立交互式管道  
 
 
 interactive_shell(channel, login_user, share_user, server_ip)  
 
 
 # 
 关闭连接  
 
 
 channel.close()  
 
 
 ssh.close()  
 
 
 if __name__ == "__main__":  
 
 
 #  
 手动执行测试  
 
 
 login_user = "xiaoming"  
 
 
 share_user = "user2"  
 
 
 password = "123456"  
 
 
 server_ip = "192.168.0.11"  
 
 
 server_port = 22  
 
 
 ssh_login(login_user, share_user, server_ip, server_port, password)  
 
5. 管理接口模块
 
 [root@BLJ baoleiji]#  
 vim bin/manager_menu.py  
 
 
 #!/usr/local/bin/python3.6  
 
 
 import os,sys,io  
 
 
 #  
 添加当前目录到  
 Python  
 模块搜索路径  
 
 
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 sys.path.append(BASE_DIR)  
 
 
 #  
 改变标准输出的默认编码,解决中文编码错误 
 :UnicodeEncodeError  
 
 
 sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')  
 
 
 from modules.manager_handle import *  
 
 
 choices = {  
 
 
 "1": " 
 添加用户名 
 ",  
 
 
 "2": " 
 授权用户名访问主机 
 ",  
 
 
 "3": " 
 添加主机 
 ",  
 
 
 "4": " 
 删除主机 
 ",  
 
 
 "5": " 
 查看日志 
 ",  
 
 
 "6": " 
 退出 
 "  
 
 
 }  
 
 
 while True:  
 
 
 print(" 
 菜单 
 :")  
 
 
 for k, v in choices.items():  
 
 
 print(k + ". " + v)  
 
 
 choice = input(" 
 请输入编号 
 : ")  
 
 
 choice = str(choice).strip()  
 
 
 if choice in choices.keys():  
 
 
 if choice == "1":  
 
 
 add_user()  
 
 
 elif choice == "2":  
 
 
 authorized()  
 
 
 elif choice == "3":  
 
 
 add_host()  
 
 
 elif choice == "4":  
 
 
 del_host()  
 
 
 elif choice == "5":  
 
 
 view_log()  
 
 
 elif choice == "6":  
 
 
 quit()  
 
 
 else:  
 
 
 print(" 
 编号错误,请重新输入 
 !")  
 
 
 continue  
 
 
 [root@BLJ baoleiji]#  
 vim modules/manager_handle.py  
 
 
 #!/usr/local/bin/python3.6  
 
 
 #import os,sys  
 
 
 #BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 #sys.path.append(BASE_DIR)  
 
 
 import re  
 
 
 from modules.table_init import *  
 
 
 from modules.db_conn import session  
 
 
 def add_user():  
 
 
 username = input(" 
 请输入要添加的用户名 
 : ").strip()  
 
 
 user = User(username=username)  
 
 
 session.add(user)  
 
 
 session.commit()  
 
 
 print(" 
 添加成功 
 .")  
 
 
 def authorized():  
 
 
 n = 1  
 
 
 while True:  
 
 
 username = input(" 
 请输入要授权的用户名 
 : ").strip()  
 
 
 user = session.query(User).filter(User.username==username).first()  
 
 
 if user:  
 
 
 break  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 return  
 
 
 print(" 
 用户名不存在 
 ,  
 请重新输入 
 !")  
 
 
 n += 1  
 
 
 continue  
 
 
 n = 1  
 
 
 while True:  
 
 
 ip = input(" 
 请输入要授权的主机  
 IP  
 地址 
 : ").strip()  
 
 
 host = session.query(Host).filter(Host.ip==ip).first()  
 
 
 if host:  
 
 
 break  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 return  
 
 
 print(" 
 主机  
 IP  
 地址不存在 
 ,  
 请重新输入 
 !")  
 
 
 n += 1  
 
 
 continue  
 
 
 n = 1  
 
 
 while True:  
 
 
 sudo = input(" 
 需要  
 sudo  
 到  
 root  
 权限 
 ?(Y/N): ").strip()  
 
 
 if sudo in ['y','Y', 'n', 'N']:  
 
 
 break  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 return  
 
 
 print(" 
 输入错误 
 ,  
 请重新输入 
 !")  
 
 
 n += 1  
 
 
 continue  
 
 
 hosts = eval(user.hosts)  
 
 
 if sudo in ['y', 'y']:  
 
 
 hosts['sudo'].append(ip)  
 
 
 elif sudo in ['n', 'N']:  
 
 
 hosts['no-sudo'].append(ip)  
 
 
 user = session.query(User).filter(User.username==username).update({"hosts": str(hosts)})  
 
 
 session.commit()  
 
 
 print(" 
 添加成功 
 .")  
 
 
 def add_host():  
 
 
 n = 1  
 
 
 while True:  
 
 
 ip = input(" 
 请输入要添加的  
 IP: ").strip()  
 
 
 ip_match = re.match('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', ip)  
 
 
 if ip_match:  
 
 
 host = Host(ip=ip)  
 
 
 session.add(host)  
 
 
 session.commit()  
 
 
 print(" 
 添加成功 
 .")  
 
 
 break  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 break  
 
 
 print("IP  
 地址错误 
 ,  
 请重新输入 
 !")  
 
 
 n +=1  
 
 
 continue  
 
 
 def del_host():  
 
 
 n = 1  
 
 
 while True:  
 
 
 ip = input(" 
 请输入要删除的  
 IP  
 地址 
 : ").strip()  
 
 
 host = session.query(Host).filter(Host.ip==ip).first()  
 
 
 if host:  
 
 
 session.delete(host)  
 
 
 session.commit()  
 
 
 print(" 
 删除成功 
 .")  
 
 
 break  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 break  
 
 
 print("IP  
 地址不存在 
 ,  
 请重新输入 
 !")  
 
 
 n +=1  
 
 
 continue  
 
 
 def view_log():  
 
 
 logs = session.query(Cmd_log).all()  
 
 
 if logs:  
 
 
 for i in logs:  
 
 
 print("login_user: %s | share_user: %s | server_ip: %s | shell_command: %s |  
 
 
 datetime: %s"  
 
 
 %(i.login_user, i.share_user, i.server_ip, i.shell_command, i.datetime))  
 
 
 else:  
 
 
 print(" 
 没有日志记录 
 .")  
 
 
 def quit():  
 
 
 sys.exit()  
 
6. 用户接口模块
 
 [root@BLJ baoleiji]#  
 vim bin/user_menu.py  
 
 
 #!/usr/local/bin/python3.6  
 
 
 import os,sys,io  
 
 
 #  
 添加当前目录到  
 Python  
 模块搜索路径  
 
 
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  
 
 
 sys.path.append(BASE_DIR)  
 
 
 #  
 改变标准输出的默认编码,解决中文编码错误 
 :UnicodeEncodeError  
 
 
 sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')  
 
 
 from modules.table_init import *  
 
 
 from modules.db_conn import session  
 
 
 from modules.ssh_login import ssh_login  
 
 
 import getpass  
 
 
 login_user = getpass.getuser()  
 
 
 server_port = 22  
 
 
 share_user = {'sudo': 'admin', 'no-sudo': 'user', 'password': '123456'}  
 
 
 user_host = session.query(User).filter(User.username==login_user).first()  
 
 
 #  
 如果  
 root  
 登陆说明是管理员,则退出不执行  
 
 
 if login_user == "root":  
 
 
 sys.exit()  
 
 
 if not user_host:  
 
 
 print(" 
 数据库未记录该登录用户 
 ,  
 请联系管理员 
 !")  
 
 
 sys.exit()  
 
 
 sudo = set(eval(user_host.hosts)['sudo'])  
 
 
 no_sudo = set(eval(user_host.hosts)['no-sudo'])  
 
 
 def display_menu():  
 
 
 print(" 
 主机列表 
 :")  
 
 
 if not sudo and not no_sudo:  
 
 
 print(" 
 没有授权的主机 
 !")  
 
 
 for i in sudo:  
 
 
 print(i + " sudo")  
 
 
 for i in no_sudo:  
 
 
 print(i)  
 
 
 n = 1  
 
 
 while True:  
 
 
 display_menu()  
 
 
 server_ip = input(str(" 
 请输入要登录的主机  
 IP  
 地址 
 : "))  
 
 
 if server_ip in sudo:  
 
 
 ssh_login(login_user,share_user['sudo'],server_ip,server_port,share_user['password'])  
 
 
 elif server_ip in no_sudo:  
 
 
 ssh_login(login_user,share_user['no-sudo'],server_ip,server_port,share_user['password'])  
 
 
 else:  
 
 
 if n == 3:  
 
 
 print(" 
 错误次数达到  
 3  
 次 
 ,  
 退出 
 !")  
 
 
 os.system("exit")  
 
 
 print("IP  
 地址错误,请重新输入 
 !")  
 
 
 n +=1  
 
 
 Continue  
 
8.2.4 部署
 
 部署之前需要先将程序放到 
 /opt  
 目录下。  
 
1. 安装模块
 
 [root@BLJ baoleiji]#  
 pip3 install paramiko pymysql sqlalchemy  
 
2. 配置数据库信息
 
 [root@BLJ baoleiji]#  
 vim config/config.py  
 
 
 database='test'  
 
 
 ip='127.0.0.1'  
 
 
 port=3306  
 
 
 user='root'  
 
 
 password='123456'  
 
 
 engine_param = 'mysql+pymysql://%s:%s@%s:%d/%s?charset=utf8' %(user, password,  
 
 
 ip, port, database)  
 
 
 把对应字段值修改为自己的数据库信息。  
 
3. 初始化数据库表
 
 下面是创建  
 test  
 数据库的命令:  
 
 
 [root@BLJ baoleiji]#  
 mysql –uroot -p  
 
 
 mysql>  
 create database test;  
 
 
 初始化数据库表:  
 
 
 [root@BLJ baoleiji]#  
 python3 modules/table_init.py  
 
 
 执行完成后,进数据库核实下是否创建如下表:  
 
 
 [root@BLJ baoleiji]#  
 mysql –uroot -p  
 
 
 mysql>  
 use test;  
 
 
 mysql>  
 show tables;  
 
 
 
 +----------------+  
 
 
 | Tables_in_test |  
 
 
 +----------------+  
 
 
 | cmd_log  
 
 
 |  
 
 
 | host  
 
 
 |  
 
 
 | user  
 
 
 |  
 
 
 +----------------+  
 
 
 3 rows in set (0.00 sec)  
 
4. 被管理主机创建登录账号
 
 在  
 Server1  
 和  
 Server2  
 主机中分别创建  
 admin  
 和  
 user  
 账号,该账号用于跳板机  
 SSH  
 
 
 到被管理主机登录时的账号。下面以  
 Server1  
 为例:  
 
 
 [root@server1 ~]#  
 useradd admin  
 
 
 [root@server1 ~]#  
 passwd admin  
 
 
 [root@server1 ~]#  
 useradd user  
 
 
 [root@server1 ~]#  
 passwd user  
 
 
 密码均设置为  
 123456 
 。  
 
 
 这两个账号配置在程序里:  
 
 
 [root@BLJ baoleiji]#  
 cat bin/user_menu.py  
 
 
 ......  
 
 
 share_user = {'sudo': 'admin', no-sudo: 'user', 'password': '123456'}  
 
 
 ......  
 
5. 配置用户登录自动执行用户菜单程序
 
 [root@BLJ baoleiji]#  
 vim /etc/profile.d/baoleiji.sh  
 
 
 python3 /opt/baoleiji/bin/user_menu.py  
 
 
 注意:在  
 Linux  
 中,通过配置用户家目录下的 
 .bashrc  
 文件,实现自动执行脚本。  
 
6. 将被管理主机 IP 添加到堡垒机数据库
 
 [root@BLJ baoleiji]#  
 python3 bin/manager_menu.py  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 3  
 
 
 请输入要添加的  
 IP: 192.168.0.11  
 
 
 添加成功 
 .  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 3  
 
 
 请输入要添加的  
 IP: 192.168.0.12  
 
 
 添加成功 
 .  
 
8.2.5 测试
 
 工作流程:  
 
 
 (1)  
 管理员为用户在服务器上创建系统账号与添加此账号到堡垒机数据库。  
 
 
 (2)  
 用户登录堡垒机,输入系统账户与密码,认证通过后显示可访问的主机列表。  
 
 
 (3)  
 用户输入主机  
 IP  
 自动登录。  
 
 
 (4)  
 用户操作记录到数据库。  
 
 
 下面是测试过程:  
 
1. 管理员使用 root 账户登录到堡垒机为同事创建系统账号
 
 [root@BLJ ~]#  
 useradd zhangsan  
 
 
 [root@BLJ ~]#  
 passwd zhangsan  
 
 
 [root@BLJ ~]#  
 useradd lisi  
 
 
 [root@BLJ ~]#  
 passwd lisi  
 
2. 添加系统账号到堡垒机数据库
 
 [root@BLJ baoleiji]#  
 python3 bin/manager_menu.py  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 1  
 
 
 请输入要添加的用户名 
 : zhangsan  
 
 
 添加成功 
 .  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 1  
 
 
 请输入要添加的用户名 
 : lisi  
 
 
 添加成功 
 .  
 
3. 授权用户名访问主机
 
 [root@BLJ baoleiji]#  
 python3 bin/manager_menu.py  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 2  
 
 
 请输入要授权的用户名 
 : zhangsan  
 
 
 请输入要授权的主机  
 IP  
 地址 
 : 192.168.0.11  
 
 
 需要  
 sudo  
 到  
 root  
 权限 
 ?(Y/N): y  
 
 
 添加成功 
 .  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 2  
 
 
 请输入要授权的用户名 
 : zhangsan  
 
 
 请输入要授权的主机  
 IP  
 地址 
 : 192.168.0.12  
 
 
 需要  
 sudo  
 到  
 root  
 权限 
 ?(Y/N): n  
 
 
 添加成功 
 .  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 2  
 
 
 请输入要授权的用户名 
 : lisi  
 
 
 请输入要授权的主机 IP  
 地址 
 : 192.168.0.12  
 
 
 需要  
 sudo  
 到  
 root  
 权限 
 ?(Y/N): n  
 
 
 添加成功 
 .  
 
 
 说明:上述授权  
 zhangsan  
 用户对  
 192.168.0.11  
 有可  
 sudo  
 到  
 root  
 权限,对  
 192.168.0.12  
 
 
 无  
 sudo  
 权限。授权  
 lisi  
 用户对  
 192.168.0.12  
 无  
 sudo  
 权限。  
 
4. 用户登录测试
(1)使用 zhangsan 用户登录 Server1 服务器。
 
 [root@BLJ ~]#  
 login as: zhangsan  
 
 
 Localhost login 
 : 
 zhangsan  
 
 
 password:  
 
 
 主机列表:  
 
 
 192.168.0.11 sudo  
 
 
 192.168.0.12  
 
 
 请输入要登录的主机  
 IP  
 地址 
 : 192.168.0.11  
 
 
 [admin@localhost ~]$  
 ip addr  
 
 
 # 
 查看是否连接到了目标服务器  
 
(2)使用 lisi 用户登录 Server2 服务器。
 
 [root@BLJ ~]# 
 login as: lisi  
 
 
 Localhost login 
 : 
 lisi 
 
 
 password:  
 
 
 主机列表:  
 
 
 192.168.0.12  
 
 
 请输入要登录的主机  
 IP  
 地址 
 : 192.168.0.12  
 
 
 [user@localhost ~]$ 
 ip addr  
 
 
 # 
 查看是否连接到了目标服务器  
 
 
 5. 管理员查看操作日志  
 
 
 [root@BLJ ~]#  
 python3 /opt/baoleiji/bin/manager_menu.py  
 
 
 菜单 
 :  
 
 
 1.  
 添加用户名  
 
 
 2.  
 授权用户名访问主机  
 
 
 3.  
 添加主机  
 
 
 4.  
 删除主机  
 
 
 5.  
 查看日志  
 
 
 6.  
 退出  
 
 
 请输入编号 
 : 5  
 
 
 login_user: zhangsan | share_user: admin | server_ip: 192.168.0.11 | shell_command: echo  
 :  
 
 
 "zhangsan" | datetime: 2020-07-14 14:29:27  
 
 
 login_user: zhangsan | share_user: admin | server_ip: 192.168.0.11 | shell_command: ls | datetime:  
 
 
 2020-07-14 14:29:30  
 
 
 login_user: lisi | share_user: user | server_ip: 192.168.0.12 | shell_command: echo "lisi | datetime:  
 
 
 2020-07-14 14:29:52  
 
 
 login_user: lisi | share_user: user | server_ip: 192.168.0.12 | shell_command: ls | datetime: 
 2020-07-14 14:29:54  
 
 
 本项目实现了基本的堡垒机功能,相比生产环境的使用功能还有待完善,这里的目的是  
 
 
 让大家熟悉堡垒机项目开发流程。 
 
 
 
                 
 



















