Bootstrap

RASP研发踩坑之attach失败

1.创建一个简单的 Java Agent

主动发起 attach 代码
import com.sun.tools.attach.*;

import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        VirtualMachine vm = null;
        try {
            vm = VirtualMachine.attach("50447");
            // 指定Java Agent的jar包路径
            String agentPath = "../agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
            vm.loadAgent(agentPath, "agent test");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                vm.detach();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
一个简单的Java Agent 代码

Java agent 包的代码:

下载下来之后执行 mvn clean package 打包

2.踩坑 /tmp目录下的 .java_pid 文件被删除之后,attach到目标Java 进程错误

1.问题背景

我们在一批次的主机上开启了RASP,灰度完成之后,发现其中有58台主机执行attach错误

Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
2.初步思路

看上面的告警信息是目标 JVM 进程未响应 socket 连接,没有详细调查问题之前,我对此问题的初步想法(问题出现在哪里)

1.attach失败异常在 rasp多个版本中都有出现,不仅仅在 rasp2.8.0,初步排查问题不在rasp2.8.0存在(也就是说不是2.8.0新特性导致 attach 失败)

2.attach 失败的主机总是出现在一些固定主机上(这点可以排除 rasp attach机制的问题,attach代码没毛病),可能与主机上的Java 服务有关

3.接上面的第2点,attach本质是与jvm进行socket通信,attach失败,可能是jvm无法响应到这个socket连接。(问题的突破点)

3.我想到了哪些可能会影响 attach?

1.jvm处于过载状态是否会响应socket通信;

2.使用 attach机制的工具还有 jmap,jstack,如果 rasp 不能 attach,这些工具应该也是不能 attach 成功的。

4.申请权限登陆目标主机,手动拉起 rasp
2020-04-10 11:49:51.478 Rasp-Server版本:2.8.0 开始尝试注入到进程pid:542 watcher.go:390:Run
2020-04-10 11:49:56.490 Rasp-Server版本:2.8.0 attach出错 watcher.go:350:Exec
2020-04-10 11:49:56.490 Rasp-Server版本:2.8.0 watcher运行失败:exit status 2 sandbox_process.go:130:func

attach 失败!!

查看 Java 服务的 CPU 和内存

PID    USER    %CPU    %MEM   COMMAND

542   sankuai   0.3    23.1    java

Java服务内存和 CPU 都属于正常状态,初步排除 "1.jvm处于过载状态是否会响应socket通信"这个原因

5. 我尝试 使用 jstack -l 542 ,也失败
[sankuai@set-gh-sec-mtscanner-filter-test02 ~]$ jstack 542
542: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
6. 但是Java进程重启之后,jstack 工具可以正常使用,rasp 也正常attach

7. 再次熟悉

链接博客的重点:.attach_pid 与 .java_pid 分别被主动发起attach的进程(这里是rasp-server)和目标JVM进程创建, 并且JVM 创建 .java_pid 由 Djava.io.tmp 路径指定默认为 /tmp

8. 删除 .java_pid 再次使用 jstack -l
[sankuai@set-yp-logan-open-web-test01 tmp]$ jstack -l 68546
68546: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
9.猜测

首次 attach到目标 JVM 后,.java_pid 文件被创建在 /tmp 下,/tmp 目录被系统清理之后,.java_pid被删除了,再次attach (jstack或者 rasp-server重启开启注入)都会失败

10.验证

在开启进程出错的主机上,查询 Java 进程看 Djava.io.tmp=dir的目录下是否有 .java_pid文件

验证主机:set-yp-logan-open-web-test01

结果:没有 .java_pid 文件,符合预期。

11. 出错复现

第一步,启动 java 进程 ,使用jstack 或者 rasp 执行 attach

第二步,删除/tmp 下的 .java_pid

第二步,开启rasp 执行注入,出现attach进程出错。

12. 问题的解决思路

策略1:对于无法开启attach 的 Java 进程只能等待 Java 进程重启之后,生成新的 .java_pid

策略2:Djava.io.tmp 建议指定到 Java进程的运行目录,因为默认的/tmp 容易被清除掉

参考连接:

踩坑2 attach时携带的字符串长度有限制

问题

一般我们会在 attach 时给JVM 传递一些字符串信息,来辅助 Agent 的初始化

vm.loadAgent(agentPath, "agent test");

随着RASP各种需求的增加,需要给 Agent 传输的字符串变的越来越多

这是我们项目中要传递给 jvm 的参数(字符串的长度898):

agentTomcatRule=(DP Server 6|Apache Tomcat/6|Apache Tomcat/7|Apache Tomcat/8|Apache Tomcat/9|6|7|8|9)\..*, classTransLimit=12, confVersion=2.8.0.4_20200512_2, raspHome=/opt/meituan/apps/direwolf/plugin/1004, hookFileWrite=false, pid=30496, hookFileUpload=false, rsDegradeLimit=2, recordTimeSample=50000, offlineLogSize=50, bootClassPath=/opt/meituan/apps/direwolf/plugin/1004/lib/guava-20.0.0.jar:/opt/meituan/apps/direwolf/plugin/1004/lib/mygson-1.0.0.jar, hookFileUploadContent=false, maxFailSize=1, closeWaitTimeInSeconds=6, rsLimitDiskFree=10, agentJettyRule=(8\.1|8\.2|9\.2|9\.3|9\.4)\..*, debug=true, queueSize=500, enableHttpPara=true, rsLimitCpuPid=40, agentJdkRule=1\.(?:7|8)\..*, dumpDir=/tmp/rasp_dump, serverPID=30513, recordTimeThreshold=5, rsLimitMemPid=90, dumpClasses=false, rsCheckEnable=true, rsLimitMemTotal=95, enableHttpReq=true, rsCircuitBreakLimit=129600, agentLogLevel=DEBUG

经过测试,最大可以携带的字符串长度为:938

超过938时,报错如下:

java.io.IOException: Premature EOF
	at sun.tools.attach.HotSpotVirtualMachine.readInt(HotSpotVirtualMachine.java:292)
	at sun.tools.attach.BsdVirtualMachine.execute(BsdVirtualMachine.java:183)
	at sun.tools.attach.HotSpotVirtualMachine.loadAgentLibrary(HotSpotVirtualMachine.java:58)
	at sun.tools.attach.HotSpotVirtualMachine.loadAgentLibrary(HotSpotVirtualMachine.java:79)
	at sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:103)
	at Main.main(Main.java:23)

疑问

attach 携带的字符串没有超过 String类型的最大长度,为什么会报错呢?我想去看下Java源码?

未完....待续