RASP 研发踩坑之 attach 失败

用户头像
国服第一
关注
发布于: 2020 年 05 月 27 日
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 包的代码: https://github.com/xl1605368195/SimpleAgent.git

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

2.踩坑 /tmp目录下的 .java_pid<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机制

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

http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/17870c6c1d4e

8. 删除 .java_pid<pid> 再次使用 jstack -l <pid>
[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<pid> 文件被创建在 /tmp 下,/tmp 目录被系统清理之后,.java_pid被删除了,再次attach (jstack或者 rasp-server重启开启注入)都会失败

10.验证

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

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



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

11. 出错复现

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

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

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

12. 问题的解决思路

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

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

参考连接:

https://stackoverflow.com/questions/5769877/attachnotsupportedexception-due-to-missing-java-pid-file-in-attach-api



踩坑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源码?



未完....待续



发布于: 2020 年 05 月 27 日 阅读数: 541
用户头像

国服第一

关注

长期有耐心 2020.05.21 加入

2019年加入美团点评,负责 mt-rasp 研发工作,专注于 java agent 技术

评论

发布
暂无评论
RASP研发踩坑之attach失败