VPN 설명에서 TLS라는 단어를 보고 웹 프록시라면 이 트래픽을 이해할 것이라고 결론짓는 것은 흔한 지름길이다. 이 프로토콜 안에서 피어들은 자신들의 패킷을 교환하고, TLS는 그 교환 안의 한 채널의 일부로 동작한다. 사용자에게 중요한 페이로드는 다른 메시지 형식으로 흐르기 때문에, 둘은 일반 HTTPS 세션과 결코 일치하지 않는다.

먼저 봉투가 온다

모든 터널 패킷은 프로토콜 자체의 헤더로 시작한다. 메시지 유형, 키 식별자, 그리고 바이트 스트림 전송에서는 패킷이 어디서 끝나는지 표시하는 길이 접두사다. 그 봉투를 파싱하지 않는 판독자는 다음 필드가 어디서 시작하는지 알 수 없다.

outer transport (UDP datagram or TCP stream)
  -> OpenVPN header (opcode, key id, length where needed)
    -> control message (reliability wrapper, TLS exchange)
    -> data message (protected inner packet)

그 스케치는 바이트 표가 아니라 지도다. 정확한 필드는 메시지 유형에 달려 있지만, 첫 옥텟은 항상 오퍼코드와 키 식별자를 함께 담는다. 포트 번호로 메시지 유형을 추측하는 것이 디코더가 보는 것을 잘못 이름 붙이는 방식이다. 진단에서 중요한 것은 순서다. 전송, 그다음 프로토콜 봉투, 그다음 두 가지 작업 중 하나. 뒤에 무엇이 오는지 판단하기 전에 봉투를 읽어라. 잘못된 지점에서 시작하는 디코더는 자신감 넘치는 헛소리를 만들어 낸다.

신뢰성 계층은 프로토콜에 속한다

제어 메시지는 순서가 있고 확인된 전달이 필요하며, 프로토콜은 확인과 재전송으로 그것을 스스로 제공한다. 확인은 별도의 메시지로 도착하거나 다른 제어 메시지와 함께 이동할 수 있어서, 깔끔한 한 번의 교환을 기대하는 사람에게는 추적이 불규칙해 보인다.

장점은 외부 전송이 UDP여도 TLS 핸드셰이크가 신뢰할 수 있는 반송로를 얻는다는 것이다. 대가는 하나의 데이터그램이 그 대화의 일부만 담을 수 있어서, 그것을 독립된 TLS 레코드로 취급하면 헛소리가 된다는 점이다. 재조립은 프로토콜 계층에서 이루어져야 한다. 그래서 캡처한 패킷이 완전한 핸드셰이크 단계처럼 보이는 경우는 드물다.

외부 전송을 TCP로 바꾼다고 이 계층이 사라지지 않는다. 신뢰할 수 있는 전송은 프로토콜 수준에서 재전송이 줄어든다는 뜻이지, 프레이밍이 사라지거나 트래픽이 HTTP가 된다는 뜻이 아니다.

데이터 패킷은 다른 형식을 쓴다

세션이 생기면 유용한 페이로드는 내부 IP 패킷이나 Ethernet 프레임을 운반하는 데이터 메시지로 도착한다. 그것들은 자체 키와 자체 처리로 보호되며, TLS 라이브러리가 받아들일 어떤 의미에서도 TLS 애플리케이션 레코드가 아니다.

이 분리는 구성에서 드러난다. 제어 채널과 데이터 채널은 별도의 암호 설정을 가지므로, 하나를 지목하는 로그 줄은 다른 하나에 대해 아무것도 말해 주지 않는다. 강한 제어 암호를 강한 데이터 암호의 증거로 읽는 것은 흔한 보고 오류다.

관련된 오해도 설명에서 빼 두어라. TLS는 대칭 데이터 채널 옆에 앉은 공개 키 채널이 아니다. 키가 설정된 뒤에는 TLS 자체가 그 레코드를 대칭적으로 보호하기 때문이다. 진짜 차이는 역할과 패킷 형식이지, 어느 쪽이 어떤 종류의 암호를 쓰는지가 아니다.

HTTPS 프록시가 도움이 되지 않는 이유

TLS를 종료하는 프록시는 TLS 레코드 스트림과 그 안의 애플리케이션 프로토콜을 기대한다. 이 프로토콜의 제어 채널은 자체 프레이밍 안에, 주위에 신뢰성 계층을 두고 운반되므로, 종료하는 프록시는 그 프레이밍을 손상된 입력으로 읽는다.

바이트나 데이터그램을 해석하지 않고 통과시키는 투명 포워더는 스트림을 다시 쓰거나 재조립하지 않는 한 동작할 수 있다. 모든 중개자에 대해 물어야 할 질문은 실제 전송과 프레이밍을 전달하는지이지, 어딘가에서 TLS를 지원하는지가 아니다. 프록시 규칙에 저녁을 쏟기 전에 그것을 물어라. 여기서의 실패 대부분은 빠진 설정이 아니라 구조적이다.

추적을 올바르게 읽기

먼저 제어 교환, 그다음 훨씬 많은 수의 데이터 메시지가 이어지며 둘은 하나의 전송을 공유한다고 예상하라. 제어 단계가 끝나고 데이터 단계가 멈춘다면, 결함은 핸드셰이크가 아니라 키, 라우팅 또는 데이터 경로의 필터링에 관한 것이다. 데이터 메시지를 몇 개 캡처해서 클라이언트를 떠나는지 확인하라. 그 한 가지 관찰이 탐색을 절반으로 가른다.

키 방식에 대한 역사적 설명은 프로토콜 문서 에서 특정 릴리스가 여전히 모든 옛 형식을 지원한다는 뜻이 아니다. 실행하는 버전을 확인한 뒤, 예전에 어떻게 동작했는지 설명하는 문단에 기대를 세워라.

관련 글