Недавно я начал централизовать управление ноутбуками и настольными компьютерами, принадлежащими компании, с помощью Active Directory и других инструментов, и это потребовало разработки некоторых соглашений об именовании для присвоения имен хостов машин.

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

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

  • Имена хостов должны быть ≤ 15 символов (совместимость с устаревшими предположениями NetBIOS).
  • Имена хостов должны генерироваться детерминировано на основе характеристик оборудования.
  • Имена хостов не должны меняться в зависимости от того, кто/что/где развернут компьютер.
  • Имена хостов должны сообщать что-то о характеристиках оборудования.
  • Имена хостов должны быть запоминающимися, но не спорными.

Установив эти требования, я быстро осознал, что «запоминающиеся» и «детерминированные аппаратными характеристиками» будет сложно соблюсти. Случайные буквы и цифры не запоминаются, равно как и «просто факты» не являются номерами активов. Имена или понятия запоминаются! Несколько лет назад я назвал набор ноутбуков Сквиртлом, Ахиллесом, Малкольмом и Гейтсом, и их всегда было чрезвычайно легко идентифицировать и ссылаться на них. Но я выбрал эти имена специально, а не генерировал их детерминистически. Забегая вперед, у меня были некоторые идеи:

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

В настоящее время многие приложения по умолчанию генерируют симпатичные и запоминающиеся имена хостов для новых проектов (смотрю на вас, Glitch.com). Некоторые операционные системы Linux также известны тем, что в именах своих выпусков используются кажущиеся (но, вероятно, не) случайными пары «прилагательное-существительное». Эти случайно сгенерированные имена хостов должны быть откуда-то взяты из списков слов.

Итак, я начал искать подходящие списки слов. Впервые я наткнулся на Списки слов, ориентированные на проверку орфографии (SCOWL) и друзья и Списки слов Electronic Frontier Foundation для генерации фраз-паролей из бросков костей. Некоторые ответы StackOverflow также привели к лексической базе данных WordNet, которая может перечислять слова по частям речи (прилагательное/существительное/и т. д.). Общий краткий список слов EFF был наиболее полезным для начала, потому что он составлен таким образом, чтобы содержать только слова длиной не более пяти символов, а все вульгарные слова были удалены.

Чтобы использовать этот словарь, я настроил приложение Node.js на Glitch.com для запуска простой функции, которая принимает произвольное значение, хеширует его, а затем возвращает слово из словаря путем модуляции хеш-значения по словарю. длина. Словарь — это просто текстовый файл с немногим более тысячи строк, поэтому в рамках последовательности запуска сервера я просто выполняю fs.readFileSync() и заполняю массив всеми словами в алфавитном порядке. Я использую встроенную криптобиблиотеку узла для запуска хэша MD5 на входе, поскольку нет причин избегать злонамеренных коллизий хэшей в этом приложении. Но затем мы сталкиваемся с проблемой хэш-значения MD5, являющегося 128-битным целым числом, в то время как тип данных JavaScript Number может обрабатывать только максимальный размер 53-битного целого числа. Хэш-значение выводится в виде шестнадцатеричной строки, но мы не можем преобразовать его в число, чтобы выполнить над ним операцию по модулю.

К счастью, Node.js 10.4.0 и выше поддерживает BigInt(), что решает эту проблему для нас, не требуя дополнительных внешних библиотек или ручной арифметики. Glitch работает на Node.js 8 по умолчанию, но вы можете изменить свойство engine вашего package.json файла, чтобы запросить любую из версий, описанных здесь (укажите только основную версию, чтобы избежать предупреждений при запуске).

Таким образом, несмотря на то, что MD5 взломан для стойкой криптографии, он по-прежнему является случайным оракулом, поэтому он имеет равномерное распределение по выходному диапазону для уникальных входных данных. Поскольку этот выходной диапазон (128 бит) огромен по сравнению с нашим ограниченным диапазоном из примерно 1000 словарных слов, мы можем безопасно распределить значение хеш-функции по модулю на ограниченный диапазон и по-прежнему гарантировать относительную однородность в ограниченном диапазоне. Это выглядит так:

const crypto = require('crypto');
module.exports = function (dict) {
  return function unsafeHashWord(stringData) {
    const md5 = crypto.createHash('MD5'); // intentionally not-cryptographically-secure
    md5.update(stringData, 'utf8');

    return dict[BigInt('0x' + md5.digest('hex')) % BigInt(dict.length)];
  };
}

Это прекрасно работает, но словарь меня не удовлетворил. «Сгенерированные имена» в конечном итоге выглядели странно, как отдельные слова, такие как «амино», «путт», «спал», «веселье» и т. д. Я хотел попробовать конструкцию прилагательное-существительное.

Итак, я получил список, сгенерированный из списков частей речи WordNet, и RegEx преобразовал данные в несколько дополнительных текстовых файлов для приложения Glitch, взяв слова длиной всего от 3 до 5 букв. Для файла данных, который аннотирует прилагательное/существительное следующим образом:

glass A:
glass N: glasses

RegEx для извлечения слов этой длины из одного или другого будет выглядеть так:

^[a-z]{3,5}(?= (A|N).*)

(Вот как вы используете положительный просмотр вперед!) Я сделал это в текстовом редакторе с помощью команды «Найти все» и просто вставил результат поиска с множественным выбором в новое окно. Спасибо Sublime Text.

Итак, теперь, когда у меня были отдельные словари для прилагательных и существительных, я создал новую функцию в приложении Node.js, чтобы возвращать комбинацию прилагательного и существительного. Я не был полностью уверен, какое решение для этого было бы лучшим, поэтому я решил сократить 128 бит хэш-вывода наполовину, передав передние 64 бита в словарь прилагательных, а последние 64 бита в словарь существительных, используя тот же самый арифметика по модулю подробно описана выше.

Это сработало как волшебство, но ТЕПЕРЬ моя проблема заключалась в том, что новые словари частей речи были не такими гигиеничными, как EFF. Итак, я изменил процесс запуска приложения, чтобы фильтровать список EFF по категориям прилагательных и существительных, сопоставляя значения в словарях частей речи.

Почти готово! Теперь у меня были неслучайно-вульгарные пары прилагательное-существительное, детерминистически генерируемые из компьютерных серийных номеров. Пока я разрабатывал все это, я тестировал эти поколения, используя скрипт Google Apps, чтобы определить пользовательскую функцию электронной таблицы на листе, где я храню наш компьютерный инвентарь (со всеми зарегистрированными серийными номерами, конечно). Эта функция отправляет веб-запрос на маршрут Express.js в приложении узла, размещенном на Glitch, которое вызывает запрошенную функцию JavaScript и возвращает значение. Интересно отметить: кажется, что связанные скрипты, которые предоставляют пользовательские функции на листах, не отображаются в журнале выполнения скриптов (поэтому любую обработку ошибок вы должны выяснить сами заранее).

Итак, пришло время очистить и разработать остальную часть структуры имени хоста. Все наши серверы уже следуют соглашению FC-BLAH01, и я решил, что клиентские машины будут соответствовать PAC-??-NAME. Хотя PAC технически нарушает правило связывания имени хоста с местом развертывания, на самом деле это скорее пространство имен, поскольку управление этими машинами осуществляется только на уровне моего отдела в рамках общего корпоративного домена. То есть, если бы все эти машины управлялись центральной ИТ-службой предприятия, это соглашение было бы недопустимым, но, поскольку машины фактически принадлежат нашему собственному отделу и не покидали бы отдел без очистки перед передачей в центральную ИТ-службу, префикс эффективно указывает, кто отвечает за управление машинами в домене.

Средний компонент должен был служить меткой для неизменяемых характеристик машины. Машины, на которые распространяются эти имена хостов, — это ноутбуки и настольные компьютеры Apple и Dell. Сначала я попробовал односимвольный код для обозначения рабочего стола ПК (D), ноутбука ПК (L), рабочего стола Mac (M), MacBook (B). В конце концов я понял, что M и B недостаточно ясны по сравнению с D и L. Затем я использовал двухсимвольный код, где первый символ обозначал марку (D для Dell и A для Apple), а второй символ будет означать D для рабочего стола и L для ноутбука. Это даст AD/AL/DD/DL. Я думал, что это было понятнее, но Apple Desktop привел бы к попаданию большого количества AD в базу данных Active Directory, что, вероятно, привело бы к путанице в долгосрочной перспективе. Поэтому я решил переключиться с «Desktop» на «Workstation», в результате чего получился окончательный набор кодов: AW/AL/DW/DL.

Последней проблемой, которую нужно было решить, было то, что мой префикс теперь занимал семь символов из возможных 15, оставляя 8 оставшихся для заполнения двумя детерминированными хеш-словами, большинство из которых состояло из 5 символов каждое. Итак, я добавил необязательный параметр в эту функцию JavaScript, которая принимала весь префикс, вычисляла, сколько символов осталось для работы (учитывая, что ограничение в 15 символов является историческим и статичным), а затем изменяла словари прилагательных и существительных. использовался для оставшейся части выполнения функции, чтобы гарантировать достижение этого предела. Учитывая нечетное количество оставшихся символов, разница делится между Math.ceil() и Math.floor() на длину, деленную на два (в пользу прилагательного).

Итак, теперь у меня есть легкий веб-сервис, привязанный к Google Sheet, который генерирует детерминированные имена хостов на основе информации о серийных номерах.