堡垒机

前景介绍

到目前为止,不少公司对堡垒机依然不太感冒,实际上是没有充分认识到堡垒机在IT管理中的重要做用的,不少人以为,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒机所具有的功能属性中的其中一项而已,下面我就给你们介绍一下堡垒机的重要性,以帮助你们参考本身公司的业务是否须要部署堡垒机。python

堡垒机有如下两个相当重要的功能:mysql

权限管理

当你公司的服务器变的愈来愈多后,须要操做这些服务器的人就确定不仅是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操做业务系统,若是权限分配不当就会存在很大的安全风险,举几个场景例子:ios

  1. 设想大家公司有300台Linux服务器,A开发人员须要登陆其中5台WEB服务器查看日志或进行问题追踪等事务,同时对另外10台hadoop服务器有root权限,在有300台服务器规模的网络中,按常理来说你是已经使用了ldap权限统一认证的,你如何使这个开发人员只能以普通用户的身份登陆5台web服务器,而且同时容许他以管理员的身份登陆另外10台hadoop服务器呢?而且同时他对其它剩下的200多台服务器没有访问权限web

  2. 目前据我了解,不少公司的运维团队为了方面,整个运维团队的运维人员仍是共享同一套root密码,这样内部信任机制虽然使你们的工做方便了,但同时存在着极大的安全隐患,不少状况下,一个运维人员只须要管理固定数量的服务器,毕竟公司分为不一样的业务线,不一样的运维人员管理的业务线也不一样,但若是共享一套root密码,其实就等于无限放大了每一个运维人员的权限,也就是说,若是某个运维人员想干坏事的话,他能够在几分钟内把整个公司的业务停转,甚至数据都给删除掉。为了下降风险,因而有人想到,把不一样业务线的root密码改掉就ok了么,也就是每一个业务线的运维人员只知道本身的密码,这固然是最简单有效的方式,但问题是若是你同时用了ldap,这样作又比较麻烦,即便你设置了root不经过ldap认证,那新问题就是,每次有运维人员离职,他所在的业务线的密码都须要从新改一次。sql

 

其实上面的问题,我以为能够很简单的经过堡垒机来实现,收回全部人员的直接登陆服务器的权限,全部的登陆动做都经过堡垒机受权,运维人员或开发人员不知道远程服务器的密码,这些远程机器的用户信息都绑定在了堡垒机上,堡垒机用户只能看到他能用什么权限访问哪些远程服务器。shell

 

在回收了运维或开发人员直接登陆远程服务器的权限后,其实就等于大家公司生产系统的全部认证过程都经过堡垒机来完成了,堡垒机等于成了大家生产系统的SSO(single sign on)模块了。你只须要在堡垒机上添加几条规则就能实现如下权限控制了:数据库

  1. 容许A开发人员经过普通用户登陆5台web服务器,经过root权限登陆10台hadoop服务器,但对其他的服务器无任务访问权限json

  2. 多个运维人员能够共享一个root帐户,可是依然能分辨出分别是谁在哪些服务器上操做了哪些命令,由于堡垒机帐户是每一个人独有的,也就是说虽然全部运维人员共享了一同一个远程root帐户,但因为他们用的堡垒帐户都是本身独有的,所以依然能够经过堡垒机控制每一个运维人员访问不一样的机器。windows

审计管理

审计管理其实很简单,就是把用户的全部操做都纪录下来,以备往后的审计或者事故后的追责。在纪录用户操做的过程当中有一个问题要注意,就是这个纪录对于操做用户来说是不可见的,什么意思?就是指,不管用户愿不肯意,他的操做都会被纪录下来,而且,他本身若是不想操做被纪录下来,或想删除已纪录的内容,这些都是他作不到的,这就要求操做日志对用户来说是不可见和不可访问的,经过堡垒机就能够很好的实现。安全

堡垒机架构 

堡垒机的主要做用权限控制和用户行为审计,堡垒机就像一个城堡的大门,城堡里的全部建筑就是你不一样的业务系统 , 每一个想进入城堡的人都必须通过城堡大门并通过大门守卫的受权,每一个进入城堡的人必须且只能严格按守卫的分配进入指定的建筑,且每一个建筑物还有本身的权限访问控制,不一样级别的人能够到建筑物里不一样楼层的访问级别也是不同的。还有就是,每一个进入城堡的人的全部行为和足迹都会被严格的监控和纪录下来,一旦发生犯罪事件,城堡管理人员就能够经过这些监控纪录来追踪责任人。 

 

垒要想成功彻底记到他的做用,只靠堡垒机自己是不够的, 还须要一系列安全上对用户进行限制的配合,堡垒机部署上后,同时要确保你的网络达到如下条件:

  • 全部人包括运维、开发等任何须要访问业务系统的人员,只能经过堡垒机访问业务系统
    • 回收全部对业务系统的访问权限,作到除了堡垒机管理人员,没有人知道业务系统任何机器的登陆密码
    • 网络上限制全部人员只能经过堡垒机的跳转才能访问业务系统 
  • 确保除了堡垒机管理员以外,全部其它人对堡垒机自己无任何操做权限,只有一个登陆跳转功能
  • 确保用户的操做纪录不能被用户本身以任何方式获取到并篡改  

堡垒机执行流程:

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

堡垒机功能实现需求

业务需求:

  1. 兼顾业务安全目标与用户体验,堡垒机部署后,不该使用户访问业务系统的访问变的复杂,不然工做将很难推动,由于没人喜欢改变现状,尤为是改变后生活变得更艰难
  2. 保证堡垒机稳定安全运行, 没有100%的把握,不要上线任何新系统,即便有100%把握,也要作好最坏的打算,想好故障预案

功能需求:

  1. 全部的用户操做日志要保留在数据库中
  2. 每一个用户登陆堡垒机后,只须要选择具体要访问的设置,就链接上了,不须要再输入目标机器的访问密码
  3. 容许用户对不一样的目标设备有不一样的访问权限,例:
    1. 对10.0.2.34 有mysql 用户的权限
    2. 对192.168.3.22 有root用户的权限
    3. 对172.33.24.55 没任何权限
  4. 分组管理,便可以对设置进行分组,容许用户访问某组机器,但对组里的不一样机器依然有不一样的访问权限 

设计表结构:

实现过程

import paramiko
import sys
import os
import socket
import select
import getpass
 
tran = paramiko.Transport(('10.211.55.4', 22,))
tran.start_client()
tran.auth_password('wupeiqi', '123')
 
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
 
#########
# 利用sys.stdin,肆意妄为执行操做
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
#########
 
chan.close()
tran.close()
基础一
import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u
 
tran = paramiko.Transport(('10.211.55.4', 22,))
tran.start_client()
tran.auth_password('wupeiqi', '123')
 
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
 
while True:
    # 监视用户输入和服务器返回数据
    # sys.stdin 处理用户输入
    # chan 是以前建立的通道,用于接收服务器返回信息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        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 readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)
 
chan.close()
tran.close()
密码登陆简单版
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import paramiko
import sys
import os
import socket
import select
import getpass
from paramiko.py3compat import u


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 %s@%s: ' % (username, hostname))
    tran.auth_password(username, pw)



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

while True:
    # 监视用户输入和服务器返回数据
    # sys.stdin 处理用户输入
    # chan 是以前建立的通道,用于接收服务器返回信息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    if chan in readable:
        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 readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)

chan.close()
tran.close()

完整示例(一)
简易完整版
import paramiko
import sys
import os
import socket
import select
import getpass
import termios
import tty
from paramiko.py3compat import u
 
tran = paramiko.Transport(('10.211.55.4', 22,))
tran.start_client()
tran.auth_password('wupeiqi', '123')
 
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()
 
 
# 获取原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()
进阶一密码登陆
import paramiko
import sys
import os
import socket
import select
import getpass
import termios
import tty
from paramiko.py3compat import u


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 %s@%s: ' % (username, hostname))
    tran.auth_password(username, pw)

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


# 获取原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()

绝对不改版本
进阶二密码秘钥登陆完整版
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():
    tran = paramiko.Transport(('10.211.55.4', 22,))
    tran.start_client()
    tran.auth_password('wupeiqi', '123')

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

    interactive_shell(chan)

    chan.close()
    tran.close()


if __name__ == '__main__':
    run()

打死也不改版本
进阶二密码登陆完整版

 

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 %s@%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()

终极
Final Version
相关文章
相关标签/搜索