воскресенье, 24 ноября 2019 г.

Добавление в репозиторий aptly пакетов новой архитектуры

Ещё одна микрозаметка. Не так давно я уже писал об aplty - утилите для управления репозиториями пакетов Debian: Создание своего репозитория Debian при помощи aptly.

По какой-то причине aptly не добавляет в индекс пакеты тех архитектур, которые отсутствовали в репозитории на момент первичной публикации репозитория. Если в опубликованный репозиторий нужно добавить пакеты новой архитектуры, понадобится удалить существующую публикацию и создать новую публикацию, указав необходимые архитектуры пакетов.

Удаляем существующую публикацию репозитория с именем stretch в каталоге repo:
$ aptly publish drop stretch repo
Removing /home/stupin/.aptly/public/repo/dists/stretch...
Cleaning up prefix "repo" components main...

Published repository has been removed successfully.
Публикуем репозиторий на том же месте снова, указывая явным образом список публикуемых архитектур пакетов:
$ aptly publish repo -architectures="amd64,armhf,sources" -skip-signing=true stretch repo
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...

Local repo stretch has been successfully published.
Please setup your webserver to serve directory '/home/stupin/.aptly/public' with autoindexing.
Now you can add following line to apt sources:
  deb http://your-server/repo/ stretch main
Don't forget to add your GPG key to apt with apt-key.

You can also use `aptly serve` to publish your repositories over HTTP quickly.
При дальнейших обновлениях уже опубликованного репозитория все архитектуры, указанные при его первичной публикации, будут обрабатываться обычным образом:
$ aptly publish update -skip-signing=true stretch repo
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...
Cleaning up prefix "repo" components main...

Publish for local repo repo/stretch [amd64, armhf, sources] publishes {main: [stretch]} has been successfully updated.
Посмотреть список архитектур пакетов в уже опубликованных репозиториях можно следующим образом:
$ aptly publish list
Published repositories:
  * repo/buster [amd64, source] publishes {main: [buster]}
  * repo/stretch [amd64, armhf, sources] publishes {main: [stretch]}
  * repo/wheezy [amd64, source] publishes {main: [wheezy]}

воскресенье, 17 ноября 2019 г.

Проверка действительности SSL-сертификата в urllib2

Эта очень короткая заметка представляет собой дополнение к одной из моих прошлых заметок: Использование urllib2 в Python. После обновления Debian с релиза Wheezy до релиза Stretch библиотека urllib2 при обращении к HTTPS-ресурсам стала проверять действительность сертификата. Т.к. в моём случае на этих ресурсах использовался сертификат, подписанный удостоверяющим центром локальной сети, то обращения к таким ресурсам приводили к ошибкам: библиотека urllib2 в подобных случаях исторгала из своих недр исключение ssl.SSLError.

Для исправления этой ошибки нужно было во-первых добавить сертификат удостоверящюего центра локальной сети в список доверенных, а во-вторых - указать библиотеке urllib2 список доверенных сертификатов.

Первым делом нужно установить в систему стандартные сертификаты удостоверяющих центров. Сделать это можно путём установки пакета ca-certificates:
# apt-get install ca-certificates
Дополнительные сертификаты удостоверяющих центров, которым следует доверять, можно положить в каталог /usr/local/share/ca-certificates, после чего обновить файл /etc/ssl/certs/ca-certificates.crt при помощи незамысловатой команды:
# update-ca-certificates
Теперь можно указать файл /etc/ssl/certs/ca-certificates.crt библиотеке urllib2. Сделать это можно, добавив в список обработчиков запросов дополнительный обработчик, который занимается проверкой действительности SSL-сертификатов. В прошлой статье уже имеются примеры использования сразу нескольких разных обработчиков. Если воспользоваться тем же подходом, то получится примерно так:
handlers = []

# Добавляем обработчик HTTPS-запросов
context = ssl.create_default_context()
context.load_verify_locations('/etc/ssl/certs/ca-certificates.crt')
handler = urllib2.HTTPSHandler(context=context)
handlers.append(handler)

opener = urllib2.build_opener(*handlers)

воскресенье, 10 ноября 2019 г.

Контроль работоспособности DHCP-сервера в Zabbix

Как-то раз на работе потребовалось поставить на контроль DHCP-сервер. Сделать это нужно было, как обычно, срочно. Пока я делал, заказчики постоянно "держали руку на пульсе", интересовались результатами и поторапливали. Пытался воспользоваться различными утилитами командной строки, но в итоге наиболее пригодным вариантом оказалось написание собственного скрипта на python с использованием библиотеки ScaPy, благо в интернете можно найти примеры подобных скриптов. Собирались испытать скрипт на одном DHCP-сервере, а потом поставить на контроль ещё несколько десятков.

Года через два наткнулся на этот забытый скрипт. Раз уж он никому не нужен, то можно попытаться довести его до ума и поделиться им. Забрал скрипт домой и стал экспериментировать на виртуалках. Пока доводил скрипт до ума, на работе снова возникла необходимость поставить на контроль один DHCP-сервер, никак не связанный с теми, для которых скрипт первоначально писался.

Принципы работы DHCP

Для начала ознакомимся немого с принципами работы DHCP.

Выдача настроек по DHCP происходит в следующей последовательности:
  1. клиент, желающий получить настройки, посылает широковещательный запрос DHCPDISCOVERY, при помощи которого пытается обнаружить доступные DHCP-серверы,
  2. сервер откликается на запрос DHCPDISCOVERY и отвечает пакетом DHCPOFFER, в котором предлагает клиенту использовать определённый IP-адрес,
  3. клиент отправляет серверу запрос DHCPREQUEST, в котором просит закрепить за ним указанный IP-адрес,
  4. сервер отвечает клиенту пакетом DHCPACK, в котором подтверждает, что IP-адрес закреплён за клиентом. Если сервер по каким-то причинам не может закрепить за клиентом указанный им IP-адрес, то отвечает пакетом DHCPNAK.
Для продления аренды ранее выданного сервером IP-адреса клиент использует запросы DHCPREQUEST, указывая в запросе ранее выданный ему IP-адрес.

Также протоколом предусмотрены пакеты DHCPDECLINE и DHCPINFO.

При помощи DHCPDECLINE клиент может сообщить серверу, что предложенный им IP-адрес уже используется.

Если клиенту не нужен IP-адрес от DHCP-сервера, то вместо запроса DHCPREQUEST клиент может отправить запрос DHCPINFORM для запроса дополнительных сетевых параметров. Так же как и в ответ на запрос DHCPREQUEST, на запрос DHCPINFORM сервер отвечает пакетом DHCPACK.

Если клиент хочет вернуть IP-адрес DHCP-серверу, он может отправить серверу запрос DHCPRELEASE. Если сервер получит такой запрос, он помечает IP-адрес как свободный. Протоколом не предусмотрен ответ на запросы DHCPRELEASE, поэтому невозможно убедиться в том, что сервер получил и обработал запрос.

Описание скрипта dhcp.py

А теперь перейдём к делу. Для контроля работоспособности DHCP-сервера был подготовлен скрипт dhcp.py, принимающий следующие аргументы:
  • имя сетевого интерфейса, за которым находится контролируемый DHCP-сервер,
  • MAC-адрес клиента, который будет использоваться в запросах к DHCP-серверу. По умолчанию - MAC-адрес сетевого интерфейса,
  • IP-адрес, который ожидается получить от DHCP-сервера. По умолчанию клиент будет принимать любой IP-адрес,
  • IP-адреса DHCP-серверов, ответ от которых ожидается получить. Если на пути между клиентом и DHCP-сервером имеются DHCP-релеи, то тут нужно указывать настоящие IP-адреса серверов, а не IP-адреса релеев. Можно указать несколько IP-адресов, разделив их двоеточиями. По умолчанию принимаются ответы от любых DHCP-серверов,
  • время ожидания ответа, по умолчанию составляет 2 секунды.
Обязательно для указания только имя интерфейса. Все остальные настройки не обязательны и могут быть опущены.

Алгоритм работы скрипта следующий:
  • через указанный интерфейс отправляется запрос DHCPDISCOVERY с указанным MAC-адресом или MAC-адресом интерфейса,
  • в течение таймаута принимаются пакеты DHCPOFFER,
  • если пакетов DHCPOFFER не поступило, выполнение скрипта прерывается,
  • если указан ожидаемый IP-адрес, то проверяется, что в DHCPOFFER предложен именно этот IP-адрес. Если DHCP-сервер предложил другой IP-адрес, то выполнение скрипта прерывается,
  • если указан список IP-адресов DHCP-серверов, то проверяется, что в DHCPOFFER указан IP-адрес одного из ожидаемых DHCP-серверов. Если получен ответ от другого DHCP-сервера, то выполнение скрипта прерывается,
  • отправляется запрос DHCPREQUEST с просьбой выдать в аренду предложенный IP-адрес,
  • в течение таймаута принимаются пакеты DHCPACK,
  • если пакетов DHCPACK не поступило, выполнение скрипта прерывается,
  • отправляется запрос DHCPRELEASE на возврат арендованного IP-адреса.
Коды ошибок скрипта, которые он выдаёт на стандартный вывод:
  1. указанный сетевой интерфейс не существует,
  2. неправильный MAC-адрес,
  3. нет ответа DHCPOFFER,
  4. неправильный пакет DHCPOFFER,
  5. получен ответ от нежелательного DHCP-сервера,
  6. предложен нежелательный IP-адрес,
  7. нет ответа DHCPACK,
  8. неправильный пакет DHCPACK.
Для работы скрипту необходимы привилегии пользователя root, но Zabbix работает под простым пользователем, поэтому придётся воспользоваться sudo. Если использовать скрипт dhcp.py как скрипт внешнего опроса, то для его запуска через sudo потребуется создать ещё один скрипт. Но такое решение не кажется мне красивым. Я решил вызывать скрипт dhcp.py через Zabbix-агента, для чего понадобится настроить в конфигурации агента UserParameter, где найдётся место и для sudo.

Настройка Zabbix-агента

Сначала установим пакет sudo, если он ещё не установлен:
# apt-get install sudo
И выдадим пользователю zabbix, от имени которого работает Zabbix-агент, права запускать скрипт dhcp.py с правами пользователя root:
Defaults:zabbix !requiretty
zabbix ALL=(ALL) NOPASSWD:/etc/zabbix/dhcp.py *
Теперь положим скрипт dhcp.py в каталог /etc/zabbix и выдадим права запускать его:
# chmod +x /etc/zabbix/dhcp.py
В конфигурации Zabbix-агента в файле /etc/zabbix/zabbix_agentd.conf добавим строчку:
UserParameter=dhcp[*],/usr/bin/sudo /etc/zabbix/dhcp.py "$1" "$2" "$3" "$4" "$5"
После изменения конфигурации Zabbix-агента, его нужно перезапустить:
# systemctl restart zabbix-agent

Шаблоны для Zabbix

Для того, чтобы в последних данных можно было наглядно видеть результат последней проверки, я подготовил файл для преобразования значений Valuemap_DHCP.xml. В файле созданы следующие преобразования значений:

Также я подготовил два варианта шаблона:
  • Template_App_DHCP.xml - шаблон для использования с Zabbix-агентом, работающим в обычном, пассивном режиме,
  • Template_App_DHCP_Active.xml - шаблон для использования с Zabbix-агентом, работающим в активном режиме.
В шаблоне имеются макросы, при помощи которых можно настроить элемент данных, который контролирует состояние DHCP-сервера. Указано только одно значение по умолчанию для имени интерфейса - eth0, т.к. этот аргумент обязателен для указания.

В шаблоне есть только один элемент данных, который использует макросы из шаблона в качестве своих аргументов по умолчанию:

К этому элементу данных присоединено 8 триггеров, два из которых имеют высокую важность, т.к. свидетельствуют о неработоспособности DHCP-сервера или о появлении чужого DHCP-сервера. Остальные триггеры имеют уровень важности "Предупреждение":

Пример использования

Для примера я настроил контроль исправности DHCP-сервера на своём домашнем компьютере. В виртуальную машину с Zabbix проброшен интерфейс ens7, смотрящий в локальную сеть с DHCP-сервером. На интерфейсе отсутствуют сетевые настройки, используется он только для контроля исправности DHCP-сервера. На уровне сетевого узла созданы макросы, переопределяющие значения по умолчанию, определённые в шаблоне:

В последних данных можно увидеть числовое значение результата проверки DHCP-сервера и короткий текст, позволяющий быстро понять, какой именно результат получен:

Использованные материалы