Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов

в 1:30, , рубрики: Ansible, devops, DNS, python, rfc, rfc2845, rfc4635, rfc6014, TSIG, Администрирование доменных имен, Стандарты связи

Эта статья о неудаче, создана для того чтобы продемонстрировать как полезно рассказывать о неудачах. Надеюсь она изменит результат на успех благодаря комментариям.
Как это обычно бывает, появляется новая задача, вы её обдумываете и останавливаетесь на каком то решении. Дальше, это решение необходимо воплотить в жизнь и вот тут то начинается самое интересное…

Передо мной встала большая задача перевести инфраструктуру компании и её проектов на автоматизированное управление. Впоследствии длительной мозговой деятельности, было последовательно выбрано использовать ansible и как веб интерфейс к нему jenkins. По мере понимания всех бизнес-процессов и системы работы серверов и сервисов, я уже чётко видел всю картину IaC (Infrastructure as Сode — инфраструктура как код). После пилотных плейбуков и ролей пора было начинать частичное внедрение, для ансибл есть замечательная возможность динамически собирать списки серверов которыми нам требуется управлять. Для хранения большого числа серверов и всех необходимых переменных для них решил на первых этапах использовать наши собственные ДНС серверы почерпнув идею у badoo(чтоб вам икалось ребята).

Когда вы хотели как лучше, а получилось как всегда. Вперёд, вниз в глубины гугловых дебрей.

Безопасность запросов к DNS

Конечно первое, что должно волновать после выбора технологии соответствующей задачам это безопасность. Для гарантии клиентского запроса, что ответ пришёл именно от нашего сервера, мы планируем внедрить DNSSEC, но времени на всё не хватает. Для получения списка всей зоны используется AXFR запрос, это стандартная функция подавляющего большинства днс серверов, которую мы используем и для репликации данных между нашими днс слейвами. Но выдавать всем желающим всю нашу зону неприемлемо, вариантов защиты от выполнения нелегитимных запросов не много. Во первых это ограничение на стороне сервера по внутренним подсетям IPv4 которые мы используем. Во вторых это TSIG или GSS-TSIG аутентификация. Настройка GSS-TSIG более длительна из-за большего количества необходимых действий, но возможно в будущем мы её всё таки будем использовать. Следовательно выбор пал на TSIG, для которого существует вполне простенький документ стандарта RFC 2845.

Кишки стандарта TSIG

Как работает TSIG аутентификация, я рассказывать не буду это описано в документе или на русском языке в гугле. В RFC нас информируют о всяких подробностях, типа формата пакета с запросом к ДНС серверу, но про формат файлов ключей там не написано, зато есть перечисление алгоритмов хешей на базе которых и работает аутентификация:

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 Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 1 Description Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 2 Mnemonic Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 3 Zone
Signing Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 4
Trans.
Sec. Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 5
Reference Ansible Dynamic Inventory из DNS или как растрачивать время на поиски стандартов - 6
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.

Так как большую часть информации я не нашёл, был бы очень признателен ссылкам на следующие материалы, которые возможно пригодятся мне или другим людям:

  1. Формат приватного ключа TSIG
    TSIG

    Private-key-format: v1.3
    Algorithm: 165 (HMAC_SHA512)
    Key: qxkDE9ZBykFEk4VBNUnB5s+mvJDWhP2dHi4Gd0PeSHf4ckOSMTlThnXDoXz3Av5zcXhIgAKlVkJ7j/Ru4qzSFg==
    Bits: AAA=
    Created: 20170902233427
    Publish: 20170902233427
    Activate: 20170902233427
  2. Формат самого ключа TSIG
  3. Полная таблица списка идентификаторов протоколов
  4. Полная таблица списка идентификаторов алгоритмов

UPD

Перепутал спойлер с приватным ключём, исправился.

Автор: AlexGluck

Источник

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


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