피드로 돌아가기
Dev.toBackend
원문 읽기
Coroot가 Java 에이전트와 eBPF uprobe를 결합해 JVM 내 암호화된 TLS 트래픽을 평문으로 관찰 가능하게 구현
Making encrypted Java traffic observable with eBPF
AI 요약
Context
Java의 TLS 구현(JSSE)은 네이티브 공유 라이브러리가 아닌 JVM 내부의 Java 코드로 실행되어, eBPF 도구가 SSL_write/SSL_read 같은 기호를 후킹할 수 없다. 따라서 Java 애플리케이션이 MySQL, PostgreSQL, HTTPS를 TLS로 통신할 때 eBPF 도구는 암호화된 데이터만 인식할 수 있었다.
Technical Solution
- Java 에이전트를 JVM Attach API로 동적 로딩: 프로파일러와 디버거가 사용하는 동일한 메커니즘을 활용해 실행 중인 JVM에 에이전트 주입
- SSLSocketImpl 내부 클래스 계측: AppOutputStream.write와 AppInputStream.read 메서드를 ASM 바이트코드 변환으로 후킹하여 TLS 계층 진입/진출 시점에서 평문 캡처
- 네이티브 메모리 복사로 GC 안전성 확보: JNI 호출 반환 후 Java GC가 배열을 이동할 수 있으므로, 평문의 첫 1KB를 스레드 로컬 네이티브 버퍼로 복사
- eBPF uprobe를 네이티브 스텁 함수에 부착: 컴파일러 최적화 방지용 asm volatile 배리어가 포함된 더미 함수를 uprobe 대상으로 설정해 모든 호출 캡처
- 기존 프로토콜 탐지 파이프라인 재사용: 평문 포인터와 크기를 syscall 트레이스포인트와 동일 스레드에서 매칭해 HTTP, MySQL, PostgreSQL, Redis, Kafka 등 자동 파싱
Impact
- CPU 오버헤드 15% 증가: 기준선(370m CPU 코어) 대비 eBPF 에이전트 적용 시 426m으로 증가하면서도 처리량 유지
- OpenTelemetry 대비 38% 낮은 CPU 비용: OTel 에이전트의 511m 코어, 20% 처리량 저하(1000 RPS → 800 RPS)에 비해 개선
Key Takeaway
JVM처럼 TLS 구현이 네이티브 라이브러리로 노출되지 않는 언어에서도, 동적 에이전트 주입과 eBPF uprobe를 조합하면 애플리케이션 코드 변경 없이 암호화된 트래픽의 평문을 관찰할 수 있다. 이 방식은 언어 런타임의 내부 구조를 활용한 관찰성(Observability) 확장의 사례이다.
실천 포인트
Java 마이크로서비스 환경에서 TLS 통신 트래픽을 모니터링해야 할 때, 동적 Java 에이전트로 SSLSocketImpl 메서드를 계측하고 eBPF uprobe로 캡처하면 코드 변경 없이 HTTP, 데이터베이스, 메시지 브로커 프로토콜을 자동으로 파싱할 수 있으며, OpenTelemetry 대비 CPU 오버헤드를 최소화(15% vs 38%)할 수 있다.