到目前为止,不少公司对堡垒机依然不太感冒,实际上是没有充分认识到堡垒机在IT管理中的重要做用的,不少人以为,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒机所具有的功能属性中的其中一项而已,下面我就给你们介绍一下堡垒机的重要性,以帮助你们参考本身公司的业务是否须要部署堡垒机。python
堡垒机有如下两个相当重要的功能:mysql
当你公司的服务器变的愈来愈多后,须要操做这些服务器的人就确定不仅是一个运维人员,同时也可能包括多个开发人员,那么这么多的人操做业务系统,若是权限分配不当就会存在很大的安全风险,举几个场景例子:ios
设想大家公司有300台Linux服务器,A开发人员须要登陆其中5台WEB服务器查看日志或进行问题追踪等事务,同时对另外10台hadoop服务器有root权限,在有300台服务器规模的网络中,按常理来说你是已经使用了ldap权限统一认证的,你如何使这个开发人员只能以普通用户的身份登陆5台web服务器,而且同时容许他以管理员的身份登陆另外10台hadoop服务器呢?而且同时他对其它剩下的200多台服务器没有访问权限web
目前据我了解,不少公司的运维团队为了方面,整个运维团队的运维人员仍是共享同一套root密码,这样内部信任机制虽然使你们的工做方便了,但同时存在着极大的安全隐患,不少状况下,一个运维人员只须要管理固定数量的服务器,毕竟公司分为不一样的业务线,不一样的运维人员管理的业务线也不一样,但若是共享一套root密码,其实就等于无限放大了每一个运维人员的权限,也就是说,若是某个运维人员想干坏事的话,他能够在几分钟内把整个公司的业务停转,甚至数据都给删除掉。为了下降风险,因而有人想到,把不一样业务线的root密码改掉就ok了么,也就是每一个业务线的运维人员只知道本身的密码,这固然是最简单有效的方式,但问题是若是你同时用了ldap,这样作又比较麻烦,即便你设置了root不经过ldap认证,那新问题就是,每次有运维人员离职,他所在的业务线的密码都须要从新改一次。sql
其实上面的问题,我以为能够很简单的经过堡垒机来实现,收回全部人员的直接登陆服务器的权限,全部的登陆动做都经过堡垒机受权,运维人员或开发人员不知道远程服务器的密码,这些远程机器的用户信息都绑定在了堡垒机上,堡垒机用户只能看到他能用什么权限访问哪些远程服务器。shell
在回收了运维或开发人员直接登陆远程服务器的权限后,其实就等于大家公司生产系统的全部认证过程都经过堡垒机来完成了,堡垒机等于成了大家生产系统的SSO(single sign on)模块了。你只须要在堡垒机上添加几条规则就能实现如下权限控制了:数据库
容许A开发人员经过普通用户登陆5台web服务器,经过root权限登陆10台hadoop服务器,但对其他的服务器无任务访问权限json
多个运维人员能够共享一个root帐户,可是依然能分辨出分别是谁在哪些服务器上操做了哪些命令,由于堡垒机帐户是每一个人独有的,也就是说虽然全部运维人员共享了一同一个远程root帐户,但因为他们用的堡垒帐户都是本身独有的,所以依然能够经过堡垒机控制每一个运维人员访问不一样的机器。windows
审计管理其实很简单,就是把用户的全部操做都纪录下来,以备往后的审计或者事故后的追责。在纪录用户操做的过程当中有一个问题要注意,就是这个纪录对于操做用户来说是不可见的,什么意思?就是指,不管用户愿不肯意,他的操做都会被纪录下来,而且,他本身若是不想操做被纪录下来,或想删除已纪录的内容,这些都是他作不到的,这就要求操做日志对用户来说是不可见和不可访问的,经过堡垒机就能够很好的实现。安全
堡垒机的主要做用权限控制和用户行为审计,堡垒机就像一个城堡的大门,城堡里的全部建筑就是你不一样的业务系统 , 每一个想进入城堡的人都必须通过城堡大门并通过大门守卫的受权,每一个进入城堡的人必须且只能严格按守卫的分配进入指定的建筑,且每一个建筑物还有本身的权限访问控制,不一样级别的人能够到建筑物里不一样楼层的访问级别也是不同的。还有就是,每一个进入城堡的人的全部行为和足迹都会被严格的监控和纪录下来,一旦发生犯罪事件,城堡管理人员就能够经过这些监控纪录来追踪责任人。
垒要想成功彻底记到他的做用,只靠堡垒机自己是不够的, 还须要一系列安全上对用户进行限制的配合,堡垒机部署上后,同时要确保你的网络达到如下条件:
堡垒机执行流程:
实现过程
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() 终极