현재 도메인 이름 시스템(DNS)을 둘러싼 논쟁이 많이 벌어지고 있으며, 36년 된 이 프로토콜에 대한 엄청난 변경 제안이 나오고 있습니다. ARPANET 에서 유래된 인터넷 이름 서비스는 처음 나온 이래로 이전 버전과의 호환성에 문제가 생긴 적이 한 번도 없습니다. 하지만 DNS 전송 메커니즘을 변경하려는 새로운 제안이 이를 바꿀 수도 있습니다.
이 글에서는 DNS 보안을 위한 두 가지 새로운 기술인 TLS를 통한 DNS(DoT)와 HTTPS를 통한 DNS(DoH)를 살펴보고 NGINX 오픈 소스와 NGINX Plus를 사용하여 이를 구현하는 방법을 보여드리겠습니다.
[ 편집자 - 이 게시물은 NGINX JavaScript 모듈의 사용 사례를 살펴보는 여러 게시물 중 하나입니다. 전체 목록은 NGINX JavaScript 모듈의 사용 사례를 참조하세요.
이 게시물의 코드는 다음을 사용하도록 업데이트되었습니다. js_import
더 이상 사용되지 않는 지시문을 대체하는 지시문 js_포함
지시문에 NGINX 플러스 R23<.htmla> 그리고 나중에. 자세한 내용은 NGINX JavaScript 모듈 의 참조 문서를 참조하세요. 예제 구성 섹션에서는 NGINX 구성 및 JavaScript 파일에 대한 올바른 구문을 보여줍니다.]
DNS는 ARPA(Advanced Research Project Agency)의 초기 인터넷을 위한 명명 서비스를 만드는 두 번째 시도였으며 첫 번째는 1979년 John Postel이 발표한 인터넷 이름 서버 프로토콜이었습니다. IEN-116. DNS는 계층적으로 설계되었으며 호스트 이름을 여러 영역으로 분산시키고 여러 개별 기관에서 관리할 수 있는 구조를 제공합니다. DNS에 대한 첫 번째 RFC는 1983년에 게시되었습니다(RFC882 그리고883 ), 그리고 수년에 걸쳐 여러 번 확장되었지만, 당시 정의된 표준에 따라 작성된 클라이언트는 오늘날에도 계속 작동할 것입니다.
그렇다면 왜 지금 프로토콜을 변경해야 할까요? 의도한 대로 분명히 작동하며, DNS 패킷에 버전 번호를 포함하지 않은 작성자의 자신감이 입증되었습니다. 이런 주장을 할 수 있는 다른 프로토콜이 많이 생각나지 않습니다. DNS는 대부분 프로토콜이 일반 텍스트이고 종종 7비트 ASCII 였던 아주 순진한 시절에 고안되었지만, 오늘날의 인터넷은 1980년대의 ARPANET보다 훨씬 무서운 곳입니다. 오늘날 대부분의 프로토콜은 암호화 및 검증 목적으로 TLS(전송 계층 보안)를 채택했습니다. DNS 비판론자들은 추가적인 보안 보호 기능이 이미 필요하다고 주장한다.
따라서 DNS가 인터넷에 대한 이름 서비스 프로토콜을 제공하려는 두 번째 시도였던 것과 같은 방식으로 TLS(DoT)를 통한 DNS와 HTTPS(DoH)를 통한 DNS가 DNS 프로토콜을 보안하려는 두 번째 시도로 등장하고 있습니다. 첫 번째 시도는 DNSSEC 으로 알려진 확장이었으며, 대부분의 최상위 도메인(TLD)이 DNSSEC을 사용하지만 DNS 패킷에 포함된 데이터를 암호화하도록 의도된 것은 아닙니다. 데이터가 변조되지 않았음을 확인할 뿐입니다. DoT와 DoH는 DNS를 TLS 터널 내부로 래핑하는 프로토콜 확장이며, 이를 채택하면 36년간의 하위 호환성이 종료됩니다.
저는 DoT가 대체로 합리적인 확장책으로 여겨진다고 생각합니다. 이는 이미 IANA(인터넷 할당 번호 기관)에서 자체 포트 번호(TCP/853)를 할당받았으며, 단순히 TCP DNS 패킷을 TLS로 암호화된 터널로 래핑합니다. 많은 프로토콜이 이전에 이 작업을 수행했습니다. HTTPS는 TLS 터널 내부의 HTTP이고, SMTPS, IMAPS, LDAPS는 해당 프로토콜의 보안 버전입니다. DNS는 항상 UDP(또는 특정 경우에는 TCP)를 전송 프로토콜로 사용해 왔으므로 TLS 래퍼를 추가하는 것은 큰 변경 사항이 아닙니다.
반면 DoH는 좀 더 논란의 여지가 많습니다. DoH는 DNS 패킷을 가져와 HTTP GET
또는 POST
요청 안에 넣은 다음, 이 패킷을 HTTPS 연결을 통해 HTTP/2 이상을 사용하여 전송합니다. 이는 다른 HTTPS 연결과 마찬가지인 것처럼 보이며, 기업이나 서비스 제공자가 어떤 요청이 이루어지고 있는지 확인할 수 없습니다. 모질라와 다른 지지자들은 이 관행이 사용자가 방문하는 사이트를 엿보는 눈으로부터 비공개로 유지하여 사용자의 개인 정보 보호를 향상시킨다고 주장합니다.
하지만 사실은 그렇지 않습니다. DoH에 대한 비판론자들은 DoH가 DNS의 Tor 와는 약간 다르다고 지적합니다. 왜냐하면 브라우저가 DoH를 사용하여 찾은 호스트에 연결을 할 때, 요청은 거의 확실히 TLS 서버 이름 표시 (SNI) 확장을 사용하고, 여기에는 호스트 이름이 포함되며 일반 텍스트로 전송되기 때문입니다. 또한 브라우저가 OSCP(온라인 인증서 상태 프로토콜)를 사용하여 서버의 인증서를 검증하려고 시도하는 경우 해당 프로세스도 일반 텍스트로 수행될 가능성이 높습니다. 따라서 DNS 조회를 모니터링할 수 있는 사람은 연결의 SNI나 OCSP 검증의 인증서 이름을 읽을 수도 있습니다.
많은 사람들에게 DoH의 가장 큰 문제는 브라우저 공급업체가 기본적으로 사용자가 만든 DoH 요청을 보낼 DNS 서버를 선택한다는 것입니다(예를 들어 미국에 있는 Firefox 사용자의 경우 DNS 서버는 Cloudflare 에 속합니다). DNS 서버 운영자는 사용자의 IP 주소와 사용자가 요청을 보낸 사이트의 도메인 이름을 볼 수 있습니다. 그다지 중요한 것은 아니지만 일리노이 대학의 연구자들은 웹 페이지 요소에 대한 요청의 목적지 주소만 가지고 도 사람들이 어떤 웹사이트를 방문했는지 유추 할 수 있다는 것을 발견했습니다. 이를 페이지 로드 지문이라고 합니다. 그런 다음 해당 정보를 사용하여 "광고를 위한 사용자 프로파일링 및 타겟팅"이 가능합니다.
DoT와 DoH가 본질적으로 나쁜 것은 아니며, 사용자 개인 정보 보호를 강화하는 사용 사례도 있습니다. 그러나 공개적이고 중앙화된 DoH 서비스는 사용자 개인정보 보호에 좋지 않다는 의견이 점차 커지고 있으므로, 어떤 경우에도 이러한 서비스 사용을 피하는 것이 좋습니다.
어떤 경우든, 귀하의 사이트와 앱에 대해 일부는 공개, 일부는 비공개, 일부는 분할된 수평선을 갖는 자체 DNS 영역을 관리할 가능성이 높습니다. 어느 시점에서는 DoT 또는 DoH 서비스를 직접 운영하고 싶다고 결정할 수도 있습니다. 여기서 NGINX가 도움을 줄 수 있습니다.
DoT가 제공하는 개인 정보 보호 강화 기능은 DNS 보안에 큰 이점을 제공하지만 현재 DNS 서버가 DoT를 지원하지 않는다면 어떻게 될까요? NGINX는 DoT와 표준 DNS 간의 게이트웨이를 제공하여 도움을 줄 수 있습니다.
아니면 DoT 포트가 차단된 경우 DoH의 방화벽 차단 잠재력을 좋아할 수도 있습니다. NGINX가 DoH-to-DoT/DNS 게이트웨이를 제공하여 도움을 줄 수 있습니다.
NGINX Stream(TCP/UDP) 모듈은 SSL 종료를 지원하므로 실제로 DoT 서비스를 설정하는 것은 정말 간단합니다. NGINX 구성 몇 줄만으로 간단한 DoT 게이트웨이를 만들 수 있습니다.
DNS 서버에는 업스트림
블록이 필요하고 TLS 종료를 위해서는 서버
블록이 필요합니다.
물론, 반대 방향으로 가서 들어오는 DNS 요청을 상위 DoT 서버로 전달할 수도 있습니다. 하지만 이 방법은 대부분 DNS 트래픽이 UDP이고 NGINX는 DoT와 TCP 기반 DNS와 같은 다른 TCP 서비스 간에만 변환할 수 있기 때문에 그다지 유용하지 않습니다.
DoT 게이트웨이에 비해 간단한 DoH 게이트웨이의 구성은 조금 더 복잡합니다. HTTPS 서비스와 스트림 서비스가 모두 필요하며, JavaScript 코드와 NGINX JavaScript 모듈<.htmla> (njs)을 사용하여 두 프로토콜 간을 변환합니다. 가장 간단한 구성은 다음과 같습니다.
이 구성은 패킷을 DNS 서비스로 보내는 데 필요한 최소한의 처리만 수행합니다. 이 사용 사례에서는 업스트림 DNS 서버가 다른 필터링, 로깅 또는 보안 기능을 수행한다고 가정합니다.
이 구성에 사용된 JavaScript 스크립트( nginx_stream.js )에는 다양한 DNS 라이브러리 모듈 파일이 포함되어 있습니다. dns.js 모듈 내부에서 dns_decode_level
변수는 DNS 패킷에서 얼마나 많은 처리가 수행되는지 설정합니다. DNS 패킷을 처리하면 성능이 확실히 저하됩니다. 위와 같은 구성을 사용하는 경우 dns_decode_level을
다음과 같이 설정하세요.0
.
NGINX에서는 HTTP에 상당히 능숙하기 때문에 NGINX를 단순한 DoH 게이트웨이로만 사용하는 것은 기회 낭비라고 생각합니다.
여기에 사용된 JavaScript 코드는 DNS 패킷의 전체 또는 부분 디코딩을 수행하도록 설정할 수 있습니다. 이를 통해 DNS 응답의 최소 TTL을 기반으로 Expires 및 Cache-Control
헤더가 설정된 DoH 쿼리에 대한 HTTP 콘텐츠 캐시를 구축할 수 있습니다.
추가 연결 최적화와 콘텐츠 캐싱 및 로깅 지원이 포함된 보다 완전한 예는 다음과 같습니다.
NGINX Plus 구독이 있는 경우 위의 예를 활성 상태 검사 및 고가용성과 같은 고급 NGINX Plus 기능과 결합하거나 캐시된 DoH 응답을 관리하기 위해 캐시 제거 API를 사용할 수도 있습니다.
NGINX Plus 키-값 저장소를 사용하면 사용자를 악성 도메인으로부터 보호하는 DNS 필터링 시스템을 구축할 수도 있습니다. 이 시스템은 사용자에 대한 액세스를 효과적으로 차단하는 DNS 응답을 반환합니다. RESTful NGINX Plus API를 사용하면 키-값 저장소의 내용을 동적으로 관리할 수 있습니다.
우리는 악성 도메인을 "차단됨"과 "블랙홀"의 두 가지 범주로 정의합니다. DNS 필터링 시스템은 도메인의 카테고리에 따라 DNS 쿼리를 다르게 처리합니다.
NXDOMAIN
응답(도메인이 존재하지 않음을 나타냄)을 반환합니다.A
레코드 요청에 대한 응답으로 0.0.0.0의 단일 DNS A
레코드를 반환하거나 AAAA
(IPv6) 레코드 요청에 대한 응답으로 ::의 단일 AAAA
레코드를 반환합니다. 다른 레코드 유형의 경우 답변이 0개인 응답을 반환합니다.DNS 필터가 요청을 받으면 먼저 쿼리된 FQDN과 정확히 일치하는 키가 있는지 확인합니다. 하나를 찾으면 연관된 값에 의해 지정된 대로 요청을 "스크럽"(차단 또는 블랙홀)합니다. 정확한 일치 항목이 없으면 차단된 도메인 목록과 블랙홀된 도메인 목록의 두 목록에서 도메인 이름을 검색하여 일치 항목이 있으면 적절한 방식으로 요청을 삭제합니다.
쿼리된 도메인이 악성이 아닌 경우 시스템은 정기적인 처리를 위해 해당 도메인을 Google DNS 서버로 전달합니다.
우리는 NGINX Plus 키-값 저장소의 두 가지 항목으로 악성으로 간주되는 도메인을 정의합니다.
차단됨
또는 블랙홀
값을 갖는 정규화된 도메인 이름(FQDN)에 대한 개별 항목blocks_domains
및 blackholed_domains
라는 키가 있는 두 개의 도메인 목록은 각각 CSV(쉼표로 구분된 값) 형식의 도메인 목록에 매핑됩니다. 키‑값 저장소에 대한 다음 구성은 스트림
컨텍스트에 적용됩니다. keyval_zone
지시문은 dns_config 라는 키-값 저장소에 대한 메모리 블록을 할당합니다. 첫 번째 keyval
지시문은 설정된 일치하는 FQDN 키-값 쌍을 로드하는 반면 두 번째와 세 번째 지시문은 두 개의 도메인 목록을 정의합니다.
그런 다음 NGINX Plus API를 사용하여 명시적으로 스크러빙하려는 FQDN 키에 차단됨
또는 블랙홀
값을 할당하거나 차단됨_도메인
또는 블랙홀_도메인
키와 관련된 CSV 형식의 목록을 수정할 수 있습니다. 언제든지 정확한 FQDN을 수정하거나 제거하거나, 두 목록에 있는 도메인 집합을 수정할 수 있으며 DNS 필터는 변경 사항에 따라 즉시 업데이트됩니다.
다음 구성은 $dns_response
변수를 로드합니다. 이 변수는 아래 DNS 쿼리를 수신하는 서버 구성 에서 정의된 server
블록의 js_preread
지시문에 의해 채워집니다. 호출된 JavaScript 코드가 요청을 삭제해야 한다고 판단하면 해당 변수를 차단
또는 블랙홀
로 적절히 설정합니다.
$dns_response
변수의 값을 $upstream_pool
변수에 할당하기 위해 map
지시문을 사용하여 아래의 업스트림 서버 정의 에서 어떤 업스트림 그룹이 요청을 처리할지 제어합니다. 악성이 아닌 도메인에 대한 쿼리는 기본 Google DNS 서버로 전달되고, 차단되거나 블랙홀된 도메인에 대한 쿼리는 해당 범주의 업스트림 서버에서 처리됩니다.
이 구성은 차단된
도메인, 블랙홀 도메인
및 비악성 도메인에 대한 요청을 각각 처리하는 차단된 도메인, 블랙홀 도메인 및 google
도메인의 업스트림 서버를 정의합니다.
여기서는 들어오는 DNS 요청을 수신하는 스트림
컨텍스트에서 서버를 정의합니다. js_preread
지시어는 DNS 패킷을 디코딩하는 Javascript 코드를 호출하고, 패킷의 NAME
필드에서 도메인 이름을 검색하고, 키-값 저장소에서 도메인을 조회합니다. 도메인 이름이 FQDN 키와 일치하거나 blocks_domains
또는 blackhole_domains
목록에 있는 도메인 중 하나의 영역 내에 있는 경우 해당 도메인은 삭제됩니다. 그렇지 않으면 확인을 위해 Google DNS 서버로 전송됩니다.
이제 거의 다 됐습니다. 쿼리에 블랙홀 또는 차단된 응답으로 응답하는 최종 서버
블록만 추가하면 됩니다.
이 블록의 서버는 바로 위에 있는 실제 DNS 서버와 거의 동일하며, 주요 차이점은 이러한 서버가 요청을 상위 DNS 서버 그룹에 전달하는 대신 클라이언트에 응답 패킷을 보낸다는 것입니다. JavaScript 코드는 포트 9953 또는 9853에서 실행되는지 감지하고 패킷을 차단해야 함을 나타내는 플래그를 설정하는 대신 $dns_response를
실제 응답 패킷으로 채웁니다. 그게 전부입니다.
물론 DoT 및 DoH 서비스에도 이 필터링을 적용할 수 있지만, 단순화를 위해 표준 DNS를 사용했습니다. DNS 필터링을 DoH 게이트웨이와 병합하는 것은 독자의 연습으로 남겨둡니다.
이전에 NGINX Plus API를 사용하여 두 개의 FQDN에 대한 항목을 추가하고 두 도메인 목록에 일부 도메인을 추가했습니다.
$ curl -s http://localhost:8080/api/5/stream/keyvals/dns_config | jq { "www.some.bad.host": "차단됨", "www.some.other.host": "블랙홀", "차단된 도메인": "bar.com,baz.com", "블랙홀 도메인": "foo.com,nginx.com" }
따라서 www.foo.com (블랙홀된 foo.com 도메인 내의 호스트)의 확인을 요청하면 IP 주소가 0.0.0.0인 A
레코드를 얻습니다.
$ dig @localhost www.foo.com ; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> @localhost www.foo.com ; (1개 서버 발견) ;; 전역 옵션: +mcd ;; 답변을 받았습니다: ;; ->>HEADER,,- 명령어: 쿼리, 상태: 오류, id: 58558 ;; 플래그: qr aa rd ad; 쿼리: 1, 정답: 1, 권한: 0, 추가: 0 ;; 경고: 재귀를 요청했지만 사용할 수 없습니다 ;; 질문 섹션: ; www.foo.com. 정답란: www.foo.com. 300 IN A 0.0.0.0 ;; 쿼리 시간: 0 밀리초 ;; 서버: 172.0.0.1#53(126.0.0.1) ;; 언제: 월요일 12월 2일 14:31:35 UTC 2019 ;; MSG 크기 수신: 45
DOH 및 DOT 파일은 내 GitHub 저장소 에서 사용할 수 있습니다.
NGINX Plus를 사용해보려면 오늘 30일 무료 체험판을 시작하거나 저희에게 연락해 사용 사례에 대해 논의해 보세요 .
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."