Python之路——堡壘機原理及其簡單實現


1 堡壘機基本概述  

    其從功能上講,它綜合了核心系統運維和安全審計管控兩大主干功能,從技術實現上講,通過切斷終端計算機對網絡和服務器資源的直接訪問,而采用協議代理的方式,接管了終端計算機對網絡和服務器的訪問。形象地說,終端計算機對目標的訪問,均需要經過運維安全審計的翻譯。打一個比方,運維安全審計扮演着看門者的工作,所有對網絡設備和服務器的請求都要從這扇大門經過。因此運維安全審計能夠攔截非法訪問,和惡意攻擊,對不合法命令進行命令阻斷,過濾掉所有對目標設備的非法訪問行為,並對內部人員誤操作和非法操作進行審計監控,以便事后責任追蹤。一個好的運維審計堡壘機產品應實現對服務器、網絡設備、安全設備等核心資產的運維管理賬號的集中管理、集中認證和授權,通過單點登錄,提供對操作行為的精細化管理和審計,達到運維管理簡單、方便、可靠的目的。堡壘機具備的兩點要求:
   (1)管理方便
  應提供一套簡單直觀的賬號管理、授權管理策略,管理員可快速方便地查找某個用戶,查詢修改訪問權限;同時用戶能夠方便的通過登錄堡壘機對自己的基本信息進行管理,包括賬號、口令等進行修改更新。
  (2)可擴展
  當進行新系統建設或擴容時,需要增加新的設備到堡壘機時,系統應能方便的增加設備數量和設備種類。

2 堡壘機的概念和種類 

  “堡壘”一詞的含義是指用於防守的堅固建築物或比喻難於攻破的事物,因此從字面的意思來看“堡壘機”是指用於防御攻擊的計算機。在實際應用中堡壘機又被稱為“堡壘主機”,是一個主機系統,其自身通常經過了一定的加固,具有較高的安全性,可抵御一定的攻擊,其作用主要是將需要保護的信息系統資源與安全威脅的來源進行隔離,從而在被保護的資源前面形成一個堅固的“堡壘”,並且在抵御威脅的同時又不影響普通用戶對資源的正常訪問。
   基於其應用場景,堡壘機可分為兩種類型:
     2.1 網關型堡壘機
  網關型的堡壘機被部署在外部網絡和內部網絡之間,其本身不直接向外部提供服務而是作為進入內部網絡的一個檢查點,用於提供對內部網絡特定資源的安全訪問控制。這類堡壘機不提供路由功能,將內外網從網絡層隔離開來,因此除非授權訪問外還可以過濾掉一些針對內網的來自應用層以下的攻擊,為內部網絡資源提供了一道安全屏障。但由於此類堡壘機需要處理應用層的數據內容,性能消耗很大,所以隨着網絡進出口處流量越來越大,部署在網關位置的堡壘機逐漸成為了性能瓶頸,因此網關型的堡壘機逐漸被日趨成熟的防火牆、UTM、IPS、網閘等安全產品所取代。
    2.2 運維審計型堡壘機
  運維審計型堡壘機,有時也被稱作“內控堡壘機”,這種類型的堡壘機也是當前應用最為普遍的一種。運維審計型堡壘機的原理與網關型堡壘機類似,但其部署位置與應用場景不同且更為復雜。運維審計型堡壘機被部署在內網中的服務器和網絡設備等核心資源的前面,對運維人員的操作權限進行控制和操作行為審計;運維審計型堡壘機既解決了運維人員權限難以控制的混亂局面,又可對違規操作行為進行控制和審計,而且由於運維操作本身不會產生大規模的流量,堡壘機不會成為性能的瓶頸,所以堡壘機作為運維操作審計的手段得到了快速發展。

3 堡壘機運維操作審計的工作原理  

    作為運維操作審計手段的堡壘機的核心功能是用於實現對運維操作人員的權限控制與操作行為審計。
  3.1 主要技術思路
  如何實現對運維人員的權限控制與審計呢?堡壘機必須能夠截獲運維人員的操作,並能夠分析出其操作的內容。堡壘機的部署方式,確保它能夠截獲運維人員的所有操作行為,分析出其中的操作內容以實現權限控制和行為審計的目的,同時堡壘機還采用了應用代理的技術。
    運維審計型堡壘機對於運維操作人員相當於一台代理服務器(Proxy Server),其工作流程如下圖所示:

   (1)運維人員在操作過程中首先連接到堡壘機,然后向堡壘機提交操作請求;
   (2)該請求通過堡壘機的權限檢查后,堡壘機的應用代理模塊將代替用戶連接到目標設備完成該操作,之后目標設備將操作結果返回給堡壘機,最后堡壘機再將操作結果返回給運維人員。
   通過這種方式,堡壘機邏輯上將運維人員與目標設備隔離開來,建立了從“運維人員->堡壘機用戶賬號->授權->目標設備賬號->目標設備”的管理模式,解決操作權限控制和行為審計
   3.2 工作原理簡介
   下面就簡單介紹一下堡壘機運維操作審計的工作原理,其工作原理示意圖如下:

      在實際使用場景中堡壘機的使用人員通常可分為管理人員、運維操作人員、審計人員三類用戶。
  管理員最重要的職責是根據相應的安全策略和運維人員應有的操作權限來配置堡壘機的安全策略。堡壘機管理員登錄堡壘機后,堡壘機內部“策略管理”組件負責與管理員進行交互,並將管理員輸入的安全策略存儲到堡壘機內部的策略配置庫中。
 “應用代理”組件是堡壘機的核心,負責中轉運維操作用戶的操作並與堡壘機內部其他組件進行交互。“應用代理”組件收到運維人員的操作請求后調用“策略管理”組件對該操作行為進行核查,核查依據便是管理員已經配置好的策略配置庫,如此次操作不符合安全策略,“應用代理”組件將拒絕該操作行為的執行。
  運維人員的操作行為通過“策略管理”組件的核查之后,“應用代理”組件則代替運維人員連接目標設備完成相應操作,並將操作結果返回給對應的運維操作人員;同時此次操作過程被提交給堡壘機內部的“審計模塊”,然后此次操作過程被記錄到審計日志數據庫中。
  最后當需要調查運維人員的歷史操作記錄時,由審計員登錄堡壘機進行查詢,然后“審計模塊”從審計日志數據庫中讀取相應日志記錄並展示在審計員交互界面上。

   3.3 技術基礎

  以下代碼其實是paramiko源碼包里interactive.py的內容,適用於交互執行,前兩種模式適用於linux,模式三適用於windows。  

   (1)模式1—操作linux

 1 #!/usr/bin/env python  2 #-*- coding:utf-8 -*-  3  4 import paramiko  5 import os  6 import sys  7 import select  8 import socket  9 10 tran = paramiko.Transport(('192.168.1.175', 22,)) #創建連接對象 11 tran.start_client() 12 13 ''' 14 #使用密鑰認證 15 default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa') #獲取秘鑰路徑 16 key = paramiko.RSAKey.from_private_key_file(default_path) 17 tran.auth_publickey('root', key) 18 ''' 19 #通過用戶名和密碼認證 20 tran.auth_password('root', '111111') 21 chan = tran.open_session()# 打開一個通道 22 chan.get_pty()# 獲取終端 23 chan.invoke_shell()# 激活一個交互式命令行會話 24 25 ''' 26 # 利用sys.stdin,肆意妄為執行操作 27 # 用戶在終端輸入內容,並將內容發送至遠程服務器 28 # 遠程服務器執行命令,並將結果返回 29 # 用戶終端顯示內容 30 ''' 31 while True: 32 #監視用戶輸入和服務器返回數據 33 #sys.stdin處理用戶輸入 34 #chan是之前創建的通道,用於接收服務器返回信息 35 #監聽chan、終端,這里把chan也當做了文件描述符進行監聽 36 readable, writeable, error = select.select([chan, sys.stdin],[],[],1) 37 #只要輸入發生變化,那么chan、stdin其中之一就會發生變化或者兩者都變化 38 if chan in readable: #捕獲服務端變化 39 try: 40 x = chan.recv(1024) #接收數據,發送接收數據也是基於socket 41 if len(x) == 0: 42 print '\r\n*** EOF\r\n', 43 break 44 sys.stdout.write(x) #輸出到終端 45 sys.stdout.flush() #刷新緩存 46 except socket.timeout: 47 pass 48 if sys.stdin in readable: #捕獲終端輸入 49 inp = sys.stdin.readline() #讀取標准輸入 50 chan.sendall(inp) #發送輸入到服務端 51 52 chan.close() #關閉chan通道 53 tran.close() #關閉連接
實例代碼

   上面的例子中,輸入一行命令只有輸入回車鍵后,sys.stdin才能捕獲到,默認的終端也是這樣設計的。

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import paramiko
 5 import os
 6 import sys
 7 import select
 8 import socket
 9 
10 tran = paramiko.Transport(('192.168.1.175', 22,)) #創建連接對象
11 tran.start_client()
12 
13 '''
14 #使用密鑰認證
15 default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
16 key = paramiko.RSAKey.from_private_key_file(default_path)
17 tran.auth_publickey('root', key)
18 '''
19 
20 tran.auth_password('root', '111111!') #通過密碼認證
21 chan = tran.open_session()# 打開一個通道
22 chan.get_pty()# 獲取一個終端
23 chan.invoke_shell()# 激活交互式命令行終端
24 
25 '''
26 # 利用sys.stdin,肆意妄為執行操作
27 # 用戶在終端輸入內容,並將內容發送至遠程服務器
28 # 遠程服務器執行命令,並將結果返回
29 # 用戶終端顯示內容
30 '''
31 
32 log = open('record.log','ab') #打開一個文件記錄用戶的輸入
33 
34 while True:
35     # 監視用戶輸入和服務器返回數據
36     # sys.stdin 處理用戶輸入
37     # chan 是之前創建的通道,用於接收服務器返回信息
38     readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)  #監聽
39    
40     if chan in readable: #捕獲遠程服務器變化
41         try:
42             x = chan.recv(1024) #接收數據
43             if len(x) == 0:
44                 log.close() #關閉文件
45                 break
46             sys.stdout.write(x)#內容輸出到終端
47             sys.stdout.flush()
48         except socket.timeout:
49             pass
50     if sys.stdin in readable: #捕獲終端輸入
51         inp = sys.stdin.readline() #讀取用戶輸入
52         log.write(inp) #記錄命令
53         chan.sendall(inp)#發送命令
54 
55 chan.close()
56 tran.close()
記錄操作命令代碼

   (2)模式2—操作linux

    首先我們要做的就是修改終端模式:把原來的命令處理方式(即以換行符為命令的結尾),改為輸入一個字符就發送服務器執行,同時支持對特殊字符的處理。

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import paramiko
 5 import os
 6 import sys
 7 import select
 8 import socket
 9 import termios
10 import tty
11 
12 tran = paramiko.Transport(('192.168.1.175', 22,))
13 tran.start_client()
14 
15 '''
16 #使用密鑰認證
17 default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
18 key = paramiko.RSAKey.from_private_key_file(default_path)
19 tran.auth_publickey('root', key)
20 '''
21 tran.auth_password('root', '111111') #通過用戶名密碼認證
22 chan = tran.open_session()# 打開一個通道
23 chan.get_pty()# 獲取一個終端
24 chan.invoke_shell()# 激活交互式終端
25 
26 '''
27 # 利用sys.stdin,肆意妄為執行操作
28 # 用戶在終端輸入內容,並將內容發送至遠程服務器
29 # 遠程服務器執行命令,並將結果返回
30 # 用戶終端顯示內容
31 '''
32 # 獲取原tty屬性
33 oldtty = termios.tcgetattr(sys.stdin)
34 try:
35     # 為tty設置新屬性
36     # 默認當前tty設備屬性:
37     # 輸入一行回車,執行
38     # CTRL+C進程退出,遇到特殊字符,特殊處理。
39     # 這是為原始模式,不認識所有特殊符號
40     # 放置特殊字符應用在當前終端,如此設置,將所有的用戶輸入均發送到遠程服務器
41     tty.setraw(sys.stdin.fileno()) #把tty更換為LINUX原始模式
42     chan.settimeout(0.0)
43 
44     while True:
45         # 監視用戶輸入和遠程服務器返回數據(socket)
46         # 阻塞,直到句柄可讀
47         r, w, e = select.select([chan, sys.stdin], [], [], 1)
48         if chan in r:
49             try:
50                 x = chan.recv(1024)
51                 if len(x) == 0:
52                     print '\r\n*** EOF\r\n',
53                     break
54                 sys.stdout.write(x)
55                 sys.stdout.flush()
56             except socket.timeout:
57                 pass
58         if sys.stdin in r:
59             x = sys.stdin.read(1)
60             if len(x) == 0:
61                 break
62             chan.send(x)
63 
64 finally:
65     # 重新設置終端屬性
66     termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
67 chan.close()
68 tran.close()
修改終端模式后代碼

    記錄操作命令日志,並且不記錄tab輸入  

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import paramiko
 5 import os
 6 import sys
 7 import select
 8 import socket
 9 import termios
10 import tty
11 
12 tran = paramiko.Transport(('192.168.1.175', 22,))
13 tran.start_client()
14 
15 '''
16 #使用密鑰認證
17 default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
18 key = paramiko.RSAKey.from_private_key_file(default_path)
19 tran.auth_publickey('root', key)
20 '''
21 tran.auth_password('root', '111111') #通過用戶名密碼認證
22 chan = tran.open_session() #打開一個通道
23 chan.get_pty()     #獲取一個終端
24 chan.invoke_shell() #激活
25 
26 '''
27 #利用sys.stdin,肆意妄為執行操作
28 #用戶在終端輸入內容,並將內容發送至遠程服務器
29 #遠程服務器執行命令,並將結果返回
30 #用戶終端顯示內容
31 '''
32 #獲取原tty屬性
33 oldtty = termios.tcgetattr(sys.stdin)
34 
35 try:
36     #為tty設置新屬性
37     #默認當前tty設備屬性:
38     #輸入一行回車,執行
39     #CTRL+C進程退出,遇到特殊字符,特殊處理。
40 
41     #這是為原始模式,不認識所有特殊符號
42     #放置特殊字符應用在當前終端,如此設置,將所有的用戶輸入均發送到遠程服務器
43     tty.setraw(sys.stdin.fileno()) #更換為LINUX原始模式
44     chan.settimeout(0.0)
45     #打開文件
46     user_log = open('record.log','ab')
47     
48     while True:
49         # 監視用戶輸入和遠程服務器返回數據
50         # 阻塞,直到句柄可讀
51         r, w, e = select.select([chan, sys.stdin], [], [], 1)
52         if chan in r:
53             try:
54                 x = chan.recv(1024)
55                 if len(x) == 0:
56                     user_log.close()
57                     print '\r\n*** EOF\r\n',
58                     break
59                 sys.stdout.write(x)
60                 sys.stdout.flush()
61             except socket.timeout:
62                 pass
63         if sys.stdin in r:
64             x = sys.stdin.read(1)
65             if len(x) == 0:
66                 break
67             if x == '\t': #判斷用戶的是否為tab如果為tab將不記錄
68                 pass
69             else:
70                 user_log.write(x)#如果用戶輸入的命令保存至日志
71             chan.send(x)
72 
73 finally:
74     # 重新設置終端屬性
75     termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
76 chan.close()
77 tran.close()
記錄操作命令代碼

    (3)模式三—操作windows

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 
 4 import paramiko
 5 import sys
 6 import threading
 7 
 8 tran = paramiko.Transport(('192.168.1.175', 22,))
 9 tran.start_client()
10 
11 '''
12 #使用密鑰認證
13 default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
14 key = paramiko.RSAKey.from_private_key_file(default_path)
15 tran.auth_publickey('root', key)
16 '''
17 tran.auth_password('root', 'nihao123!') #通過姓名密碼認證
18 chan = tran.open_session()# 打開一個通道
19 chan.get_pty()# 獲取一個終端
20 chan.invoke_shell()# 激活
21 
22 '''
23 # 利用sys.stdin,肆意妄為執行操作
24 # 用戶在終端輸入內容,並將內容發送至遠程服務器
25 # 遠程服務器執行命令,並將結果返回
26 # 用戶終端顯示內容
27 '''
28 sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
29 
30 def writeall(sock):
31     while True:
32         data = sock.recv(256)
33         if not data:
34             sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
35             sys.stdout.flush()
36             break
37         sys.stdout.write(data)
38         sys.stdout.flush()
39 
40 writer = threading.Thread(target=writeall, args=(chan,)) #創建了一個線程,去執行writeall方法,參數為chan(建立的SSH連接)
41 writer.start()
42 
43 try:
44     while True: #主線程循環
45         d = sys.stdin.read(1)  #輸入一個字符發送一個
46         if not d:
47             break
48         chan.send(d)
49 except EOFError:
50     pass
51 
52 chan.close()
53 tran.close()
實例代碼

 

待續....

 

參考資料:

        http://sec.chinabyte.com/480/12202480.shtml

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM