Эта статья о неудаче, создана для того чтобы продемонстрировать как полезно рассказывать о неудачах. Надеюсь она изменит результат на успех благодаря комментариям.
Как это обычно бывает, появляется новая задача, вы её обдумываете и останавливаетесь на каком то решении. Дальше, это решение необходимо воплотить в жизнь и вот тут то начинается самое интересное…
Передо мной встала большая задача перевести инфраструктуру компании и её проектов на автоматизированное управление. Впоследствии длительной мозговой деятельности, было последовательно выбрано использовать ansible и как веб интерфейс к нему jenkins. По мере понимания всех бизнес-процессов и системы работы серверов и сервисов, я уже чётко видел всю картину IaC (Infrastructure as Сode — инфраструктура как код). После пилотных плейбуков и ролей пора было начинать частичное внедрение, для ансибл есть замечательная возможность динамически собирать списки серверов которыми нам требуется управлять. Для хранения большого числа серверов и всех необходимых переменных для них решил на первых этапах использовать наши собственные ДНС серверы почерпнув идею у badoo(чтоб вам икалось ребята).
Когда вы хотели как лучше, а получилось как всегда. Вперёд, вниз в глубины гугловых дебрей.
Безопасность запросов к DNS
Конечно первое, что должно волновать после выбора технологии соответствующей задачам это безопасность. Для гарантии клиентского запроса, что ответ пришёл именно от нашего сервера, мы планируем внедрить DNSSEC, но времени на всё не хватает. Для получения списка всей зоны используется AXFR запрос, это стандартная функция подавляющего большинства днс серверов, которую мы используем и для репликации данных между нашими днс слейвами. Но выдавать всем желающим всю нашу зону неприемлемо, вариантов защиты от выполнения нелегитимных запросов не много. Во первых это ограничение на стороне сервера по внутренним подсетям IPv4 которые мы используем. Во вторых это TSIG или GSS-TSIG аутентификация. Настройка GSS-TSIG более длительна из-за большего количества необходимых действий, но возможно в будущем мы её всё таки будем использовать. Следовательно выбор пал на TSIG, для которого существует вполне простенький документ стандарта RFC 2845.
Кишки стандарта TSIG
Как работает TSIG аутентификация, я рассказывать не буду это описано в документе или на русском языке в гугле. В RFC нас информируют о всяких подробностях, типа формата пакета с запросом к ДНС серверу, но про формат файлов ключей там не написано, зато есть перечисление алгоритмов хешей на базе которых и работает аутентификация:
- Algorithm Name Reference
- gss-tsig RFC 3645
- HMAC-MD5.SIG-ALG.REG.INT RFC 2845
- hmac-sha1 RFC 4635
- hmac-sha224 RFC 4635
- hmac-sha256 RFC 4635
- hamc-sha384 RFC 4635
- hmac-sha512 RFC 4635
MD5 уже больше 10 лет подвержен коллизиям и раз есть варианты более стойких хешей давайте посмотрим на них. Вы наверняка уже слышали о том, что sha1 тоже уже подвержен коллизиям и т.к. я не боюсь дополнительной работы, и у нас не предполагается большого количества обращений к днс, то выберем самый стойкий алгоритм из доступных, sha512. Алгоритм выбран, теперь создадим ключик и поэкспериментируем с запросами к днс серверу, для этого установим пакет bind-utils и выполним команду:
dnssec-keygen -a HMAC-SHA512 -b 512 -n USER abracadabra.example.com
Ключик мы сделали, теперь вставим в наш днс сервер настройки для него и вернёмся к основной теме повествования. Мы потестили наши запросы и решили перейти на LDAP решительно приступили к реализации на питоне dynamic ansible inventory. Во первых нам надо разобрать файл ключа, его формат такой:
abracadabra.example.com. IN KEY 512 3 165 qxkDE9ZBykFEk4VBNUnB5s+mvJDWhP2dHi4Gd0PeSHf4ckOSMTlThnXD oXz3Av5zcXhIgAKlVkJ7j/Ru4qzSFg==
name class rrtype keysize protocol algorithm data
Начнём с конца, в 512 байт для udp пакета наш большой HMAC-SHA512 не уложился и строку ключа разделило пробелом. Не страшно склеим и будем в тср отправлять.
Дальше, алгоритм здесь непонятные циферки(именно эти циферки и заставили меня написать статью), пропустим пока.
Протокол, что такое, опять непонятные циферки в RFC об этом ничего не говорилось.
Разбираем дальше и там всё понятно: мы указали при создании ключа 512 байт и здесь мы видим длину нашего ключа, хорошо запомним(мне не пригодилась эта информация).
Тип ресурсной записи, у нас же днс вот мы и говорим днс серверу что это ключ.
Класс ресурсной записи; теоретически считается, что DNS может использоваться не только с TCP/IP, но и с другими типами сетей, код в поле класс определяет тип сети(бесполезная легаси фигня).
Ну и соответственно наше имя.
Ищем, ищем
У нас есть 2 непонятных поля, давайте разберёмся. Для начала я попытался разобраться, что это за идентификатор протокола. Но кроме того что есть диапазон от 1 до 250 и номер 3 в этом диапазоне принадлежит dnssec я ничего не узнал.
Потом я решил поискать идентификаторы алгоритмов, здесь информации было побольше. Я наткнулся на такую страничку администрации IANA, там была
Number | Description | Mnemonic | Zone Signing |
Trans. Sec. |
Reference |
---|---|---|---|---|---|
0 | Delete DS | DELETE | N | N | [RFC4034][RFC4398][RFC8078] |
1 | RSA/MD5 (deprecated, see 5) | RSAMD5 | N | Y | [RFC3110][RFC4034] |
2 | Diffie-Hellman | DH | N | Y | [RFC2539][proposed standard] |
3 | DSA/SHA1 | DSA | Y | Y | [RFC3755][proposed standard][RFC2536][proposed standard][Federal Information Processing Standards Publication (FIPS PUB) 186, Digital Signature Standard, 18 May 1994.][Federal Information Processing Standards Publication (FIPS PUB) 180-1, Secure Hash Standard, 17 April 1995. (Supersedes FIPS PUB 180 dated 11 May 1993.)] |
4 | Reserved | [RFC6725] | |||
5 | RSA/SHA-1 | RSASHA1 | Y | Y | [RFC3110][RFC4034] |
6 | DSA-NSEC3-SHA1 | DSA-NSEC3-SHA1 | Y | Y | [RFC5155][proposed standard] |
7 | RSASHA1-NSEC3-SHA1 | RSASHA1-NSEC3-SHA1 | Y | Y | [RFC5155][proposed standard] |
8 | RSA/SHA-256 | RSASHA256 | Y | * | [RFC5702][proposed standard] |
9 | Reserved | [RFC6725] | |||
10 | RSA/SHA-512 | RSASHA512 | Y | * | [RFC5702][proposed standard] |
11 | Reserved | [RFC6725] | |||
12 | GOST R 34.10-2001 | ECC-GOST | Y | * | [RFC5933][standards track] |
13 | ECDSA Curve P-256 with SHA-256 | ECDSAP256SHA256 | Y | * | [RFC6605][standards track] |
14 | ECDSA Curve P-384 with SHA-384 | ECDSAP384SHA384 | Y | * | [RFC6605][standards track] |
15 | Ed25519 | ED25519 | Y | * | [RFC8080][standards track] |
16 | Ed448 | ED448 | Y | * | [RFC8080][standards track] |
17-122 | Unassigned | ||||
123-251 | Reserved | [RFC4034][RFC6014] | |||
252 | Reserved for Indirect Keys | INDIRECT | N | N | [RFC4034][proposed standard] |
253 | private algorithm | PRIVATEDNS | Y | Y | [RFC4034] |
254 | private algorithm OID | PRIVATEOID | Y | Y | [RFC4034] |
255 | Reserved | [RFC4034][proposed standard] |
Хорошо, часть таблицы идентификаторов у нас есть, но наша циферка идентификатора 165 которая входит в зарезервированное пространство. Ссылки в таблице из этого пространства ведут на другие стандарты которые не содержат информации об идентификаторах алгоритмов. И тут Остапа понесло… Я начал погружаться в эти непомерные перекрёстные ссылки, то на один стандарт, то на второй. В итоге я плюнул и полез в исходники bind-utils. Там я нашёл список дефайнов
/* DST algorithm codes */
#define DST_ALG_UNKNOWN 0
#define DST_ALG_RSAMD5 1
#define DST_ALG_RSA DST_ALG_RSAMD5 /*%< backwards compatibility */
#define DST_ALG_DH 2
#define DST_ALG_DSA 3
#define DST_ALG_ECC 4
#define DST_ALG_RSASHA1 5
#define DST_ALG_NSEC3DSA 6
#define DST_ALG_NSEC3RSASHA1 7
#define DST_ALG_RSASHA256 8
#define DST_ALG_RSASHA512 10
#define DST_ALG_ECCGOST 12
#define DST_ALG_ECDSA256 13
#define DST_ALG_ECDSA384 14
#define DST_ALG_HMACMD5 157
#define DST_ALG_GSSAPI 160
#define DST_ALG_HMACSHA1 161 /* XXXMPA */
#define DST_ALG_HMACSHA224 162 /* XXXMPA */
#define DST_ALG_HMACSHA256 163 /* XXXMPA */
#define DST_ALG_HMACSHA384 164 /* XXXMPA */
#define DST_ALG_HMACSHA512 165 /* XXXMPA */
#define DST_ALG_INDIRECT 252
#define DST_ALG_PRIVATE 254
#define DST_ALG_EXPAND 255
#define DST_MAX_ALGS 255
и добавил часть в свой код на питоне
HashMap = {
'160': 'gss-tsig',
'157': 'HMAC-MD5.SIG-ALG.REG.INT',
'161': 'hmac-sha1',
'162': 'hmac-sha224',
'163': 'hmac-sha256',
'164': 'hmac-sha384',
'165': 'hmac-sha512'
}
О чём вообще тут написано
Для AXFR запроса мне нужно только 3 поля из ключа: имя, алгоритм и сам ключ. Собственно свою задачу я решил, но успехом это не считаю.
В целом повествование наверно написано не очень понятно, но вывод будет простой. Если я столкнулся с такими трудностями, значит это повторится у других. Стандарт 17 летней давности заставил меня потратить больше десятка часов на поиск информации для понимания. Не тратьте больше пары часов на поиски стандартов, смотрите на популярные реализации и заимствуйте информацию оттуда, исходники проще и понятней. В исходниках популярных проектов уже всю эту работу сделали другие люди, экономьте своё время.
P.S.
Так как большую часть информации я не нашёл, был бы очень признателен ссылкам на следующие материалы, которые возможно пригодятся мне или другим людям:
- Формат приватного ключа TSIG
TSIG
Private-key-format: v1.3
Algorithm: 165 (HMAC_SHA512)
Key: qxkDE9ZBykFEk4VBNUnB5s+mvJDWhP2dHi4Gd0PeSHf4ckOSMTlThnXDoXz3Av5zcXhIgAKlVkJ7j/Ru4qzSFg==
Bits: AAA=
Created: 20170902233427
Publish: 20170902233427
Activate: 20170902233427
- Формат самого ключа TSIG
- Полная таблица списка идентификаторов протоколов
- Полная таблица списка идентификаторов алгоритмов
UPD
Перепутал спойлер с приватным ключём, исправился.
Автор: AlexGluck