NGINX 블로그에서 QUIC과 HTTP/3에 대한 첫 언급은 4년 전 이었습니다 (!), 여러분과 마찬가지로 저희도 QUIC 구현이 NGINX 오픈 소스 메인라인 브랜치에 임박하여 병합되는 것을 간절히 기대하고 있습니다. 오랜 잉태 기간을 감안하면 여러분이 QUIC에 대해 많은 생각을 하지 않았다면 이해할 만합니다.
하지만 이 시점에서 개발자나 사이트 관리자라면 QUIC이 일부 네트워킹 세부 정보에 대한 책임을 운영 체제에서 NGINX(및 모든 HTTP 앱)로 어떻게 옮기는지 알아야 합니다. 네트워킹이 당신의 분야가 아니더라도 QUIC을 채택한다는 것은 네트워크에 대한 걱정이 이제(적어도 약간은) 당신의 일의 일부가 되었다는 것을 의미합니다.
이 게시물에서는 QUIC에서 사용되는 주요 네트워킹 및 암호화 개념을 살펴보고, 명확성을 추구하기 위해 일부 세부 사항을 간소화하고 필수적이지 않은 정보를 생략합니다. 이 과정에서 약간의 뉘앙스가 사라질 수 있지만, 우리의 의도는 여러분이 환경에서 QUIC를 효과적으로 도입하거나 적어도 지식을 쌓을 수 있는 기반을 제공하는 데 충분한 정보를 제공하는 것입니다.
QUIC가 처음이시라면 먼저 이전 게시물 중 하나를 읽고 개요 영상을 시청하시는 것이 좋습니다.
QUIC에 대한 보다 자세하고 완전한 설명이 필요하면 IETC QUIC 작업 그룹의 QUIC 전송 프로토콜 관리 설명서와 이 문서 전반에 링크된 추가 자료를 읽어보시기 바랍니다 .
QUIC의 네트워킹과 암호화에 관심을 가져야 하는 이유는 무엇입니까?
클라이언트와 NGINX 간의 네트워크 연결에 대한 더러운 세부 사항은 지금까지 대부분의 사용자에게 특별히 관련이 없었습니다. 결국 HTTP/1.x 와 HTTP/2에서는 운영 체제가 클라이언트와 NGINX 간의 전송 제어 프로토콜(TCP) 연결을 설정해 줍니다. NGINX는 연결이 설정되면 간단히 사용합니다.
그러나 QUIC를 사용하면 연결 생성, 검증 및 관리에 대한 책임이 기본 운영 체제에서 NGINX로 이동합니다. NGINX는 설정된 TCP 연결을 수신하는 대신 이제 User Datagram Protocol(UDP) 데이터그램 스트림을 수신하고 이를 클라이언트 연결 및 스트림으로 구문 분석해야 합니다. NGINX는 이제 패킷 손실, 연결 재시작 및 혼잡 제어를 처리할 책임도 있습니다.
또한 QUIC는 연결 시작, 버전 협상 및 암호화 키 교환을 단일 연결 설정 작업으로 결합합니다. TLS 암호화는 QUIC+HTTP/3 및 TCP+HTTP/1+2 모두에서 광범위하게 유사한 방식으로 처리되지만 레이어 4 로드 밸런서, 방화벽 및 보안 어플라이언스와 같은 다운스트림 장치에 중요할 수 있는 차이점이 있습니다.
궁극적으로 이러한 변경 사항의 전반적인 효과는 NGINX 구성이나 운영에 거의 변경 사항이 없이 사용자에게 보다 안전하고 빠르고 안정적인 경험을 제공하는 것입니다. 그러나 NGINX 관리자는 문제가 발생할 경우 무죄까지의 평균 시간을 가능한 한 짧게 유지하기 위해서라도 QUIC 및 NGINX에서 무슨 일이 일어나고 있는지 최소한 조금은 이해해야 합니다.
(이 게시물은 HTTP 작업에 초점을 맞추고 있지만 HTTP/3에는 QUIC가 필요하기 때문에 QUIC는 다른 프로토콜에도 사용될 수 있습니다. RFC 9250 , 전용 QUIC 연결을 통한 DNS 에 정의된 QUIC를 통한 DNS가 좋은 예입니다 .)
소개를 마치고 이제 QUIC 네트워킹의 세부 사항을 자세히 살펴보겠습니다.
TCP 대 UDP
QUIC는 클라이언트와 서버 간에 HTTP 애플리케이션 데이터를 전송하는 데 사용되는 기본 네트워크 프로토콜에 상당한 변경 사항을 도입합니다.
언급했듯이 TCP는 항상 HTTP 웹 애플리케이션 데이터를 전송하는 프로토콜이었습니다. TCP는 IP 네트워크를 통해 데이터를 안정적으로 전달하도록 설계되었습니다. 연결을 설정하고 데이터 수신을 확인하기 위한 잘 정의되고 이해된 메커니즘과 신뢰할 수 없고 혼잡한 네트워크에서 흔히 발생하는 패킷 손실 및 지연을 관리하기 위한 다양한 알고리즘 및 기술이 있습니다.
TCP는 안정적인 전송을 제공하지만 성능과 지연 시간 측면에서 상쇄 효과가 있습니다. 또한 데이터 암호화는 TCP에 내장되어 있지 않아 별도로 구현해야 합니다. 또한 HTTP 트래픽 패턴이 변경되는 상황에서 TCP를 개선하거나 확장하는 것도 어려웠습니다. TCP 처리가 Linux 커널에서 수행되기 때문에 모든 변경 사항은 전체 시스템 성능과 안정성에 예상치 못한 영향을 미치지 않도록 신중하게 설계하고 테스트해야 합니다.
또 다른 문제는 많은 시나리오에서 클라이언트와 서버 간의 HTTP 트래픽이 방화벽이나 로드 밸런서(집합적으로 "미들박스"라고 함)와 같은 여러 TCP 처리 장치를 통과한다는 것입니다. 이로 인해 TCP 표준에 대한 변경 사항을 구현하는 데 느릴 수 있습니다.
QUIC는 대신 UDP를 전송 프로토콜로 사용합니다. UDP는 TCP와 같은 IP 네트워크를 통해 데이터를 전송하도록 설계되었지만 의도적으로 연결 설정 및 안정적인 전달을 폐기합니다. 이러한 오버헤드 부족으로 인해 UDP는 효율성과 속도가 안정성보다 더 중요한 많은 애플리케이션에 적합합니다.
그러나 대부분의 웹 애플리케이션의 경우 안정적인 데이터 전달이 필수적입니다. 기본 UDP 전송 계층은 안정적인 데이터 전달을 제공하지 않으므로 이러한 기능은 QUIC(또는 애플리케이션 자체)에서 제공해야 합니다. 다행히도 QUIC은 이와 관련하여 TCP에 비해 몇 가지 이점이 있습니다.
- QUIC 처리가 Linux 사용자 공간에서 수행되므로 특정 작업의 문제가 전체 시스템에 미치는 위험이 적습니다. 이를 통해 새로운 기능의 빠른 개발이 더욱 실현 가능해집니다.
- 위에서 언급한 "중간 상자"는 일반적으로 UDP 트래픽의 최소한의 처리를 수행하므로 QUIC 프로토콜의 향상을 제한하지 않습니다.
단순화된 QUIC 네트워크 해부학
QUIC 스트림 은 HTTP/3 요청 또는 응답(또는 다른 애플리케이션 데이터)을 포함하는 논리적 객체입니다. 네트워크 엔드포인트 간 전송의 경우 다이어그램에 표시된 대로 여러 논리적 계층 내부에 래핑됩니다.
그림 1. QUIC 스트림의 해부학외부에서 시작하여 내부로 살펴보면 논리적 계층과 객체는 다음과 같습니다.
연결 설정
익숙한 SYN//SYN-ACKACK 3방향 핸드셰이크는 TCP 연결을 설정합니다 .
QUIC 연결을 설정하는 데는 비슷한 단계가 필요하지만 더 효율적입니다. 또한 암호화 핸드셰이크의 일부로 연결 설정에 주소 검증을 빌드합니다. 주소 검증은 악의적인 행위자가 의도한 공격 피해자에 대한 스푸핑된 소스 주소 정보가 포함된 패킷을 서버에 보내는 트래픽 증폭 공격으로부터 방어합니다. 공격자는 서버가 공격자가 스스로 생성할 수 있는 것보다 더 많거나 더 큰 패킷을 피해자에게 생성하여 엄청난 양의 트래픽이 발생하기를 바랍니다. (자세한 내용은 RFC 9000, QUIC: UDP 기반 다중화 및 보안 전송 의 섹션 8을 참조하세요 .)
연결 설정의 일부로 클라이언트와 서버는 QUIC 헤더에 인코딩된 독립적인 연결 ID를 제공하여 클라이언트 소스 IP 주소와 상관없이 연결을 간단히 식별합니다.
그러나 QUIC 연결의 초기 설정에는 TLS 암호화 키 교환 작업도 포함되므로 SYN-ACKTCP 연결 설정 중에 생성하는 간단한 응답보다 서버에 더 많은 컴퓨팅 비용이 듭니다. 또한 키 교환 작업이 발생하기 전에 클라이언트 IP 주소가 검증되지 않기 때문에 분산 서비스 거부(DDoS) 공격에 대한 잠재적 벡터가 생성됩니다.
하지만 복잡한 암호화 작업이 시작되기 전에 클라이언트 IP 주소를 검증하도록 NGINX를 구성할 수 있습니다. 지시문을 . quic_retry으로 설정합니다 on. 이 경우 NGINX는 클라이언트에게 토큰이 포함된 재시도 패킷을 보내는데 , 클라이언트는 이를 연결 설정 패킷에 포함해야 합니다.
이 메커니즘은 3방향 TCP 핸드셰이크와 다소 유사하며, 중요한 점은 클라이언트가 제시하는 소스 IP 주소를 소유하고 있다는 것을 확립한다는 것입니다. 이 확인이 없으면 NGINX와 같은 QUIC 서버는 스푸핑된 소스 IP 주소로 쉬운 DoS 공격에 취약할 수 있습니다. (이러한 공격을 완화하는 또 다른 QUIC 메커니즘은 모든 초기 연결 패킷을 최소 1200바이트로 패딩해야 한다는 요구 사항으로, 패킷을 보내는 데 더 많은 비용이 드는 작업이 됩니다.)
또한 재시도 패킷 SYN은 클라이언트에 보내는 연결 ID에 연결 세부 정보를 인코딩하여 TCP 플러드 공격(메모리에 저장된 열린 상태이지만 완료되지 않은 핸드셰이크가 엄청나게 많아 서버 리소스가 고갈되는 공격)과 유사한 공격을 완화합니다. 이는 클라이언트가 나중에 제공한 연결 ID와 토큰에서 연결 정보를 재구성할 수 있으므로 서버 측 정보를 보관할 필요가 없다는 추가적인 이점이 있습니다. 이 기술은 TCP SYN쿠키와 유사합니다. 또한 NGINX와 같은 QUIC 서버는 클라이언트의 향후 연결에서 사용할 만료 토큰을 제공하여 연결 재개 속도를 높일 수 있습니다.
연결 ID를 사용하면 연결이 기본 전송 계층과 독립적이 될 수 있으므로 네트워킹의 변경으로 인해 연결이 끊어지지 않아도 됩니다. 이는 Gracefully Managing Client IP Address Changes 에서 설명합니다 .
손실 감지
연결이 설정되고( 아래에서 더 자세히 설명할 것처럼 암호화가 활성화됨 ) HTTP 요청과 응답은 클라이언트와 NGINX 사이에서 앞뒤로 흐를 수 있습니다. UDP 데이터그램이 전송되고 수신됩니다. 그러나 이러한 데이터그램 중 일부가 손실되거나 지연되는 원인이 될 수 있는 여러 요소가 있습니다.
TCP는 패킷 전달을 확인하고, 패킷 손실 또는 지연을 감지하고, 손실된 패킷의 재전송을 관리하고, 적절하게 시퀀싱되고 완전한 데이터를 애플리케이션 계층에 전달하는 복잡한 메커니즘을 가지고 있습니다. UDP는 이러한 기능이 없으므로 혼잡 제어 및 손실 감지가 QUIC 계층에 구현됩니다.
- 클라이언트와 서버는 모두 수신한 각 QUIC 패킷에 대해 명시적인 확인을 보냅니다(다만, 우선순위가 낮은 프레임만 포함된 패킷은 즉시 확인되지 않습니다).
안정적인 전달이 필요한 프레임이 포함된 패킷이 정해진 시간 초과 후에도 확인되지 않으면 해당 패킷은 손실된 것으로 간주됩니다.
시간 초과 기간은 패킷의 내용에 따라 달라집니다. 예를 들어, 암호화를 수립하고 연결을 설정하는 데 필요한 패킷의 시간 초과는 더 짧습니다. 이는 이러한 패킷이 QUIC 핸드셰이크 성능에 필수적이기 때문입니다.
- 패킷이 손실된 것으로 간주되면, 손실된 프레임은 새로운 패킷으로 재전송되며, 이 패킷에는 새로운 시퀀스 번호가 부여됩니다.
- 패킷 수신자는 패킷의 스트림 ID와 오프셋을 사용하여 전송된 데이터를 올바른 순서로 조립합니다. 패킷 번호는 전송 순서만 지시하며, 패킷을 어떻게 조립해야 하는지는 지시하지 않습니다.
- 수신기에서의 데이터 어셈블리는 전송 순서와 무관하기 때문에 손실되거나 지연된 패킷은 연결의 모든 스트림이 아니라 포함된 개별 스트림에만 영향을 미칩니다. 이는 스트림이 전송 계층의 일부가 아니기 때문에 HTTP/ 1.x 및 HTTP/2에 영향을 미치는 헤드 오브 라인 차단 문제를 제거합니다 .
손실 감지에 대한 완전한 설명은 이 프라이머의 범위를 벗어납니다. 시간 초과를 결정하는 메커니즘과 전송 중에 허용되는 승인되지 않은 데이터의 양에 대한 자세한 내용은 RFC 9002 , QUIC 손실 감지 및 혼잡 제어를 참조하세요.
클라이언트 IP 주소 변경을 우아하게 관리하기
클라이언트의 IP 주소(애플리케이션 세션의 맥락에서 소스 IP 주소 라고 함 )는 세션 중에 변경될 수 있습니다. 예를 들어 VPN이나 게이트웨이가 공용 주소를 변경하거나 스마트폰 사용자가 WiFi가 적용되는 위치를 떠나 셀룰러 네트워크로 전환해야 하는 경우입니다. 또한 네트워크 관리자는 전통적으로 TCP 연결보다 UDP 트래픽에 대한 타임아웃을 낮게 설정해 네트워크 주소 변환 (NAT) 리바인딩 가능성이 높아집니다.
QUIC는 발생할 수 있는 중단을 줄이기 위한 두 가지 메커니즘을 제공합니다. 클라이언트는 서버에 주소가 변경될 것임을 사전에 알릴 수 있고, 서버는 클라이언트 주소의 계획되지 않은 변경을 우아하게 처리할 수 있습니다. 연결 ID는 전환 중에도 일관되게 유지되므로, 확인되지 않은 프레임은 새 IP 주소로 다시 전송할 수 있습니다.
QUIC 세션 중에 소스 IP 주소가 변경되면 소스 IP 주소와 포트를 사용하여 특정 UDP 데이터그램을 수신할 업스트림 서버를 결정하는 다운스트림 로드 밸런서(또는 기타 레이어 4 네트워킹 구성 요소)에 문제가 발생할 수 있습니다. 올바른 트래픽 관리를 보장하기 위해 레이어 4 네트워크 장치 공급자는 QUIC 연결 ID를 처리하도록 업데이트해야 합니다. 로드 밸런싱 및 QUIC의 미래에 대해 자세히 알아보려면 IETF 초안 QUIC‑LB: Generating Routable QUIC Connection IDs를 참조하세요 .
암호화
Connection Establishment 에서 초기 QUIC 핸드셰이크가 단순히 연결을 설정하는 것 이상의 역할을 한다는 사실을 언급했습니다. TCP의 TLS 핸드셰이크와 달리 UDP에서는 키와 TLS 1.3 암호화 매개변수의 교환이 초기 연결의 일부로 발생합니다. 이 기능은 여러 교환을 제거하고 클라이언트가 이전 연결을 재개할 때 왕복 시간(0‑RTT)을 0으로 설정합니다.
그림 4. TCP+TLS/1.3 및 QUIC에 대한 암호화 핸드셰이크 비교암호화 핸드셰이크를 연결 설정 프로세스에 접목하는 것 외에도 QUIC는 TCP+TLS보다 더 많은 메타데이터를 암호화합니다. 키 교환이 발생하기 전에도 초기 연결 패킷이 암호화됩니다. 도청자는 여전히 키를 파생시킬 수 있지만 암호화되지 않은 패킷보다 더 많은 노력이 필요합니다. 이렇게 하면 공격자와 잠재적인 국가 수준 검열자 모두에게 중요한 SNI(Server Name Indicator)와 같은 데이터를 더 잘 보호할 수 있습니다. 그림 5는 QUIC가 TCP+TLS보다 잠재적으로 더 민감한 메타데이터(빨간색)를 암호화하는 방법을 보여줍니다.
그림 5. QUIC는 TCP+TLS보다 더 민감한 메타데이터를 암호화합니다.QUIC 페이로드의 모든 데이터는 TLS 1.3을 사용하여 암호화됩니다. 두 가지 이점이 있습니다. 오래되고 취약한 암호 제품군과 해싱 알고리즘이 허용되지 않으며, 전방 비밀성(FS) 키 교환 메커니즘이 필수입니다. 전방 비밀성은 공격자가 개인 키와 트래픽 사본을 캡처하더라도 공격자가 데이터를 해독하지 못하도록 합니다.
낮음 및 0 RTT 연결로 대기 시간 단축
애플리케이션 데이터를 전송하기 전에 클라이언트와 서버 간에 발생해야 하는 왕복 횟수를 줄이면 애플리케이션 성능이 향상되며, 특히 지연 시간이 긴 네트워크에서 성능이 향상됩니다.
TLS 1.3은 암호화된 연결을 설정하기 위해 단일 왕복을 도입했으며, 연결을 재개하기 위해 왕복을 0번 수행했지만 TCP의 경우 TLS 클라이언트 Hello 전에 핸드셰이크가 발생해야 함을 의미합니다.
QUIC는 암호화 작업과 연결 설정을 결합하기 때문에 클라이언트가 첫 번째 QUIC 패킷에서 요청을 보낼 수 있는 진정한 0‑RTT 연결 재수립을 제공합니다. 이는 첫 번째 요청 전에 연결 설정을 위한 초기 왕복을 제거하여 지연 시간을 줄입니다.
그림 6. TCP+TLS와 QUIC를 사용하여 연결을 재설정하는 데 필요한 메시지 비교이 경우, 클라이언트는 이전 연결에서 사용된 매개변수로 암호화된 HTTP 요청을 보내고, 주소 검증 목적으로 이전 연결 중에 서버가 제공한 토큰을 포함합니다.
불행히도 0‑RTT 연결 재개는 순방향 비밀성을 제공하지 않으므로 초기 클라이언트 요청은 교환의 다른 트래픽만큼 안전하게 암호화되지 않습니다. 첫 번째 요청을 넘어서는 요청과 응답은 순방향 비밀성으로 보호됩니다. 아마도 더 문제가 될 수 있는 것은 초기 요청이 리플레이 공격 에도 취약하다는 것입니다 . 리플레이 공격은 공격자가 초기 요청을 캡처하여 서버에 여러 번 리플레이할 수 있습니다.
많은 애플리케이션과 웹사이트의 경우, 0‑RTT 연결 재개로 인한 성능 향상이 이러한 잠재적 취약성보다 더 크지만, 이는 사용자가 스스로 결정해야 할 사항입니다.
이 기능은 NGINX에서 기본적으로 비활성화되어 있습니다. 활성화하려면 ssl_early_data지시문을 .으로 설정합니다 on.
Alt-Svc헤더를 사용하여 HTTP/1.1에서 HTTP/3으로 이동
거의 모든 클라이언트(특히 브라우저)는 TCP/TLS를 통해 초기 연결을 합니다. 서버가 QUIC+HTTP/3를 지원하는 경우 헤더 h3에 매개변수를 포함하는 HTTP/1.1 응답을 반환하여 클라이언트에 해당 사실을 알립니다 Alt-Svc. 그런 다음 클라이언트는 QUIC+HTTP/3를 사용할지 아니면 이전 버전의 HTTP를 고수할지 선택합니다. (흥미로운 점은 RFC 7838Alt-Svc 에 정의된 헤더가 QUIC보다 이전이며 다른 용도로도 사용할 수 있다는 것입니다.)
그림 7. Alt-Svc헤더가 HTTP/1.1에서 HTTP/3로 연결을 변환하는 데 사용되는 방법
헤더 Alt-Svc는 클라이언트에게 동일한 서비스가 대체 호스트, 프로토콜 또는 포트(또는 이들의 조합)에서 사용 가능하다는 것을 알려줍니다. 또한 클라이언트는 이 서비스가 계속 사용 가능하다고 가정해도 안전한 기간을 알 수 있습니다.
몇 가지 예:
| Alt-Svc: h3=":443" | 이 서버에서는 포트 443에서 HTTP/3을 사용할 수 있습니다. |
| Alt-Svc: h3="new.example.com:8443" | HTTP/3는 포트 8443의 서버 new.example.com 에서 사용 가능합니다. |
| Alt-Svc: h3=":8443"; ma=600 | HTTP/3은 이 서버에서 포트 8443에서 사용할 수 있으며 최소 10분 동안 사용할 수 있습니다. |
필수는 아니지만 대부분의 경우 서버는 TCP+TLS와 동일한 포트에서 QUIC 연결에 응답하도록 구성됩니다.
헤더를 포함하도록 NGINX를 구성하려면 지시문을 Alt-Svc사용합니다 add_header. 이 예에서 $server_port변수는 NGINX가 클라이언트가 TCP+TLS 요청을 보낸 포트에서 QUIC 연결을 허용하고 86,400은 24시간임을 의미합니다.
add_header Alt-Svc 'h3=":$server_port"; ma=86400';
결론
이 블로그에서는 QUIC에 대한 간단한 입문서를 제공하고, QUIC에서 사용되는 주요 네트워킹 및 암호화 작업을 이해하는 데 충분한 개요를 제공합니다.
QUIC + HTTP/3에 대한 NGINX 구성에 대한 보다 포괄적인 내용은 블로그에서 NGINX QUIC+HTTP/3 구현을 위한 바이너리 패키지가 현재 사용 가능하다는 내용을 읽거나, NGINX 및 QUIC+HTTP/3에 대한 실습 웨비나를 시청하세요. QUIC+HTTP/3에 대한 모든 NGINX 지시문에 대한 세부 정보와 사전 빌드된 바이너리를 설치하거나 소스에서 빌드하는 방법에 대한 전체 지침은 NGINX QUIC 웹페이지를 참조하세요 .
NGINX 블로그에서 QUIC과 HTTP/3에 대한 첫 언급은 4년 전 이었습니다 (!), 여러분과 마찬가지로 저희도 QUIC 구현이 NGINX 오픈 소스 메인라인 브랜치에 임박하여 병합되는 것을 간절히 기대하고 있습니다. 오랜 잉태 기간을 감안하면 여러분이 QUIC에 대해 많은 생각을 하지 않았다면 이해할 만합니다.
하지만 이 시점에서 개발자나 사이트 관리자라면 QUIC이 일부 네트워킹 세부 정보에 대한 책임을 운영 체제에서 NGINX(및 모든 HTTP 앱)로 어떻게 옮기는지 알아야 합니다. 네트워킹이 당신의 분야가 아니더라도 QUIC을 채택한다는 것은 네트워크에 대한 걱정이 이제(적어도 약간은) 당신의 일의 일부가 되었다는 것을 의미합니다.
이 게시물에서는 QUIC에서 사용되는 주요 네트워킹 및 암호화 개념을 살펴보고, 명확성을 추구하기 위해 일부 세부 사항을 간소화하고 필수적이지 않은 정보를 생략합니다. 이 과정에서 약간의 뉘앙스가 사라질 수 있지만, 우리의 의도는 여러분이 환경에서 QUIC를 효과적으로 도입하거나 적어도 지식을 쌓을 수 있는 기반을 제공하는 데 충분한 정보를 제공하는 것입니다.
QUIC가 처음이시라면 먼저 이전 게시물 중 하나를 읽고 개요 영상을 시청하시는 것이 좋습니다.
QUIC에 대한 보다 자세하고 완전한 설명이 필요하면 IETC QUIC 작업 그룹의 QUIC 전송 프로토콜 관리 설명서와 이 문서 전반에 링크된 추가 자료를 읽어보시기 바랍니다 .
QUIC의 네트워킹과 암호화에 관심을 가져야 하는 이유는 무엇입니까?
클라이언트와 NGINX 간의 네트워크 연결에 대한 더러운 세부 사항은 지금까지 대부분의 사용자에게 특별히 관련이 없었습니다. 결국 HTTP/1.x 와 HTTP/2에서는 운영 체제가 클라이언트와 NGINX 간의 전송 제어 프로토콜(TCP) 연결을 설정해 줍니다. NGINX는 연결이 설정되면 간단히 사용합니다.
그러나 QUIC를 사용하면 연결 생성, 검증 및 관리에 대한 책임이 기본 운영 체제에서 NGINX로 이동합니다. NGINX는 설정된 TCP 연결을 수신하는 대신 이제 User Datagram Protocol(UDP) 데이터그램 스트림을 수신하고 이를 클라이언트 연결 및 스트림으로 구문 분석해야 합니다. NGINX는 이제 패킷 손실, 연결 재시작 및 혼잡 제어를 처리할 책임도 있습니다.
또한 QUIC는 연결 시작, 버전 협상 및 암호화 키 교환을 단일 연결 설정 작업으로 결합합니다. TLS 암호화는 QUIC+HTTP/3 및 TCP+HTTP/1+2 모두에서 광범위하게 유사한 방식으로 처리되지만 레이어 4 로드 밸런서, 방화벽 및 보안 어플라이언스와 같은 다운스트림 장치에 중요할 수 있는 차이점이 있습니다.
궁극적으로 이러한 변경 사항의 전반적인 효과는 NGINX 구성이나 운영에 거의 변경 사항이 없이 사용자에게 보다 안전하고 빠르고 안정적인 경험을 제공하는 것입니다. 그러나 NGINX 관리자는 문제가 발생할 경우 무죄까지의 평균 시간을 가능한 한 짧게 유지하기 위해서라도 QUIC 및 NGINX에서 무슨 일이 일어나고 있는지 최소한 조금은 이해해야 합니다.
(이 게시물은 HTTP 작업에 초점을 맞추고 있지만 HTTP/3에는 QUIC가 필요하기 때문에 QUIC는 다른 프로토콜에도 사용될 수 있습니다. RFC 9250 , 전용 QUIC 연결을 통한 DNS 에 정의된 QUIC를 통한 DNS가 좋은 예입니다 .)
소개를 마치고 이제 QUIC 네트워킹의 세부 사항을 자세히 살펴보겠습니다.
TCP 대 UDP
QUIC는 클라이언트와 서버 간에 HTTP 애플리케이션 데이터를 전송하는 데 사용되는 기본 네트워크 프로토콜에 상당한 변경 사항을 도입합니다.
언급했듯이 TCP는 항상 HTTP 웹 애플리케이션 데이터를 전송하는 프로토콜이었습니다. TCP는 IP 네트워크를 통해 데이터를 안정적으로 전달하도록 설계되었습니다. 연결을 설정하고 데이터 수신을 확인하기 위한 잘 정의되고 이해된 메커니즘과 신뢰할 수 없고 혼잡한 네트워크에서 흔히 발생하는 패킷 손실 및 지연을 관리하기 위한 다양한 알고리즘 및 기술이 있습니다.
TCP는 안정적인 전송을 제공하지만 성능과 지연 시간 측면에서 상쇄 효과가 있습니다. 또한 데이터 암호화는 TCP에 내장되어 있지 않아 별도로 구현해야 합니다. 또한 HTTP 트래픽 패턴이 변경되는 상황에서 TCP를 개선하거나 확장하는 것도 어려웠습니다. TCP 처리가 Linux 커널에서 수행되기 때문에 모든 변경 사항은 전체 시스템 성능과 안정성에 예상치 못한 영향을 미치지 않도록 신중하게 설계하고 테스트해야 합니다.
또 다른 문제는 많은 시나리오에서 클라이언트와 서버 간의 HTTP 트래픽이 방화벽이나 로드 밸런서(집합적으로 "미들박스"라고 함)와 같은 여러 TCP 처리 장치를 통과한다는 것입니다. 이로 인해 TCP 표준에 대한 변경 사항을 구현하는 데 느릴 수 있습니다.
QUIC는 대신 UDP를 전송 프로토콜로 사용합니다. UDP는 TCP와 같은 IP 네트워크를 통해 데이터를 전송하도록 설계되었지만 의도적으로 연결 설정 및 안정적인 전달을 폐기합니다. 이러한 오버헤드 부족으로 인해 UDP는 효율성과 속도가 안정성보다 더 중요한 많은 애플리케이션에 적합합니다.
그러나 대부분의 웹 애플리케이션의 경우 안정적인 데이터 전달이 필수적입니다. 기본 UDP 전송 계층은 안정적인 데이터 전달을 제공하지 않으므로 이러한 기능은 QUIC(또는 애플리케이션 자체)에서 제공해야 합니다. 다행히도 QUIC은 이와 관련하여 TCP에 비해 몇 가지 이점이 있습니다.
단순화된 QUIC 네트워크 해부학
QUIC 스트림 은 HTTP/3 요청 또는 응답(또는 다른 애플리케이션 데이터)을 포함하는 논리적 객체입니다. 네트워크 엔드포인트 간 전송의 경우 다이어그램에 표시된 대로 여러 논리적 계층 내부에 래핑됩니다.
외부에서 시작하여 내부로 살펴보면 논리적 계층과 객체는 다음과 같습니다.
QUIC 헤더 - 패킷에 대한 메타데이터를 포함합니다. 헤더에는 두 가지 유형이 있습니다.
연결 설정
익숙한 SYN//SYN-ACKACK 3방향 핸드셰이크는 TCP 연결을 설정합니다 .
QUIC 연결을 설정하는 데는 비슷한 단계가 필요하지만 더 효율적입니다. 또한 암호화 핸드셰이크의 일부로 연결 설정에 주소 검증을 빌드합니다. 주소 검증은 악의적인 행위자가 의도한 공격 피해자에 대한 스푸핑된 소스 주소 정보가 포함된 패킷을 서버에 보내는 트래픽 증폭 공격으로부터 방어합니다. 공격자는 서버가 공격자가 스스로 생성할 수 있는 것보다 더 많거나 더 큰 패킷을 피해자에게 생성하여 엄청난 양의 트래픽이 발생하기를 바랍니다. (자세한 내용은 RFC 9000, QUIC: UDP 기반 다중화 및 보안 전송 의 섹션 8을 참조하세요 .)
연결 설정의 일부로 클라이언트와 서버는 QUIC 헤더에 인코딩된 독립적인 연결 ID를 제공하여 클라이언트 소스 IP 주소와 상관없이 연결을 간단히 식별합니다.
그러나 QUIC 연결의 초기 설정에는 TLS 암호화 키 교환 작업도 포함되므로 SYN-ACKTCP 연결 설정 중에 생성하는 간단한 응답보다 서버에 더 많은 컴퓨팅 비용이 듭니다. 또한 키 교환 작업이 발생하기 전에 클라이언트 IP 주소가 검증되지 않기 때문에 분산 서비스 거부(DDoS) 공격에 대한 잠재적 벡터가 생성됩니다.
하지만 복잡한 암호화 작업이 시작되기 전에 클라이언트 IP 주소를 검증하도록 NGINX를 구성할 수 있습니다. 지시문을 . quic_retry으로 설정합니다 on. 이 경우 NGINX는 클라이언트에게 토큰이 포함된 재시도 패킷을 보내는데 , 클라이언트는 이를 연결 설정 패킷에 포함해야 합니다.
이 메커니즘은 3방향 TCP 핸드셰이크와 다소 유사하며, 중요한 점은 클라이언트가 제시하는 소스 IP 주소를 소유하고 있다는 것을 확립한다는 것입니다. 이 확인이 없으면 NGINX와 같은 QUIC 서버는 스푸핑된 소스 IP 주소로 쉬운 DoS 공격에 취약할 수 있습니다. (이러한 공격을 완화하는 또 다른 QUIC 메커니즘은 모든 초기 연결 패킷을 최소 1200바이트로 패딩해야 한다는 요구 사항으로, 패킷을 보내는 데 더 많은 비용이 드는 작업이 됩니다.)
또한 재시도 패킷 SYN은 클라이언트에 보내는 연결 ID에 연결 세부 정보를 인코딩하여 TCP 플러드 공격(메모리에 저장된 열린 상태이지만 완료되지 않은 핸드셰이크가 엄청나게 많아 서버 리소스가 고갈되는 공격)과 유사한 공격을 완화합니다. 이는 클라이언트가 나중에 제공한 연결 ID와 토큰에서 연결 정보를 재구성할 수 있으므로 서버 측 정보를 보관할 필요가 없다는 추가적인 이점이 있습니다. 이 기술은 TCP SYN쿠키와 유사합니다. 또한 NGINX와 같은 QUIC 서버는 클라이언트의 향후 연결에서 사용할 만료 토큰을 제공하여 연결 재개 속도를 높일 수 있습니다.
연결 ID를 사용하면 연결이 기본 전송 계층과 독립적이 될 수 있으므로 네트워킹의 변경으로 인해 연결이 끊어지지 않아도 됩니다. 이는 Gracefully Managing Client IP Address Changes 에서 설명합니다 .
손실 감지
연결이 설정되고( 아래에서 더 자세히 설명할 것처럼 암호화가 활성화됨 ) HTTP 요청과 응답은 클라이언트와 NGINX 사이에서 앞뒤로 흐를 수 있습니다. UDP 데이터그램이 전송되고 수신됩니다. 그러나 이러한 데이터그램 중 일부가 손실되거나 지연되는 원인이 될 수 있는 여러 요소가 있습니다.
TCP는 패킷 전달을 확인하고, 패킷 손실 또는 지연을 감지하고, 손실된 패킷의 재전송을 관리하고, 적절하게 시퀀싱되고 완전한 데이터를 애플리케이션 계층에 전달하는 복잡한 메커니즘을 가지고 있습니다. UDP는 이러한 기능이 없으므로 혼잡 제어 및 손실 감지가 QUIC 계층에 구현됩니다.
안정적인 전달이 필요한 프레임이 포함된 패킷이 정해진 시간 초과 후에도 확인되지 않으면 해당 패킷은 손실된 것으로 간주됩니다.
시간 초과 기간은 패킷의 내용에 따라 달라집니다. 예를 들어, 암호화를 수립하고 연결을 설정하는 데 필요한 패킷의 시간 초과는 더 짧습니다. 이는 이러한 패킷이 QUIC 핸드셰이크 성능에 필수적이기 때문입니다.
손실 감지에 대한 완전한 설명은 이 프라이머의 범위를 벗어납니다. 시간 초과를 결정하는 메커니즘과 전송 중에 허용되는 승인되지 않은 데이터의 양에 대한 자세한 내용은 RFC 9002 , QUIC 손실 감지 및 혼잡 제어를 참조하세요.
클라이언트 IP 주소 변경을 우아하게 관리하기
클라이언트의 IP 주소(애플리케이션 세션의 맥락에서 소스 IP 주소 라고 함 )는 세션 중에 변경될 수 있습니다. 예를 들어 VPN이나 게이트웨이가 공용 주소를 변경하거나 스마트폰 사용자가 WiFi가 적용되는 위치를 떠나 셀룰러 네트워크로 전환해야 하는 경우입니다. 또한 네트워크 관리자는 전통적으로 TCP 연결보다 UDP 트래픽에 대한 타임아웃을 낮게 설정해 네트워크 주소 변환 (NAT) 리바인딩 가능성이 높아집니다.
QUIC는 발생할 수 있는 중단을 줄이기 위한 두 가지 메커니즘을 제공합니다. 클라이언트는 서버에 주소가 변경될 것임을 사전에 알릴 수 있고, 서버는 클라이언트 주소의 계획되지 않은 변경을 우아하게 처리할 수 있습니다. 연결 ID는 전환 중에도 일관되게 유지되므로, 확인되지 않은 프레임은 새 IP 주소로 다시 전송할 수 있습니다.
QUIC 세션 중에 소스 IP 주소가 변경되면 소스 IP 주소와 포트를 사용하여 특정 UDP 데이터그램을 수신할 업스트림 서버를 결정하는 다운스트림 로드 밸런서(또는 기타 레이어 4 네트워킹 구성 요소)에 문제가 발생할 수 있습니다. 올바른 트래픽 관리를 보장하기 위해 레이어 4 네트워크 장치 공급자는 QUIC 연결 ID를 처리하도록 업데이트해야 합니다. 로드 밸런싱 및 QUIC의 미래에 대해 자세히 알아보려면 IETF 초안 QUIC‑LB: Generating Routable QUIC Connection IDs를 참조하세요 .
암호화
Connection Establishment 에서 초기 QUIC 핸드셰이크가 단순히 연결을 설정하는 것 이상의 역할을 한다는 사실을 언급했습니다. TCP의 TLS 핸드셰이크와 달리 UDP에서는 키와 TLS 1.3 암호화 매개변수의 교환이 초기 연결의 일부로 발생합니다. 이 기능은 여러 교환을 제거하고 클라이언트가 이전 연결을 재개할 때 왕복 시간(0‑RTT)을 0으로 설정합니다.
암호화 핸드셰이크를 연결 설정 프로세스에 접목하는 것 외에도 QUIC는 TCP+TLS보다 더 많은 메타데이터를 암호화합니다. 키 교환이 발생하기 전에도 초기 연결 패킷이 암호화됩니다. 도청자는 여전히 키를 파생시킬 수 있지만 암호화되지 않은 패킷보다 더 많은 노력이 필요합니다. 이렇게 하면 공격자와 잠재적인 국가 수준 검열자 모두에게 중요한 SNI(Server Name Indicator)와 같은 데이터를 더 잘 보호할 수 있습니다. 그림 5는 QUIC가 TCP+TLS보다 잠재적으로 더 민감한 메타데이터(빨간색)를 암호화하는 방법을 보여줍니다.
QUIC 페이로드의 모든 데이터는 TLS 1.3을 사용하여 암호화됩니다. 두 가지 이점이 있습니다. 오래되고 취약한 암호 제품군과 해싱 알고리즘이 허용되지 않으며, 전방 비밀성(FS) 키 교환 메커니즘이 필수입니다. 전방 비밀성은 공격자가 개인 키와 트래픽 사본을 캡처하더라도 공격자가 데이터를 해독하지 못하도록 합니다.
낮음 및 0 RTT 연결로 대기 시간 단축
애플리케이션 데이터를 전송하기 전에 클라이언트와 서버 간에 발생해야 하는 왕복 횟수를 줄이면 애플리케이션 성능이 향상되며, 특히 지연 시간이 긴 네트워크에서 성능이 향상됩니다.
TLS 1.3은 암호화된 연결을 설정하기 위해 단일 왕복을 도입했으며, 연결을 재개하기 위해 왕복을 0번 수행했지만 TCP의 경우 TLS 클라이언트 Hello 전에 핸드셰이크가 발생해야 함을 의미합니다.
QUIC는 암호화 작업과 연결 설정을 결합하기 때문에 클라이언트가 첫 번째 QUIC 패킷에서 요청을 보낼 수 있는 진정한 0‑RTT 연결 재수립을 제공합니다. 이는 첫 번째 요청 전에 연결 설정을 위한 초기 왕복을 제거하여 지연 시간을 줄입니다.
이 경우, 클라이언트는 이전 연결에서 사용된 매개변수로 암호화된 HTTP 요청을 보내고, 주소 검증 목적으로 이전 연결 중에 서버가 제공한 토큰을 포함합니다.
불행히도 0‑RTT 연결 재개는 순방향 비밀성을 제공하지 않으므로 초기 클라이언트 요청은 교환의 다른 트래픽만큼 안전하게 암호화되지 않습니다. 첫 번째 요청을 넘어서는 요청과 응답은 순방향 비밀성으로 보호됩니다. 아마도 더 문제가 될 수 있는 것은 초기 요청이 리플레이 공격 에도 취약하다는 것입니다 . 리플레이 공격은 공격자가 초기 요청을 캡처하여 서버에 여러 번 리플레이할 수 있습니다.
많은 애플리케이션과 웹사이트의 경우, 0‑RTT 연결 재개로 인한 성능 향상이 이러한 잠재적 취약성보다 더 크지만, 이는 사용자가 스스로 결정해야 할 사항입니다.
이 기능은 NGINX에서 기본적으로 비활성화되어 있습니다. 활성화하려면 ssl_early_data지시문을 .으로 설정합니다 on.
Alt-Svc헤더를 사용하여 HTTP/1.1에서 HTTP/3으로 이동
거의 모든 클라이언트(특히 브라우저)는 TCP/TLS를 통해 초기 연결을 합니다. 서버가 QUIC+HTTP/3를 지원하는 경우 헤더 h3에 매개변수를 포함하는 HTTP/1.1 응답을 반환하여 클라이언트에 해당 사실을 알립니다 Alt-Svc. 그런 다음 클라이언트는 QUIC+HTTP/3를 사용할지 아니면 이전 버전의 HTTP를 고수할지 선택합니다. (흥미로운 점은 RFC 7838Alt-Svc 에 정의된 헤더가 QUIC보다 이전이며 다른 용도로도 사용할 수 있다는 것입니다.)
헤더 Alt-Svc는 클라이언트에게 동일한 서비스가 대체 호스트, 프로토콜 또는 포트(또는 이들의 조합)에서 사용 가능하다는 것을 알려줍니다. 또한 클라이언트는 이 서비스가 계속 사용 가능하다고 가정해도 안전한 기간을 알 수 있습니다.
몇 가지 예:
필수는 아니지만 대부분의 경우 서버는 TCP+TLS와 동일한 포트에서 QUIC 연결에 응답하도록 구성됩니다.
헤더를 포함하도록 NGINX를 구성하려면 지시문을 Alt-Svc사용합니다 add_header. 이 예에서 $server_port변수는 NGINX가 클라이언트가 TCP+TLS 요청을 보낸 포트에서 QUIC 연결을 허용하고 86,400은 24시간임을 의미합니다.
add_header Alt-Svc 'h3=":$server_port"; ma=86400';결론
이 블로그에서는 QUIC에 대한 간단한 입문서를 제공하고, QUIC에서 사용되는 주요 네트워킹 및 암호화 작업을 이해하는 데 충분한 개요를 제공합니다.
QUIC + HTTP/3에 대한 NGINX 구성에 대한 보다 포괄적인 내용은 블로그에서 NGINX QUIC+HTTP/3 구현을 위한 바이너리 패키지가 현재 사용 가능하다는 내용을 읽거나, NGINX 및 QUIC+HTTP/3에 대한 실습 웨비나를 시청하세요. QUIC+HTTP/3에 대한 모든 NGINX 지시문에 대한 세부 정보와 사전 빌드된 바이너리를 설치하거나 소스에서 빌드하는 방법에 대한 전체 지침은 NGINX QUIC 웹페이지를 참조하세요 .
위 내용과 같이 NGINX Plus 를 활용하여 Demo 가 필요하시면 하단의 전문가에게 상담받기 버튼을 클릭해주세요
전문가에게 상담받기