基于python的堡垒机

一、堡垒机的概念

  堡垒机,也称为跳板机,多用于系统运维环境中。指的是在一个特定的网络环境下,为了保障网络和数据不受来自外部和内部用户的入侵和破坏,而运用各种技术手段实时收集和监控网络环境中每一个组成部分的系统状态、安全事件、网络活动,以便集中报警、及时处理及审计定责。

  从功能上分析,它综合了核心系统运维和安全审计管控两大主要功能;从技术实现上分析,它通过切断终端计算机对网络和服务器资源的直接访问,而采用协议代理的方式,接管了终端计算机对网络和服务器的访问。形象地说,终端计算机对目标的访问,均需要经过运维安全审计的翻译。打一个比方,运维安全审计扮演着看门者的工作,所有对网络设备和服务器的请求都要从这扇大门经过。因此运维安全审计能够拦截非法访问和恶意攻击,并对不合法命令进行命令阻断,过滤掉所有对目标设备的非法访问行为,并对内部人员误操作和非法操作进行审计监控,以便事后责任追踪。

  产生堡垒机的最初原因是随着系统的不断发展,网络规模和设备数量迅速扩大,日趋复杂的业务系统与不同背景的运维人员的行为给系统安全带来较大风险:

  1.多个用户使用同一个账号。这种情况主要出现在同一工作组中,由于工作需要,同时系统管理账号唯一,因此只能多用户共享同一账号。如果发生安全事故,不仅难以定位账号的实际使用者和责任人,而且无法对账号的使用范围进行有效控制,存在较大安全风险和隐患。

  2.一个用户使用多个账号。目前,一个维护人员使用多个账号是较为普遍的情况,用户需要记忆多套口令同时在多套主机系统、网络设备之间切换,降低工作效率,增加工作复杂度。

  3. 缺少统一的权限管理平台,权限管理日趋繁重和无序;而且维护人员的权限大多是粗放管理,无法基于最小权限分配原则的用户权限管理,难以实现更细粒度的命令级权限控制,系统安全性无法充分保证。

  4. 无法制定统一的访问审计策略,审计粒度粗。各网络设备、主机系统、数据库是分别单独审计记录访问行为,由于没有统一审计策略,并且各系统自身审计日志内容深浅不一,难以及时通过系统自身审计发现违规操作行为和追查取证。

  5. 传统的网络安全审计系统无法对维护人员经常使用的SSH、RDP等加密、图形操作协议进行内容审计。

  为了解决上面的问题,开发出了堡垒机,作为用户和主机之间的一道“防火墙”。 

  简单的讲就是:以前去一座酒店,你想去哪个房间可以直接去,但现在不行了,得通过前台这么个“堡垒机”的允许和监控下才可以,想“做坏事”得小心了......

二、堡垒机的架构

堡垒机的核心架构通常如下图所示:

设备的连接方式如下图所示:

堡垒机的一般执行流程:

  1. 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码)
  2. 用户登陆堡垒机,输入堡垒机用户名密码,显示当前用户可管理的服务器得列表
  3. 用户选择服务器,并自动登陆
  4. 执行操作并同时将用户操作记录

注:在linux中,通过配置用户的.brashrc文件,实现ssh登陆后自动执行脚本,如:/usr/bin/env python /home/jack/jump.py,完成业务操作后logout自动退出。

  另外,想要正确可靠的发挥堡垒机的作用,只靠堡垒机本身是不够的, 还需要对用户进行安全上的限制,堡垒机部署后,要确保你的系统达到以下条件:

  • 所有人包括运维、开发等任何需要访问业务系统的人员,只能通过堡垒机访问业务系统

    • 回收所有对业务系统的访问权限,做到除了堡垒机管理人员,没有人知道业务系统任何机器的登录密码
    • 网络上限制所有人员只能通过堡垒机的跳转才能访问业务系统
  • 确保除了堡垒机管理员之外,所有其它人对堡垒机本身无任何操作权限,只有一个登录跳转功能
  • 确保用户的操作纪录不能被用户自己以任何方式获取到并篡改,达到安全审计的作用。  

三、python实现堡垒机

通过第三方模块paramiko,在python中可以很方便的实现堡垒机的基础功能,还可以通过django框架实现web管理和维护。

基本架构如下:

1. 简单的实现版本

  该版本较简单,只能输入一行命令,回车,然后接收主机返回的信息,并打印。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u  # python2.x中不需要这一行

default_username = getpass.getuser()
username = input(‘Username [%s]: ‘ % default_username)
if len(username) == 0:
    username = default_username

hostname = input(‘Hostname: ‘)
if len(hostname) == 0:
    print(‘*** Hostname required.‘)
    sys.exit(1)

tran = paramiko.Transport((hostname, 22,))
tran.start_client()

default_auth = "p"
auth = input(‘Auth by (p)assword or (r)sa key[%s] ‘ % default_auth)
if len(auth) == 0:
    auth = default_auth

if auth == ‘r‘:
    default_path = os.path.join(os.environ[‘HOME‘], ‘.ssh‘, ‘id_rsa‘)
    path = input(‘RSA key [%s]: ‘ % default_path)
    if len(path) == 0:
        path = default_path
    try:
        key = paramiko.RSAKey.from_private_key_file(path)
    except paramiko.PasswordRequiredException:
        password = getpass.getpass(‘RSA key password: ‘)
        key = paramiko.RSAKey.from_private_key_file(path, password)
    tran.auth_publickey(username, key)
else:
    pw = getpass.getpass(‘Password for %[email protected]%s: ‘ % (username, hostname))
    tran.auth_password(username, pw)

# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()

while True:
    # 监视用户输入和服务器返回数据
    # sys.stdin 处理用户输入
    # chan 是之前创建的通道,用于接收服务器返回信息
    # select模块是IO多路复用模块,功能强大,无处不在,你值得学习和记忆!
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        try:
            x = u(chan.recv(1024))  # python2.x中直接接收就可以,不要u
            if len(x) == 0:
                print(‘\r\n*** EOF\r\n‘)
                break
            sys.stdout.write(x)
            sys.stdout.flush()
        except socket.timeout:
            pass
    if sys.stdin in readable:
        inp = sys.stdin.readline()    # 一行一行的读取用户在终端输入的命令
        chan.sendall(inp)

chan.close()
tran.close()

2. 逐个字符发送的版本

  上面是一行一行的命令执行方式,下面这个是一个按键一个按键的读取并发送方式。

# 从chan.invoke_shell()这一句往上省略,和上面的例子相同

import termios  # 导入python的终端模块。注意,windows系统没有这个模块。

# 获取原tty属性,用于在退出时恢复原始状态
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 为tty设置新属性
    # 默认当前tty设备属性:
    #   输入一行回车,执行
    #   CTRL+C 进程退出,遇到特殊字符,特殊处理。

    # 这是为原始模式,不认识所有特殊符号
    # 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
    tty.setraw(sys.stdin.fileno())
    chan.settimeout(0.0)

    while True:
        # 监视 用户输入 和 远程服务器返回数据(socket)
        # 阻塞,直到句柄可读
        r, w, e = select.select([chan, sys.stdin], [], [], 1)
        if chan in r:
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print(‘\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 len(x) == 0:
                break
            chan.send(x)

finally:
    # 重新设置终端属性为原状
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

chan.close()
tran.close()

3. 更高级的版本

  在linux中有tab键补全功能,要实现它,需要按键读取的方式,接收主机返回的tab补齐信息,最后组合成完整的命令,并执行。另外由于windows操作系统没有termios模块,因此使用多线程的方式。

import paramiko
import sys
import os
import socket
import getpass

from paramiko.py3compat import u

# windows does not have termios...
try:
    import termios
    import tty
    has_termios = True
except ImportError:
    has_termios = False

def interactive_shell(chan):
    if has_termios:
        posix_shell(chan)
    else:
        windows_shell(chan)

def posix_shell(chan):
    import select

    oldtty = termios.tcgetattr(sys.stdin)
    try:
        tty.setraw(sys.stdin.fileno())
        tty.setcbreak(sys.stdin.fileno())
        chan.settimeout(0.0)
        log = open(‘handle.log‘, ‘a+‘, encoding=‘utf-8‘)
        flag = False
        temp_list = []
        while True:
            r, w, e = select.select([chan, sys.stdin], [], [])
            if chan in r:
                try:
                    x = u(chan.recv(1024))
                    if len(x) == 0:
                        sys.stdout.write(‘\r\n*** EOF\r\n‘)
                        break
                    if flag:
                        if x.startswith(‘\r\n‘):
                            pass
                        else:
                            temp_list.append(x)
                        flag = False
                    sys.stdout.write(x)
                    sys.stdout.flush()
                except socket.timeout:
                    pass
            if sys.stdin in r:
                x = sys.stdin.read(1)
                import json

                if len(x) == 0:
                    break

                if x == ‘\t‘:
                    flag = True
                else:
                    temp_list.append(x)
                if x == ‘\r‘:
                    log.write(‘‘.join(temp_list))
                    log.flush()
                    temp_list.clear()
                chan.send(x)

    finally:
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

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

def run():
    default_username = getpass.getuser()
    username = input(‘Username [%s]: ‘ % default_username)
    if len(username) == 0:
        username = default_username

    hostname = input(‘Hostname: ‘)
    if len(hostname) == 0:
        print(‘*** Hostname required.‘)
        sys.exit(1)

    tran = paramiko.Transport((hostname, 22,))
    tran.start_client()

    default_auth = "p"
    auth = input(‘Auth by (p)assword or (r)sa key[%s] ‘ % default_auth)
    if len(auth) == 0:
        auth = default_auth

    if auth == ‘r‘:
        default_path = os.path.join(os.environ[‘HOME‘], ‘.ssh‘, ‘id_rsa‘)
        path = input(‘RSA key [%s]: ‘ % default_path)
        if len(path) == 0:
            path = default_path
        try:
            key = paramiko.RSAKey.from_private_key_file(path)
        except paramiko.PasswordRequiredException:
            password = getpass.getpass(‘RSA key password: ‘)
            key = paramiko.RSAKey.from_private_key_file(path, password)
        tran.auth_publickey(username, key)
    else:
        pw = getpass.getpass(‘Password for %[email protected]%s: ‘ % (username, hostname))
        tran.auth_password(username, pw)

    # 打开一个通道
    chan = tran.open_session()
    # 获取一个终端
    chan.get_pty()
    # 激活器
    chan.invoke_shell()

    interactive_shell(chan)

    chan.close()
    tran.close()

if __name__ == ‘__main__‘:
    run()

四、通过堡垒机实现SCP

  下面这个例子的堡垒机模式有点不同,它实现的是这么个架构的SCP功能:

#!/usr/bin/env python
import paramiko
import os,sys,time

hostname="192.168.1.111"   # 远程主机
username="root"                    # 远程主机的用户名
password="SKJh935yft#"

blip="192.168.1.1"              # 堡垒机
bluser="root"                        #堡垒机用户名
blpasswd="SKJh935yft#"

tmpdir="/tmp"                   #  客户机源文件路径、堡垒机临时路劲和远程主机目标文件路径
remotedir="/data"
localpath="/home/nginx_access.tar.gz"
tmppath=tmpdir+"/nginx_access.tar.gz"
remotepath=remotedir+"/nginx_access_hd.tar.gz"

port=22
passinfo=‘\‘s password: ‘                     # 这是密码信息
paramiko.util.log_to_file(‘syslogin.log‘)   # paramiko自带的日志功能

t = paramiko.Transport((blip, port))           # 创建连接对象
t.connect(username=bluser, password=blpasswd)   #建立连接
sftp =paramiko.SFTPClient.from_transport(t)  # 创建SFTP连接
sftp.put(localpath, tmppath)    # 利用put方法上传文件

sftp.close()          # 关闭sftp连接

ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=blip,username=bluser,password=blpasswd)

#创建一个新的会话
channel=ssh.invoke_shell()
channel.settimeout(10)

buff = ‘‘
resp = ‘‘
# 利用linux原生的scp功能将堡垒机上的临时文件传送到远程主机上面
channel.send(‘scp ‘+tmppath+‘ ‘+username+‘@‘+hostname+‘:‘+remotepath+‘\n‘)

while not buff.endswith(passinfo):
    try:
        resp = channel.recv(9999)
    except Exception,e:
        print ‘Error info:%s connection time.‘ % (str(e))
        channel.close()
        ssh.close()
        sys.exit()
    buff += resp
    if not buff.find(‘yes/no‘)==-1:
        channel.send(‘yes\n‘)
	buff=‘‘

channel.send(password+‘\n‘)

buff=‘‘
while not buff.endswith(‘# ‘):
    resp = channel.recv(9999)
    if not resp.find(passinfo)==-1:
        print ‘Error info: Authentication failed.‘
        channel.close()
        ssh.close()
        sys.exit()
    buff += resp

print buff
channel.close()
ssh.close()

  

  

  

  

  

  

时间: 2024-08-26 16:14:15

基于python的堡垒机的相关文章

python SQLAlchemy 堡垒机

SQLALchemy ORM db first 数据库操作类 code first 类操作数据库 1.自定义生成表 class 类(base): 列1  列2 根据类去创建表 2.使用类操作表 以后通过类和对象操作数据库 pramiko 堡垒机 ORM 连表 一对多 1.创建表,主动指定外键约束 2.操作 类:repr 单表 连表 session.query(表1).join(表2).all() 多对多 1.创建表,额外的关系表 2.filter() == int_( 都可以是另外一个查询) 3

利用Python在堡垒机模式下批量管理后端nginx服务器

在集群环境中,有时候需要批量修改nginx配置,或批量添加vhost主机.手动一个个添加,效率太慢,借助Fabric写了一个批量分发的脚本,会提高不少效率. 思路: (1.在一台nginx修改配置或添加vhost主机,并测试; (2.测试成功后,将配制文件推送至堡垒机(跳板机); (3.在堡垒机上将配置文件分发至其他nginx服务器 [如果原有文件存在,则先备份] ; 只是在堡垒机环境下一个简单的推送文件脚本,比较简陋,代码如下: # -*- coding:utf-8 -*- #! /usr/b

python之堡垒机(第九天)

本节作业: 通过使用paramiko和sqlalchemy实现堡垒机功能 主要功能实现: 1.用户登录堡垒机后,无需知道密码或密钥可以SSH登录远端服务器: 2.用户对一个组内所有主机批量执行指定命令,获取格式化输出: 3.针对远端主机,可以进行上传下载文件: 4.用户在远端主机上执行的命令,均被记录并入库,实现审计功能: 主要参考了alex的开源代码jumpserver,并添加部分功能. 一.堡垒机具体介绍: 1.堡垒机功能 :        堡垒机,也称为跳板机,多用于系统运维环境中.指的是

Python之堡垒机

本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒机所具备的功能属性中的其中一项而已,下面我就给大家介绍一下堡垒机的重要性,以帮助大家参考自己公司的业务是否需要部署堡垒机. 堡垒机有以下两个至关重要的功能: 权限管理 当你公司的服务器变的越来越多后,需要操作这些服务器的人就肯定不只是一个运维人员,同时也可能包括多个开发人员,那么这么多的人

(转)用Python写堡垒机项目

原文:https://blog.csdn.net/ywq935/article/details/78816860 前言 堡垒机是一种运维安全审计系统.主要的功能是对运维人员的运维操作进行审计和权限控制,风险规避.同时堡垒机还有账号集中管理,单点登陆的功能. 堡垒机有以下两个至关重要的功能: 集中管理 安全审计 当公司的服务器变的越来越多后,需要操作这些服务器的人就肯定不只是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操作业务系统,如果权限分配不当就会存在很大的安全风险,举几个场景例子

IronFort---基于Django和Websocket的堡垒机

WebSSH有很多,基于Django的Web服务也有很多,使用Paramiko在Python中进行SSH访问的就更多了.但是通过gevent将三者结合起来,实现通过浏览器访问的堡垒机就很少见了.本文将简要介绍下我开发的IronFort堡垒机,其详细内容在我的官方网站liujiangblog.com的视频教程中. 一.堡垒机概述 百度百科:堡垒机,在一个特定的网络环境下,为了保障网络和数据不受来自外部和内部用户的入侵和破坏,而运用各种技术手段实时收集和监控网络环境中每一个组成部分的系统状态.安全事

python之实现基于paramiko和mysql数据库的堡垒机

一.堡垒机结构 堡垒机执行流程: 管理员为用户在服务器上创建账号(将公钥放置服务器,或者使用用户名密码) 用户登陆堡垒机,输入堡垒机用户名密码,现实当前用户管理的服务器列表 用户选择服务器,并自动登陆 执行操作并同时将用户操作记录 注:配置.brashrc实现ssh登陆后自动执行脚本,如:/usr/bin/python /home/wupeiqi/menu.py 二.实现过程 步骤一,实现用户登陆 import getpass user = raw_input('username:') pwd

Python之路【第八篇】:堡垒机实例以及数据库操作

Python之路[第八篇]:堡垒机实例以及数据库操作 堡垒机前戏 开发堡垒机之前,先来学习Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作 SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: + import paramiko transport = paramiko.Transport(('hostname', 22)) transport.connect(username='wupeiqi', password='123') ssh

Python之路第一课Day9--随堂笔记之一(堡垒机实例以及数据库操作)未完待续....

一.堡垒机前戏 开发堡垒机之前,先来学习Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作 SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_hosts文件中的主机 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 连接服务器 ssh.c