Linux 主機(jī)實(shí)現(xiàn)監(jiān)控對(duì)外訪問(wèn)的 HTTP(s) 和流量

需求背景

目前公司部分業(yè)務(wù)使用阿里云環(huán)境,Linux 主機(jī)使用的是 ECS

  1. 部分 Linux 主機(jī)使用阿里云 SNAT 直接訪問(wèn)外網(wǎng)(沒(méi)辦法監(jiān)控這部分主機(jī)對(duì)外訪問(wèn)的 http(s) 請(qǐng)求和流量)
  2. 部分 Linux 主機(jī)使用 Squid 正向代理,再通過(guò) SNAT 訪問(wèn)外網(wǎng)(可以通過(guò) Squid 監(jiān)控這部分主機(jī)對(duì)外訪問(wèn)的 http(s) 請(qǐng)求和流量)

使用 Squid 正向代理,也有部分問(wèn)題,一個(gè)是無(wú)法代理 TCP 協(xié)議,另一個(gè)是部分 Java 服務(wù)使用的 http(s) Client 無(wú)法直接使用 Squid 正向代理,要做代碼修改。阿里云ECS也不支持修改主機(jī)網(wǎng)關(guān)IP,因此透明代理的方案也行不通

產(chǎn)生的問(wèn)題

平時(shí)偶爾會(huì)收到 SNAT 的 EIP 入方向量告警,也就是對(duì) Linux 主機(jī)對(duì)外請(qǐng)求的流量


解決過(guò)程:

如何實(shí)現(xiàn)監(jiān)控 Linux 主機(jī)對(duì)外訪問(wèn)的 http(s) 和流量呢?(需求背景 1 的 Linux 主機(jī))
思路1:
通過(guò)監(jiān)控 SNAT 可以實(shí)現(xiàn)嗎?
不能,SNAT 只能監(jiān)控到哪臺(tái)主機(jī)往外發(fā)的流量大小,并且還要提工單讓阿里云工程師查詢(xún)

思路2:可以通過(guò)阿里云 云防火墻 監(jiān)控整個(gè) VPC 對(duì)外訪問(wèn)的 HTTP(s) 和流量
可以,但是價(jià)格昂貴

思路3:有沒(méi)有開(kāi)源工具可以實(shí)現(xiàn)監(jiān)控 Linux 主機(jī)對(duì)外訪問(wèn)的 http(s) 和流量
嘗試過(guò) iftop、httpry、jnettop 等工具,基本都無(wú)法監(jiān)控 https 流量

思路4:自己編寫(xiě) Python 腳本,實(shí)現(xiàn)監(jiān)控 Linux 主機(jī)對(duì)外訪問(wèn)的 http(s) 和流量
可以使用 Python scapy 模塊監(jiān)控網(wǎng)絡(luò)流量,但從 https 原理上來(lái)說(shuō), scapy 是不能獲取到 https 的請(qǐng)求地址的

但可以做個(gè)變通,把 DNS 解析的記錄也監(jiān)控,就知道請(qǐng)求了哪個(gè) https 地址
示例腳本如下:

# coding: utf-8
from scapy.all import *
import logging
import re

logging.basicConfig(filename='/data/scripts/tcp_sniffer/packet.log',
                    format='%(asctime)s - %(name)s - %(levelname)s - %(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=10)
# 匹配阿里云SLB健康探測(cè)網(wǎng)段 100.64.0.0/10,參考:https://www.regextester.com/97320
pattern01 = re.compile(r'100\.(?:6[4-9]|[7-9]\d|1[0-1]\d|12[0-7])\.\d{1,3}\.\d{1,3}')
# 匹配一些阿里云內(nèi)網(wǎng)DNS解析字段和NTP解析字段
pattern02 = re.compile(r'in-addr\.arpa|cnszsc|localdomain|aliyuncs\.com|aliyun\.com|NTP')


def packet_callback(pkt):
    for x in pkt:
        if x.haslayer(UDP):
            # x.getlayer(IP).dst --> IP數(shù)據(jù)包里的目標(biāo)IP
            # 阿里云內(nèi)網(wǎng)DNS ['100.100.2.136', '100.100.2.138'] 也在 100.64.0.0/10 網(wǎng)段范圍內(nèi),所以這里先做個(gè)判斷
            if (x.getlayer(IP).dst in ['100.100.2.136', '100.100.2.138']):
                msg = pkt.sprintf(pkt.summary() + " / packet_size: %IP.len%")
                # 過(guò)濾掉 一些阿里云內(nèi)網(wǎng)DNS解析字段和NTP解析字段
                if not pattern02.search(msg):
                    # print '1' + msg
                    logging.info(msg)
        # 過(guò)濾掉 阿里云SLB健康探測(cè)網(wǎng)段 100.64.0.0/10 的請(qǐng)求
        if not pattern01.match(x.getlayer(IP).dst):
            msg = pkt.sprintf(pkt.summary() + " / packet_size: %IP.len%")
            if not pattern02.search(msg):
                # print '2' + msg
                logging.info(msg)


# 增加 store=0 參數(shù),不然內(nèi)存會(huì)持續(xù)升高,
# 參見(jiàn):https://stackoverflow.com/questions/31116970/strange-python-memory-usage-with-scapy
sniff(filter="not (dst net 10.0.0.0/8)", prn=packet_callback, store=0)

// 運(yùn)行 tcp_sniffer.py
# python tcp_sniffer.py 

在另一個(gè)終端測(cè)試 http(s) 請(qǐng)求

# curl -I https://www.baidu.com
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: Keep-Alive
Content-Length: 277
Content-Type: text/html
Date: Thu, 27 Jun 2019 12:02:55 GMT
Etag: "575e1f72-115"
Last-Modified: Mon, 13 Jun 2016 02:50:26 GMT
Pragma: no-cache
Server: bfe/1.0.8.18

實(shí)現(xiàn)效果:

2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / UDP / DNS Qry "www.baidu.com." / packet_size: 59
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / UDP / DNS Qry "www.baidu.com." / packet_size: 59
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https S/ packet_size: 60
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https PA / Raw/ packet_size: 227
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https PA / Raw/ packet_size: 166
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 52
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https A/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https PA / Raw/ packet_size: 147
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https PA / Raw/ packet_size: 71
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https FA/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https R/ packet_size: 40
2019-06-27 20:02:55 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:37626 > 14.215.177.39:https R/ packet_size: 40
2019-06-27 20:03:05 - root - INFO - tcp_sniffer:  Ether / IP / TCP 10.1.18.250:40646 > 116.31.68.142:http FA/ packet_size: 52

這樣就可以把對(duì)外訪問(wèn)的 HTTP(s) 和流量,記錄到日志文件里,或者收集到 ELK、Graylog 等日志系統(tǒng)中。
等到出現(xiàn)告警的時(shí)間點(diǎn),就可以檢查到是 對(duì)外訪問(wèn)的哪些 HTTP(s) URL 引起的告警。



其它問(wèn)題

內(nèi)存占用問(wèn)題:
腳本啟動(dòng)后,發(fā)現(xiàn)內(nèi)存持續(xù)升高,經(jīng)過(guò)排查搜索發(fā)現(xiàn),scapy 會(huì)保存所有捕獲的數(shù)據(jù)包在內(nèi)存中
加一個(gè)參數(shù) scapy sniff store=0 即可解決


參見(jiàn):
https://stackoverflow.com/questions/31116970/strange-python-memory-usage-with-scapy
https://bitbucket.org/secdev/scapy/issues/296/memory-leak-in-sniff-function-with-prn-set

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容