Информационная безопасность / Практическое применение DNSSEC

в 16:18, , рубрики: BIND, DNS, DNSSEC, HTTPS, openssl, rsa, TLS, X.509, метки: , , , , , , ,

image

В статье описываются недостатки существующей структуры DNS, полный процесс внедрения DNSSEC на примере доменов .com и .org, процедура создания валидного самоподписанного ssl-сертификата подписанного с помощью DNSSEC.

Чем плох DNS

Система DNS в нынешнем виде была разработана более 20 лет назад, когда о защите информации не особенно задумывались. Она имеет несколько фундаментальных уязвимостей.

Достоверность ответа DNS-сервера никак не проверяется Это позволяет отправить пользователя, обратившегося к доменному имени, на произвольный IP-адрес, подменив ответ сервера. На практике подобная атака может выглядеть так.

Также уязвимы кеширующие DNS-серверы провайдеров, выступающие как резолверы для клиентов: Атака Каминского.

Сегодня существуют технологии, предусматривающие хранение открытых ключей в DNS-записях, например, DKIM-подписи в электронной почте, SSH-ключей в записях SSHFP и т.д. Все эти технологии требуют защиты от подделки DNS.

Теория DNSSEC

DNSSEC — технология, позволяющая однозначно удостовериться в подлинности DNS информации при помощи криптографической подписи.

Популярно о DNSSEC можно прочитать здесь: dxdt.ru/2009/03/04/2163/

Более подробно здесь: habrahabr.ru/blogs/sysadm/120620/

И на сайте Verisign.com

Перед продолжением настоятельно рекомендуется внимательно прочитать вышеприведенные ссылки, так как на первый взгляд процедура подписания зоны кажется довольно запутанной.

Максимально упрощенно это выглядит примерно так: есть корневая зона ".", которая содержит в себе информацию о всех доменах первого уровня. Условно говоря, это текстовый файл с некоторым множеством строк, который изменяется не очень часто. Создается пара открытый/закрытый ключ и каждая строка в этом файле подписывается (по типу «clear sign» в PGP/GPG, которая используется в электронной почте для открытого подписания текста и начинается с «BEGIN PGP SIGNATURE»).
Теперь, имея открытый ключ от этой пары, можно удостовериться в подлинности каждой записи в этом списке. Например, проверить, что за зону «ru.» действительно отвечают сервера ripn.net:

dig -t any +dnssec @k.root-servers.net ru.

В ответе можно увидеть запись RRSIG содержащую хеш-подпись.

Но этого недостаточно, так как в резолве участвуют нижестоящие сервера, ответы которых тоже нужно верифицировать. Тогда владельцы домена верхнего уровня, например «com.», создают такую же пару ключей и подписывают все записи в своей зоне, и после этого добавляют слепок своего открытого ключа в корневую зону. В результате доверяя открытому ключу корневой зоны можно проверить подлинность ключа зоны «com.» и, соответственно, доверять ему:

dig -t any +dnssec @k.root-servers.net com.

В ответе запись DS содержит слепок ключа, которым подписывается зона «com.»

Важно понимать, что после каждого изменения в зоне подписание происходит заново. Но, так как корневая зона подписывает только открытый ключ зоны «com.», то нет необходимости перехешировать записи в корневой зоне при каждом изменении в зоне «com.»

Теперь можно установить подлинность ответов от серверов, ответственных за домен «com.»:

dig +dnssec -t any @a.gtld-servers.net verisign.com.

Видно, что запись о домене verisign.com. подписана, но на этом этапе возможно установить только подлинность адресов NS-серверов, ответственных за домен verisign.com. Для резолва IP-aдреса необходимо получить от них, поэтому владельцы этих NS-серверов имеют свою пару ключей, которыми подписывают зону и помещают слепок открытого ключа в DS-запись.

Запрашиваем A-запись для домена verisign.com.:

dig +dnssec -t a verisign.com @a2.nstld.com

В результате, для проверки подлинности факта, что А-запись verisign.com содержит значение 192.5.6.31 выстраивается такая цепочка доверия:
нам заранее известен открытый ключ корневой зоны "." и мы ему доверяем. В корневой зоне существует DS-запись о том, что все записи зоны «com.» подписаны указанным в ней ключом и сама запись, соответственно, подписана ключом корневой зоны. Проверив подлинность этой записи, мы доверяем всем записям в зоне «com.», подписанным этим ключом. На серверах, ответственных за зону «com.» содержится DS-запись с открытым ключом verisign.com, подписанная ключом зоны «com.», что позволяет проверить подлинность подписи в ответе NS-сервера, ответственного за verisign.com.

Схематически это выглядит так:image

Приведенное выше описание достаточно примитивно и нелепо. Оно написано с целью объяснить принцип работы «на пальцах». Вероятно, оно нисколько не упростит понимание, а только еще больше запутает.

Практика внедрения DNSSEC

На текущий момент подписаны только некоторые доменные зоны верхнего уровня, в частности: .net, .com, .org.
Зона .ru до сих пор не подписана, зона .ua подписана в режиме тестирования, зона .su официально подписана, но до сих пор ни один регистратор не поддерживает добавление DS-записей.
Полный список можно посмотреть на сайте ICANN

Для подписания своей зоны эта возможность должна поддерживаться регистратором домена. На текущий момент мне неизвестны отечественные регистраторы поддерживающие DNSSEC. Регистратор R01 и сайт dnssec.ru, которые рекламирует делегирование доменов .ru с поддержкой DNSSEC абсурдны, потому что сама зона .ru не подписана и, в таком случае, отправной точкой для построения цепочки доверия становится сервер R01.

Из поддерживающих можно выделить наиболее крупных:

  • Godaddy.com
  • Dyn.com
  • 101domain.com
  • GKG.net

Неполный список можно посмотреть здесь.
Кроме регистратора, необходимы NS-сервера с поддержкой DNSSEC. Некоторые регистраторы предоставляют такие услуги. Самый дешевый вариант у Godaddy называется Premium DNS 35$/год. Самый дорогой у dyn.com называется DynECT Lite 30$/месяц. В этой статье будет показан пример настройки собственного master DNS-сервера на базе BIND 9.7.3.

Далее предполагается, что у нас уже есть домен, делегированный на собственные, полностью настроенные DNS, и готовый файл зоны.
Для включения поддержки DNSSEC в named.conf в секцию options нужно добавить:

  options {         ...        dnssec-enable yes;         ...  };

Инструменты для генерации ключей и подписания зон входят в пакет BIND последних версий.
На этом этапе предполагается, что читателю уже известно что такое ZSK (Zone Sign Key) и KSK (Key Sign Key).
Все приведенные ниже операции необходимо проводить в отдельно созданной папке.

Генерация ключа ZSK:

dnssec-keygen -a RSASHA1 -b 1024 -n ZONE my-domain.com

Генерация ключа KSK:

dnssec-keygen -a RSASHA1 -b 2048 -f KSK -n ZONE my-domain.com

где my-domain.com — домен для которого генерируются ключи. В результате выполнения этих команд будут созданы две пары ключей.
Далее необходимо скопировать файл зоны в текущую папку и выполнить подписание:

dnssec-signzone -S -N INCREMENT my-domain.com

где my-domain.com — текстовый файл зоны. Важно выполнять команду, находясь в одной папке с ключами и файлом зоны; имя файла указывать без пути.
В результате будут созданы два файла:

my-domain.com.signed — подписанный файл зоны
dsset-my-domain.com — файл содержащий две DS-записи

Исходный файл зоны останется без изменений. Далее в конфиге BIND необходимо заменить файл на подписанный:

zone "my-domain.com" {          type master;          file "my-domain.com.signed";  	allow-query { any; };          allow-transfer { ....; };  };

Развернутые примеры файлов зон можно посмотреть на nox.su.
Чтобы повысить отказоустойчивость своих DNS, рекомендуется использоваться secondary сервера. Cуществует несколько бесплатных сервисов предлагающих slave-сервера с поддержкой DNSSEC. Вот неполный список http://www.frankb.us/dns/. Я использую rollernet.us, поэтому разрешаю трансфер с адресов 208.79.240.3 и 208.79.241.3. При использовании secondary-серверов, записи о них должны присутствовать в файле зоны еще до подписания. Я рекомендую активировать трансфер уже после того как на master-сервере будет находиться подписанная зона.

Далее предполагается, что подписанная зона уже размещена на авторитарном NS-сервере и доступна снаружи:

dig +dnssec -t any @super.vip.my.dns.com my-domain.com

Команда должна вернуть подписанную зону.

На этом этапе можно активировать secondary сервера и синхронизировать зону по AXFR.

Далее необходимо добавить DS-записи в панели регистратора домена. Они были сгенерированы при выполнении dnssec-signzone и находятся в файле dsset-my-domain в таком виде:

  my-domain.com.  IN DS 40513 5 1 6198D29A9FB9797719CDFD2316986BDFF5C29323  my-domain.com.  IN DS 40513 5 2 1AAB29EC7B67013F45865AEB06D93899B45C598D65A4E4D5522BC39E B5B9212F

Так выглядит форма добавления DS-записей в панели GoDaddy:

image

Необходимо переключиться в «Advanced mode» и скопировать обе строки, предварительно отредактировав. Нужно добавить значение TTL и удалить пробел во второй строке в отпечатке ключа, иначе форма вернет ошибку. В результате копируемые строки должны выглядеть так:

  my-domain.com. 86400 IN DS 40513 5 1 6198D29A9FB9797719CDFD2316986BDFF5C29323  my-domain.com. 86400 IN DS 40513 5 2 1AAB29EC7B67013F45865AEB06D93899B45C598D65A4E4D5522BC39EB5B9212F

Записи будут добавлены только в случае если зона на master-сервере доступна и подписана правильно.

Значения полей в DS-записи:

86400 — TTL данной записи
40513 — Key Tag
5 — Algorithm
1/2 — Digest Type

В приведенном выше примере при генерации ключей был использован алгоритм RSA-SHA1, поэтому запись имеет номер пять.
Таблица номеров алгоритмов:

Number Algorithm
1 RSAMD5
2 DH
3 DSA/SHA1
4 ECC
5 RSA/SHA-1
6 DSA-NSEC3-SHA1
7 RSASHA1-NSEC3-SHA1
8 RSA/SHA-256
9
10 RSA/SHA-512
11
12 ГОСТ Р 34.10-2001

Digest Type в приведенном примере у первой записи равен 1, во второй 2.
Таблица номеров Digest Type:

Number Digest Type
1 SHA-1
2 SHA-256
3 SHA-512

У некоторых регистраторов, например Dyn.com, форма добавления DS-записи не предусматривает копирование строк, а требует заполнения всех полей отдельно:
image

Из-за того, что у Dyn.com список алгоритмов расположен не по порядку и не отмечен номерами, это вызывает некоторую путаницу. При добавлении через эту форму так же нужно удалить пробел в отпечатке второго ключа.

После добавление DS-записей можно проверить их появление на серверах, ответственных за домен верхнего уровня. Для домена «com.» это выглядит так:

dig +dnssec -t DS @a.gtld-servers.net my-domain.com

В момент, когда это произойдет, можно проверить правильность подписания зоны с помощью DNSSEC Debugger от Verisign и визуализатора цепочки подписания.
Напомню, что после каждого изменения в записях зоны, необходимо выполнять подписание повторно. DS-записи при этом обновлять не нужно.
Если всё правильно, можно переходить к настройке клиентской части.

Настройка клиентского резолвера

Для проверки подписей на стороне клиента, эту функцию должен поддерживать системный DNS, через который происходит резолв адресов. Публичный DNS от Google 8.8.8.8 поддерживает передачу DNSSEC записей, но не производит их верификацию. Подробнее написано в FAQ.

Наиболее простой вариант — плагин для Firefox и Chrome.

image

Плагин позволяет производить резолв в обход системных DNS, имеет свои предустановленные сервера с поддержкой валидации DNSSEC. По умолчанию плагин использует системные DNS, изменить это можно в настройках плагина: выбрать CZ.NIC's или 217.31.57.6

Для того, чтобы научить утилиту dig проверять подпись, необходимо создать отправную точку в цепочке доверия, создав файл с ключом корневой зоны:

dig +nocomments +nostats +nocmd +noquestion -t dnskey . > trusted-key.key

Теперь можно проверять подлинность подписей с помощью dig:

dig +sigchase @217.31.57.6 whitehouse.gov

Как настроить рекурсивный резолвер с функцией проверки подписей можно прочитать здесь

Практическая польза

Не смотря на то, что стандарт DNSSEC до сих пор находится в разработке, уже возможно применять извлекать из него пользу.

Публичный SSH-ключ

При первом подключении к серверу SSH клиент просит самостоятельно проверить отпечаток публичного ключа сервера и ввести yes, после чего публичный ключ сервера сохраняется в файле known_hosts.
С появлением DNSSEC публичный ключ может быть помещен в DNS-запись типа SSHFP и, при первом подключении к серверу, проверяться автоматически без запроса. Для активирования этой функции нужно добавить опцию VerifyHostKeyDNS=yes в конфиг SSH-клиента, также необходимо чтобы системный резолвер поддерживал верификацию DNSSEC.

Самоподписанный SSL-сертификат (HTTPS)

С помощью DNSSEC можно самостоятельно подписать SSL-сертификат который будет «валидным» в браузере.
Эта экспериментальная функция на данный момент находится в активной разробтке и пока поддерживается только браузером Google Chrome/Chromium.
Черновой вариант стандарта: tools.ietf.org/html/draft-agl-dane-serializechain-01

Технология разрабатывается сотрудником Google по имени Адам Лэнгли (Adam Langley), он ведет очень интересный блог http://www.imperialviolet.org/.
Пост об этой технологии.

Далее предполагается, что домен для которого генерируется сертификат, может быть подписан DNSSEC.

Скачиваем dnssec-tls-tools:

git clone  git://github.com/agl/dnssec-tls-tools.git

И компилируем:

gcc -o gencert gencert.c -Wall -lcrypto

Генерация RSA-ключей:

openssl genrsa 1024 > privkey.pem  openssl rsa -pubout -in privkey.pem > pubkey.pem

Создание отпечатка ключа:

python ./gencaa.py pubkey.pem

где gencaa.py файл из пакета dnssec-tls-tools.
Команда вернет строку вида:

EXAMPLE.COM. 60 IN TYPE257 # 70 020461757468303e3039060a2b06010401d6790203010…

Это DNS-запись, которую необходимо добавить в свой файл зоны, заменив EXAMPLE.COM. своим значением. Если зона еще не подписана, это нужно сделать. Если запись добавляется к уже подписанной зоне, нужно, соответственно, выполнить подписание заново.

Проверяем правильность ключа в DNS:

dig +dnssec +sigchase -t type27 example.com

Команда должна вернуть DNSSEC validation is ok: SUCCESS

После того как запись type27 доступна и подписана, можно сгенерировать цепочку доверия DNSSEC:

 python ./chain.py example.com chain

И сам сертификат:

./gencert privkey.pem chain > cert.pem

Подключение сертификата в Nginx выглядит так:

  server {          ...  	ssl			on;  	ssl_certificate cert.pem;  	ssl_certificate_key privkey.pem;          ...   }

Из-за того, что цепочка подписания DNSSEC может изменяться, создание цепочки и генерацию сертификата (последние две команды) необходимо добавить в крон и выполнять, например, раз в сутки.

Результат должен выглядеть так: https://dnssec.imperialviolet.org
или так: https://dnssec.zhovner.com

Из-за того, что вся цепочка DNSSEC помещена в сертификат, браузеру нет необходимости производить полную проверку цепочки, так что сертификат будет «валидным» даже в случае если системный резолвер не поддерживает проверку DNSSEC.


P.S. В статье не рассмотрены сроки действия сертификатов DNSSEC, не рассказано об альтернативной цепочке доверия DLV (DNSSEC Look-aside Validation). Я буду признателен тем, кто разберется в этих вопросах и опишет их.

P.P.S. Мне известно негативное влияние подобных пошаговых HOWTO, которые приводят к бездумному копированию команд без понимания сути. Но, из-за того, что информации по данному вопросу мало и местами она противоречива, я надеюсь, кому-то эта статья поможет избежать путаницы с которой пришлось столкнуться мне. Спасибо Александру Венедюхину за консультации и его статьи.

Автор: zhovner

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js