В статье описываются недостатки существующей структуры 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.
Схематически это выглядит так:
Приведенное выше описание достаточно примитивно и нелепо. Оно написано с целью объяснить принцип работы «на пальцах». Вероятно, оно нисколько не упростит понимание, а только еще больше запутает.
Практика внедрения DNSSEC
Внимание! Данная инструкция устарела. Подписание зоны без использования NSEC3 позволяет обнаружить все DNS записи зоны.
Актуальная инструкция www.digitalocean.com/community/tutorials/how-to-setup-dnssec-on-an-authoritative-bind-dns-server--2
На текущий момент подписаны только некоторые доменные зоны верхнего уровня, в частности: .net, .com, .org.
Зона .ru до сих пор не подписана, зона .ua подписана в режиме тестирования, зона .su официально подписана, но до сих пор ни один регистратор не поддерживает добавление DS-записей.
Полный список можно посмотреть на сайте ICANN
Для подписания своей зоны эта возможность должна поддерживаться регистратором домена. На текущий момент мне неизвестны отечественные регистраторы поддерживающие DNSSEC. Регистратор R01 и сайт dnssec.ru, которые рекламирует делегирование доменов .ru с поддержкой DNSSEC абсурдны, потому что сама зона .ru не подписана и, в таком случае, отправной точкой для построения цепочки доверия становится сервер R01.
Из поддерживающих можно выделить наиболее крупных:
- Godaddy.com
- Dyn.com
- 101domain.com
- GKG.net
UPD: Теперь nic.ru тоже поддерживает DNSSEC www.nic.ru/news/2012/dnssec.html
Неполный список можно посмотреть здесь.
Кроме регистратора, необходимы 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:
Необходимо переключиться в «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-записи не предусматривает копирование строк, а требует заполнения всех полей отдельно:
Из-за того, что у 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.
UPD: С 19.03.2013 Google Public DNS производит верификацию подписей DNSSEC, и в случае невалидной подписи не резолвит домен googleonlinesecurity.blogspot.com/2013/03/google-public-dns-now-supports-dnssec.html
Наиболее простой вариант — плагин для Firefox и Chrome.
Плагин позволяет производить резолв в обход системных DNS, имеет свои предустановленные серверы с поддержкой валидации DNSSEC. По умолчанию плагин использует системные DNS, изменить это можно в настройках плагина: выбрать CZ.NIC's или 217.31.57.6
Для того, чтобы научить утилиту dig проверять подпись, необходимо создать отправную точку в цепочке доверия, создав файл с ключом корневой зоны:
dig +nocomments +nostats +nocmd +noquestion -t dnskey . > /etc/trusted-key.key
После необходимо в файле /etc/trusted-key.key удалить строку:
;; Truncated, retrying in TCP mode.
Если этого не сделать dig вернет:
No trusted keys present
Теперь можно проверять подлинность подписей с помощью dig:
dig +sigchase @217.31.57.6 whitehouse.gov
Как настроить рекурсивный резолвер с функцией проверки подписей можно прочитать здесь
Практическая польза
Не смотря на то, что стандарт DNSSEC до сих пор находится в разработке, уже возможно извлекать из него пользу.
Публичный SSH-ключ
При первом подключении к серверу SSH клиент просит самостоятельно проверить отпечаток публичного ключа сервера и ввести yes, после чего публичный ключ сервера сохраняется в файле known_hosts.
С появлением DNSSEC публичный ключ может быть помещен в DNS-запись типа SSHFP и, при первом подключении к серверу, проверяться автоматически без запроса. Для активирования этой функции нужно добавить опцию VerifyHostKeyDNS=yes в конфиг SSH-клиента, также необходимо чтобы системный резолвер поддерживал верификацию DNSSEC.
Самоподписанный SSL-сертификат (HTTPS)
UPD: Описанное ниже уже не актуально, после того как стандарт хранения ssl-ключей в DNS был опубликован, и получил название DANE ru.wikipedia.org/wiki/DANE Google убрал поддержку DANE из Chrome. Дисскуссия по этому поводу с разработчиком Chrome github.com/agl/dnssec-tls-tools/issues/4
С помощью 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/
Из-за того, что вся цепочка DNSSEC помещена в сертификат, браузеру нет необходимости производить полную проверку цепочки, так что сертификат будет «валидным» даже в случае если системный резолвер не поддерживает проверку DNSSEC.
P.S. В статье не рассмотрены сроки действия ключей DNSSEC, не рассказано об альтернативной цепочке доверия DLV (DNSSEC Look-aside Validation). Я буду признателен тем, кто разберется в этих вопросах и опишет их.
P.P.S. Мне известно негативное влияние подобных пошаговых HOWTO, которые приводят к бездумному копированию команд без понимания сути. Но, из-за того, что информации по данному вопросу мало и местами она противоречива, я надеюсь, кому-то эта статья поможет избежать путаницы с которой пришлось столкнуться мне. Спасибо Александру Венедюхину за консультации и его статьи.