首页 > 系统相关 >详解Go程序添加远程调用tcpdump功能,exec.Command("sh", "-c", "ps -elf | grep xxx | grep x

详解Go程序添加远程调用tcpdump功能,exec.Command("sh", "-c", "ps -elf | grep xxx | grep x

时间:2024-03-13 11:49:04浏览次数:29  
标签:ps grep err xxx host timeout eth tcpdump port

摘自:https://www.jb51.net/article/249001.htm

这篇文章主要介绍了go程序添加远程调用tcpdump功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

 

最近开发的telemetry采集系统上线了。听起来高大上,简单来说就是一个grpc/udp服务端,用户的机器(路由器、交换机)将它们的各种统计数据上报采集、整理后交后端的各类AI分析系统分析。目前华为/思科的大部分设备支持。

上线之后,各类用户开始找来要求帮忙定位问题,一般是上报的数据在后端系统中不存在等等。

在一通抓包分析后,百分之99都是用户自己的问题。但频繁的抓包定位问题,严重的压缩了我摸鱼的时间。而且,这套系统采用多实例方式部署在腾X云多个容器中,一个个的登录抓包,真的很烦。

这让我萌生了一个需求:

  • 主动给采集器下发抓包任务。
  • 将抓包的信息返回。
  • 将抓包的文件暂存,以备进一步定位问题。

方法1

使用fabric等ssh运维工具,编写脚本自动化登录机器后执行tcpdump,然后进一步处理。
很可惜的是,并没有容器母机ssh的权限。只能通过一个web命令行观察容器。这条路玩不转。

方法2

  • 在采集器中添加一个接口,用以下发tcpdump命令
  • 采集器执行tcpdump命令,并获取返回的信息(比如captured xxx pacs),保存相关文件。
  • 将获取的抓包信息以某种方式反发给命令下发人。

使用tcpdump定时抓取并保存信息

首先需要解决tcpdump定时的问题,以免tcpdump无限期的执行抓包,经过一通谷歌,命令如下:

1 timeout 30 tcpdump -i eth0 host 9.123.123.111 and port 6651 -w /tmp/log.cap

timeout 30 指抓取30秒,超时后tcpdump会直接退出
-i 指定抓取的端口
host xxx 源IP
port xxx 源端口

 

编写tcpdump函数

下面到了我最喜欢的写代码阶段,为了简单,直接使用os/exec库。不要笑,很多大厂的很多系统其实都是包命令行工具,解决问题最重要。

// TcpDump 执行tcpdump命令,并返回抓到的包数
func TcpDump(sudo bool, timeout int, eth string, host string, port int) (caps int, err error) {
    portStr := ""
    if port != 0 {
        portStr = fmt.Sprintf("and port %v", port)
    }
    tcpdumpCmd := fmt.Sprintf("timeout %v tcpdump -i %v host %v %v -w /tmp/log.cap", timeout, eth, host, portStr)
    if sudo {
        tcpdumpCmd = "sudo " + tcpdumpCmd
    }
    logrus.Infof("call %v", tcpdumpCmd)
    cmd := exec.Command("sh", "-c", tcpdumpCmd)
    var outb, errb bytes.Buffer
    cmd.Stderr = &errb
    err = cmd.Run()
    if err != nil {
        if !errors.Is(err, &exec.ExitError{}) {
            logrus.Infof("out:%s ; %s", outb.Bytes(), errb.Bytes())
            return getPacs(errb.String()), nil
        }
        return
    }
    return 0,fmt.Errorf("unknown error")
}
func getPacs(input string) int {
    end := strings.Index(input, "packets captured")
    pos := end
    for {
        pos -= 1
        if pos <= 0 {
            return 0
        }
        if input[pos] == '\n' {
            break
        }
    }
    // logrus.Infof("captured:%s", input[pos+1:end-1])
    v, err := strconv.Atoi(input[pos+1 : end-1])
    if err != nil {
        return 0
    }
    return v
}

这里要注意几点:

执行cmd := exec.Command("sh", "-c", tcpdumpCmd)后,tcpdump的返回信息类似:

1 listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes\n56 packets captured\n56 packets received by filter\n0 packets dropped by kernel\n

是在stderr中的。而不是stdout。

getPacs函数简单的从xx packets received中提取出了抓包数。但是如果是中文的服务器系统(不会吧,不会吧),就不太好使了。

编写api

现在函数已经有了,只要再写一个http api,就能很方便的把它暴露出去。

import "github.com/gogf/gf/v2/encoding/gjson"
// ErrJson,写入一个error json,形如:
//{
//    "err": code,
//    "err_msg": msg
//}
func ErrJson(w http.ResponseWriter, errCode int, errStr string) error {
    w.Header().Set("Content-Type", "application/json")
    js := make(map[string]interface{})
    js["err"] = errCode
    js["err_msg"] = errStr
    jsBts, _ := json.Marshal(js)
    _, err := w.Write(jsBts)
    return err
}
/* TcpDumpHandler
req:{
    "sudo":  true,
    "eth": "eth0",
    "host": "10.99.17.135",
    "port": 0
}
rsp:{
    "err": 0,
    "caps": 14
}
*/
func TcpDumpHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    ret, err := ioutil.ReadAll(r.Body)
    if err != nil {
        ErrJson(w, 1, "数据错误")
        return
    }
    js := gjson.New(ret)
    sudo := js.Get("sudo").Bool()
    eth := js.Get("eth").String()
    if eth == "" {
        ErrJson(w, 1, "数据错误, eth不存在")
        return
    }
    host := js.Get("host").String()
    if host == "" {
        ErrJson(w, 1, "数据错误, host不存在")
        return
    }
    port := js.Get("port").Int()
    timeout := js.Get("timeout").Int()
    if timeout == 0 {
        ErrJson(w, 1, "数据错误, timeout为0或不存在")
        return
    }
    go func() {
        chatKey := config.GlobalConfigObj.Global.ChatKey
        botKey := config.GlobalConfigObj.Global.BotKey
        
        // 这里直接利用了公司的一个消息系统,如果贵公司没有这样的系统,就变通一下
        msgSender := msg.NewNiuBiMsg(chatKey, botKey)
        caps, err := TcpDump(sudo, timeout, eth, host, port)
        if err != nil {
            return
        }
        if caps > 0 {
            // 这里直接利用了公司的一个消息系统,向企业IM发一条消息
            msgSender.Send(fmt.Sprintf("tcpdump agent_ip:%v host:%v eth:%v port:%v, captured:%v",
                config.GlobalLocalConfig.LocalIP, host, eth, port, caps))
            bts, err := ioutil.ReadFile("/tmp/log.cap")
            if err != nil {
                return
            }
            b64Caps := base64.StdEncoding.EncodeToString(bts)
            // 把抓包的文件通过这个消息系统也发到企业IM中
            msgSender.File(fmt.Sprintf("pacs_%v.cap", config.GlobalLocalConfig.LocalIP), b64Caps)
        }
    }()
}

然后起一个http svr

func runHttp() {
    mux := http.NewServeMux()
    server :=
        http.Server{
            Addr:         fmt.Sprintf(":%d", 3527),
            Handler:      mux,
            ReadTimeout:  3 * time.Second,
            WriteTimeout: 5 * time.Second,
        }
    // 开始添加路由
    mux.HandleFunc("/tcpdump", tcpdumpsvc.TcpDumpHandler)
    logrus.Infof("run http:%v", 3527)
    logrus.Info(server.ListenAndServe())
}

到这一步,这个系统就基本完成了。使用这个命令就能调用接口。

curl --header "Content-Type: application/json" --request GET --data '{"sudo":false,"eth":"eth0","host":"100.xxx.xxx.10","port":0,"timeout":5}' http://0.0.0.0:3527/tcpdump

这个系统有几个硬伤。

  • 依赖了公司的消息系统完成抓包数据回发的功能。假如各位大佬的公司没有这样的系统msgSender.Send,可行的方法有:
  • scp到一个特定的文件夹。
  • 使用电子邮件。
  • 和领导申请自己开发一套,你看,需求就来了。
  • tcpdump可能会生成极大的抓包文件,此时使用bts, err := ioutil.ReadFile("/tmp/log.cap"),可能会直接让系统OOM。所以设置timeout和抓包的大小(比如在tcpdump命令中使用-c)是很重要的。换句话说,这个api不是公有的,别让不了解的人去调用。

不过这都是小问题。现在用户找上门来,我只需要启动脚本,从服务发现api拉到所有的实例IP,然后依次调用tcpdump api,等待IM的反馈即可。又能快乐的摸鱼啦。

到此这篇关于go程序添加远程调用tcpdump功能的文章就介绍到这了,更多相关go远程调用tcpdump内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

 

标签:ps,grep,err,xxx,host,timeout,eth,tcpdump,port
From: https://www.cnblogs.com/LiuYanYGZ/p/18070278

相关文章

  • golang,再也不用管道了,exec.Command("bash", "-c", "ps -elf | grep xxx")
    摘自:https://www.jb51.net/article/249001.htmfuncSystem_CmdCombinedOutput(cmd_linestring)([]byte,error){mutex_exec.Lock()defermutex_exec.Unlock()//old_handler:=C.set_SIGCHLD_DFL()//自己实现,用c语言保存当前的信号屏蔽字//def......
  • godot show FPS
    varinfo:="FPS:%dDrawcalls:%sGPU时间:%.3fmsCPU时间:%.3fms"#Calledeveryframe.'delta'istheelapsedtimesincethepreviousframe.func_process(delta): varfps=Performance.get_monitor(Performance.TIME_FPS) vardraw_call......
  • 为什么defineProps宏函数不需要从vue中import导入?
    前言我们每天写vue代码时都在用defineProps,但是你有没有思考过下面这些问题。为什么defineProps不需要import导入?为什么不能在非setup顶层使用defineProps?defineProps是如何将声明的props自动暴露给模板?举几个例子我们来看几个例子,分别对应上面的几个问题。先来看一个正常的......
  • 安装JDK11+Tomcat10.0.1+eclipse-jee-2023-12-R-win32-x86_64 配置
    第一步,先双击启动软件:改一下名称:C:\Users\Administrator\eclipse-workspace变成:C:\ProgramFiles\JavaJava:为什么JSP文件要放到SpringBoot工程的src/main/webapp目录下参考文章:https://blog.csdn.net/netyeaxi/article/details/100928105为了看到更具体的页面,可以做个性化......
  • eclipse连接hana数据库修改端口号
    在Eclipse中连接SAPHANA数据库时,如果需要修改端口号,你需要在JDBC连接URL中指定正确的端口。JDBC连接URL通常遵循以下格式:jdbc:sap://<hostname>:<port>/[<instance_number>]?currentschema=<schema_name>在这个URL中:<hostname>是SAPHANA数据库服务器的地址。<port>是SAP......
  • 浅谈非内存对抗类和AI自瞄类FPS作弊程序原理及常用反反作弊措施与反作弊应对手段(上)
    一、引言    闲来无事,在浏览微信公众号的时候无意刷到了江西余江警方关于破获全国首例“AI自瞄”类外挂的案件,涉案金额达到惊人的3000余万。不得不感叹近年来AI相关科技发展之迅速及国内有关于FPS类及其他大类游戏作弊的黑产市场之大。    在工作学习之余,......
  • linuxOPS基础_Linux系统的文件目录结构及用途
    linux系统文件目录结构Linux系统不同于Windows,没有C盘、D盘、E盘那么多的盘符,只有一个根目录(/),所有的文件(资源)都存储在以根目录(/)为树根的树形目录结构中。在Linux根目录(/)下包含很多的子目录,称为一级目录。​例如bin、boot、dev等。​同时,各一级目录下还含有......
  • grep 第四天
    grep第四天1使用grep取passwd显示行数grep-c''/etc/passwd[root@master~]#grep-c''/etc/passwd1352使用grep取passwd显示行号grep-n''/etc/passwd[root@master~]#grep-n''/etc/passwd1:root:x:0:0:root:/ro......
  • 【转】QPS和并发数的关系
     原文: https://www.cnblogs.com/ooo0/p/15963700.html-------------------- QPS:  请求进入的速度并发数:系统中同时存在的请求数 根据Little'sLaw,我们能得到如下的关系式:并发数 = QPS*耗时 以大学招生为例:大一新生的招收速度是5000人/年,每个学生在大学......
  • httpsok-v1.8.1 一分钟搞定SSL证书自动续期
    ......