피드로 돌아가기
Making encrypted Java traffic observable with eBPF
Dev.toDev.to
Backend

Making encrypted Java traffic observable with eBPF

Coroot가 Java 에이전트와 eBPF uprobe를 결합해 JVM 내 암호화된 TLS 트래픽을 평문으로 관찰 가능하게 구현

Coroot2026년 3월 25일9advanced

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%)할 수 있다.

원문 읽기