3-1. TLS와의 전쟁 2025-10-14 ~ 10-18

10-14: 첫 커밋

proxelar를 포크한 지 2달 만에 첫 커밋이 들어갔다. 사내 개발자 3명이서 시작했다.

원본 proxelar는 Tauri 1 + Rustls 기반의 단순한 구조였다. 우리가 처음 한 일:

  • React Compiler 적용 (SWC → Babel 전환 삽질 후 적용)
  • 문서 사이트 구축 — rspress 기반, 영어/한국어
  • 프론트엔드 정리 — pnpm 전환, 라이선스 추가, 불필요 파일 정리
  • 기존 문서의 링크 수정, 메뉴 구조 정리

이 시점에서는 기본적인 네트워크 테이블과 트래픽 상세 보기 정도만 있었다.

10-15 ~ 16: React Compiler 삽질 삽질

React Compiler를 적용하려고 했는데 쉽지 않았다:

  1. 처음에 Babel React Compiler로 설정
  2. 성능 이슈로 SWC React Compiler로 교체 시도
  3. SWC에서 호환성 문제 발생, 다시 Babel로 복귀
  4. @babel/plugin-syntax-jsx 의존성 문제 해결

같은 기간에 로깅 시스템도 추가했다. feat: logger add라는 심플한 커밋 메시지가 이 시기의 분위기를 보여준다.

10-17: TLS 핸들링의 늪 핵심

proxelar는 Rustls만 사용했다. 문제는 Rustls가 TLS 1.2 이상만 지원한다는 것. 레거시 시스템이나 일부 앱이 TLS 1.0/1.1로 연결하면 핸드셰이크가 실패했다.

하루 동안의 커밋 흐름

이 날의 커밋을 시간 순서대로 따라가면 삽질의 여정이 보인다:

  1. feat: ClientHello 전체 메시지 읽기로 확장하여 SNI 분석 지원
  2. fix: TLS 버전 헤더 매핑 수정 및 SSL 3.0 지원 추가
  3. revert: SNI 감지 로직 제거하고 원래 TLS 버전 기반 로직으로 복원 — SNI로 가려다 포기
  4. feat: 서버 설정 생성 과정에 상세 로깅 추가 — 로그 찍어가며 디버깅
  5. refactor: Hudsucker 방식으로 인증서 생성 통합 및 캐싱 시스템 적용
  6. refactor: Hudsucker 방식으로 TLS 처리 단순화
  7. fix: SignatureAlgorithmsExtensionRequired 에러 해결
  8. feat: OpenSSL 직접 사용으로 TLS 1.0/1.1 클라이언트 지원 구현 — 결국 OpenSSL 도입
  9. fix: api2.cursor.sh OpenSSL 핸드셰이크 호환성 개선
  10. fix: 프록시 목적에 맞게 OpenSSL 설정 수정
  11. fix: api2.cursor.sh를 rustls로 처리하도록 변경 — Cursor IDE와의 호환성 문제
  12. fix: api2.cursor.sh OpenSSL 인증서 처리 개선
  13. feat: OpenSSL 핸드셰이크 상세 진단 로깅 추가

Hybrid TLS Handler 탄생

최종적으로 도달한 해결책: TLS ClientHello 패킷의 첫 바이트를 파싱해서 클라이언트 TLS 버전을 감지한다.

ClientHello 바이트 구조:
[0x16] [0x03, 0x01] [length] [handshake type] ... [client version]
 ↑ TLS   ↑ 레코드 버전              ↑ 실제 클라이언트 버전
  • TLS 1.2+ → Rustls (현대적, 빠름, 안전)
  • TLS 1.0/1.1 → OpenSSL (레거시 지원)

이게 hybrid_tls_handler 모듈이다. tls_version_detector.rs에서 바이트 레벨 파싱을 하고, 결과에 따라 TLS 백엔드를 자동 전환한다.

10-17 ~ 18: 터널 모드 설계

문제: TLS 핸드셰이크가 실패하는 도메인

Apple 서비스(setup.icloud.com 등)는 Certificate Pinning이 걸려 있어서 프록시의 가짜 인증서를 거부한다. 이런 도메인은 인터셉트하면 안 된다.

해결: 터널 모드

일반 모드: [클라이언트] → TLS 복호화 → [프록시] → TLS 재암호화 → [서버]
터널 모드: [클라이언트] → [프록시가 TCP 바이트만 중계] → [서버]

CONNECT 요청을 받으면 200 응답을 먼저 보내고, 이후 TCP 바이트를 양방향으로 그냥 중계한다. 프록시가 암호화 내용을 볼 수 없지만 연결은 유지된다.

트러블슈팅: 터널 응답 순서 버그

fix: tunnel mode now sends CONNECT response before creating tunnel
fix: tunnel mode now completes successfully without error

CONNECT 응답(200)을 보내기 전에 터널을 생성하려고 해서 클라이언트가 혼란에 빠졌다. 순서를 바로잡아서 해결.

10-18: TLS 전략 지능화

refactor: redesign TLS negotiation with intelligent strategy selection

단순히 TLS 버전만 보는 게 아니라, 도메인/확장자/이전 실패 이력을 종합해서 TLS 전략을 지능적으로 선택하는 시스템을 설계했다. 인증서 포맷 개선, 코드 리뷰 반영까지.

이 날로 TLS 관련 초기 작업이 일단락됐다. 나중에(03-05) TLS 자동 학습 바이패스로 더 발전하게 된다.

1줄 요약

Rustls만으로는 레거시 TLS를 지원할 수 없어서, ClientHello 바이트 파싱 → TLS 버전 감지 → Rustls/OpenSSL 자동 전환하는 Hybrid TLS Handler를 만들었다. 하루에 13커밋이 들어간 삽질의 날이었다.