• Sy chevron_right

      Отключение сообщений об ошибках на стандартный ввод-вывод в net-snmp

      noreply@blogger.com (morbo) · pubsub.slavino.sk / sysadmblog · Sunday, 26 July, 2020 - 08:00 edit

    Есть у меня один скрипт на Python, использующий привязки к библиотеке Net-SNMP. Скрипт отмечает результаты своей работы в базе данных и выводит сообщения об ошибках только при каких-то совсем уж неожиданных ошибках, например, при отсутствии связи с этой базой данных. Если же скрипт отработал шататно, он не должен выводить никаких сообщений. Поскольку сркипт регулярно запускался через планировщик задач cron, то при ошибках весь вывод скрипта отправлялся мне по почте. В общем, всё как обычно.

    Раньше скрипт выполнял запросы только по протоколам SNMP первой и второй версии. Когда же в сети появились устройства, доступные только по протоколу SNMP третьей версии, скрипт начал выводить ошибки такого вида:
    Authentication failed for backup
    backup - это имя пользователя SNMPv3, под которым не удавалось выполнить SNMP-запросы.

    Поскольку скрипт анализирует результаты запросов SNMP, то эти диагностические сообщение лишь засоряют вывод скрипта. Как оказалось, эти сообщения выводит сама библиотека Net-SNMP и я не нашёл никаких способов для того, чтобы их отключить, например, через конфигурацию /etc/snmp/snmp.conf. Чтобы отключить эти сообщения, потребуется поправить исходный текст библиотеки. Скачаем и распакуем пакет с исходными текстами net-snmp:
    $ apt-get source net-snmp
    Наши правки к исходному коду оформим в виде заплатки при помощи инструмента quilt, подробнее о котором я писал в заметке Использование quilt для подготовки заплат . Создадим новую заплатку:
    $ quilt new removed-redundant-log-message
    Добавим в будущую заплатку файл:
    $ quilt add snmplib/snmpusm.c
    Открываем файл snmplib/snmpusm.c в текстовом редакторе, находим и удаляем две строчки с вызовом функции snmp_log:
                snmp_log(LOG_WARNING, "Authentication failed for %s\n",
    user->name);
    В этом же файле при подобных ошибках функции snmp_log не вызываются и это единственный вызов snmp_log в этом файле. Скорее всего этот вызов функции был оставлен по ошибке, а не удалён вместе с другими подобными вызовами.

    Чтобы изменения в файле snmplib/snmpusm.c попали в заплатку, обновляем заплатку:
    $ quilt refresh
    Можно прокомментировать сделанные в пакете изменения и изменить версию пакета:
    $ dch -i
    В запустившемся редакторе описываем последние изменения:
    net-snmp (5.7.3+dfsg-5-stupin1) UNRELEASED; urgency=medium

    * Fixed SNMPv3 time widnow logic to work with D-Link switches
    * Removed redundant log message "Authentication failed from"

    -- Vladimir Stupin <vladimir@stupin.su> Fri, 15 May 2020 16:01:26 +0500
    Теперь можно пересобрать deb-пакеты, выполнив команду:
    $ debuild -us -uc
    В вышестоящем каталоге появятся готовые deb-пакеты, которые можно установить в систему командой dpkg -i или поместить в репозиторий пакетов при помощи aptly. Почитать об использовании aptly можно в другой моей заметке Создание своего репозитория Debian при помощи aptly .

    После установки исправлений надоедливые сообщения об ошибках пропали.

    Značky: #buster, #debian, #linux, #snmp, #Linux

    • Sy chevron_right

      Сертификат удостоверяющего центра Active Directory для веб-серверов

      noreply@blogger.com (morbo) · pubsub.slavino.sk / sysadmblog · Sunday, 19 July, 2020 - 08:00 edit · 1 minute

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

    Насколько я знаю, из самой популярной тройки браузеров только Firefox в конфигурации не использует системное хранилище сертификатов. Если учитывать, что большинство компьютеров в локальных сетях предприятий обычно работают под управлением Windows и включены в домен Active Directory, а большая часть пользователей используют браузеры Chrome и Internet Explorer, то таким образом можно внедрить HTTPS на веб-серверах в локальной сети максимально гладко. В случае с Firefox можно либо включить использование системного хранилища сертификатов Windows в настройках браузера, либо вручную импортировать в браузер корневой сертификат удостоверяющего центра Active Directory. Он всего один и срок его действия больше, чем у сертификатов веб-серверов.

    По возможности лучше генерировать для каждого веб-сервера индивидуальный сертификат и тогда при взломе одного веб-сервера трафик остальных останется защищённым, т.к. злоумышленник не сможет использовать приватный ключ со взломанного сервера для расшифровки перехваченного трафика других веб-серверов или организации атаки посредника. Я же для экономии времени предпочёл сегенерировать один сертификат сразу для всех веб-серверов, находящихся в моей зоне ответственности. Чтобы при необходимости продлить сертификат мне не пришлось бы вспоминать значения полей из запроса на сертификат и не пропустить по невнимательности ни одного из веб-серверов, я подготовил файл конфигурации cert-web.ini такого вида:
    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req

    [req_distinguished_name]
    countryName = RU
    countryName_default = RU
    stateOrProvinceName = Bashkortostan Republic
    stateOrProvinceName_default = Bashkortostan Republic
    localityName = Ufa
    localityName_default = Ufa
    organizationalUnitName = My Department
    organizationalUnitName_default = My Department
    commonName = My Company
    commonName_default = My Company
    commonName_max = 64

    [ v3_req ]
    # Extensions to add to a certificate request
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names

    [alt_names]
    DNS.1 = server1.domain1.tld
    DNS.2 = server2.domain1.tld
    DNS.3 = server3.domain1.tld
    DNS.4 = server4.domain2.tld
    DNS.5 = server5.domain2.tld
    DNS.6 = server6.domain3.tld
    В конфигурации упоминаются:
    • RU - код страны,
    • Bashkortostan Republic - административная единица внутри страны,
    • Ufa - название населённого пункта,
    • My Company - название компании,
    • My Department - название подразделения компании,
    • serverX.domainY.tld - доменные имена веб-серверов в локальной сети.
    Генерируем приватный ключ:
    $ openssl genrsa -out cert-web.key 2048
    Генерируем запрос на сертификат в соответствии с файлом конфигурации:
    $ openssl req -config cert-web.ini -new -key cert-web.key -out cert-web.csr
    Запрос на сертификат из файла cert-web.csr я передал администратору домена Active Directory, который подписал его в удостоверяющем центре и вернул мне подписанный сертификат cert-web.cer в формате DER.

    Полученный сертификат надо преборазовать из формата DER в формат PEM:
    $ openssl x509 -inform der -in cert-web.cer -out cert-web.crt
    Осталось соединить приватный ключ и сертификат в формате PEM для использования получившегося файла веб-сервером:
    $ cat cert-web.key cert-web.crt > cert-web.pem
    Остаётся положить получившийся один файл на веб-серверы и задействовать их использование в конфигурациях веб-серверов. Если использовать систему автоматизированного управления конфигурациями, то эта задача не займёт много времени. После освоения Ansible я стал раскладывать сертификаты на веб-серверы именно с её помощью.

    Для того, чтобы получить корневой сертификат удостоверяющего центра Active Directory, нужно зайти веб-браузером на сервер с удостоверяющим центром, где можно будет найти и скачать корневой сертификат. В моём случае корневой сертификат удостоверяющего центра был доступен по ссылке вида: https://domain1.tld/certsrv/certnew.cer?ReqID=CACert&Renewal=2&Mode=inst&Enc=b64

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

    Сначала устанавливаем стандартные сертификаты удостоверяющих центров, если они ещё не были установлены:
    # apt-get install ca-certificates
    Кладём корневой сертификат нашего удостоверяющего центра в каталог /usr/local/share/ca-certificates/, предназначенный специально для дополнительных сертификатов удостоверяющих центров.

    Обновляем список корневых сертификатов, которым должна доверять библиотека openssl:
    # update-ca-certificates
    После этого все установленные в системе программы должны начать доверять сгенерированным нами сертификатам веб-серверов. Если этого не случилось и какая-то программа или модуль не начали доверять новым сертификатам, изучите документацию. Например, для того, чтобы модуль urllib2 для Python начал доверять сертификатам, мне понадобилось передавать библиотеке urllib2 дополнительные настройки, описанные в заметке: Проверка действительности SSL-сертификата в urllib2

    Značky: #debian, #linux, #onpenssl, #ssl, #Linux

    • Sy chevron_right

      Решение проблемы с SSL/TLS в Sylpheed

      pubsub.slavino.sk / sysadmblog · Sunday, 29 December, 2019 - 08:00 edit · 2 minutes

    На рабочем компьютере решил обновить Debian с релиза Stretch до Buster и столкнулся с проблемой: при попытке проверить почту Sylpheed на POP3-сервере вылетает окно с ошибкой:
    sylpheed_ssl_error.png
    Если попытаться запустить Sylpheed через окно терминала, можно увидеть чёть больше подробностей:
    $ sylpheed 

    (sylpheed:7387): LibSylph-WARNING **: 15:42:26.003: SSL_connect() failed with error 1, ret = -1 (error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol)


    (sylpheed:7387): LibSylph-WARNING **: 15:42:26.003: can't start TLS session.


    (sylpheed:7387): LibSylph-WARNING **: 15:42:26.004: [15:42:26] Сбой сокета.
    Как видно, POP3-сервер не поддерживает требуемую версию протокола SSL/TLS.

    В прошлом мне уже приходилось разбираться с подобной проблемой при отправке уведомлений из Zabbix на сервер Jabber: Пересборка libiksemel для решения проблемы JABBER tls handshake failed в Zabbix . В этот раз я попробовал пойти тем же путём, установил утилиту командной строки gnutls-cli-debug и воспользоваться ей для проверки возможностей Jabber-сервера.

    Устанавливаем пакет gnutls-bin:
    # apt-get install gnutls-bin
    Вызываем утилиту для получения списка возможностей Jabber-сервера:
    $ gnutls-cli-debug -p 995 mail.server.ru
    Утилита выводит следующую информацию:
    GnuTLS debug client 3.6.7
    Checking mail.server.ru:995
    whether we need to disable TLS 1.2... no
    whether we need to disable TLS 1.1... no
    whether we need to disable TLS 1.0... no
    whether %NO_EXTENSIONS is required... no
    whether %COMPAT is required... no
    for TLS 1.0 (RFC2246) support... yes
    for TLS 1.1 (RFC4346) support... no
    fallback from TLS 1.1 to... TLS 1.0
    for TLS 1.2 (RFC5246) support... no
    for TLS 1.3 (RFC8446) support... no
    |<1>| FFDHE groups advertised, but server didn't support it; falling back to server's choice
    TLS1.2 neg fallback from TLS 1.6 to... TLS1.0
    for HTTPS server name... unknown
    for certificate chain order... sorted
    for safe renegotiation (RFC5746) support... yes
    for encrypt-then-MAC (RFC7366) support... no
    for ext master secret (RFC7627) support... no
    for heartbeat (RFC6520) support... no
    for version rollback bug in RSA PMS... no
    for version rollback bug in Client Hello... no
    whether the server ignores the RSA PMS version... no
    whether small records (512 bytes) are tolerated on handshake... yes
    whether cipher suites not in SSL 3.0 spec are accepted... yes
    whether a bogus TLS record version in the client hello is accepted... yes
    whether the server understands TLS closure alerts... yes
    whether the server supports session resumption... no
    for anonymous authentication support... no
    |<1>| FFDHE groups advertised, but server didn't support it; falling back to server's choice
    for ephemeral Diffie-Hellman support... yes
    |<1>| FFDHE groups advertised, but server didn't support it; falling back to server's choice
    for RFC7919 Diffie-Hellman support... no
    for ephemeral EC Diffie-Hellman support... no
    for curve SECP256r1 (RFC4492)... no
    for curve SECP384r1 (RFC4492)... no
    for curve SECP521r1 (RFC4492)... no
    for curve X25519 (RFC8422)... no
    for AES-GCM cipher (RFC5288) support... no
    for AES-CCM cipher (RFC6655) support... no
    for AES-CCM-8 cipher (RFC6655) support... no
    for AES-CBC cipher (RFC3268) support... yes
    for CAMELLIA-GCM cipher (RFC6367) support... no
    for CAMELLIA-CBC cipher (RFC5932) support... yes
    for 3DES-CBC cipher (RFC2246) support... yes
    for ARCFOUR 128 cipher (RFC2246) support... yes
    for CHACHA20-POLY1305 cipher (RFC7905) support... no
    for MD5 MAC support... yes
    for SHA1 MAC support... yes
    for SHA256 MAC support... no
    for max record size (RFC6066) support... no
    for OCSP status response (RFC6066) support... no
    Сервер POP3 не поддерживает новейшие версии протокола TLS1.2 и TLS1.1, но поддерживает TLS1.0. Видимо почтовый клиент пытается использовать более безопасную версию протокола и не соглашается на TLS1.0.

    Решение проблемы несколько затянулось, т.к. первоначально я пошёл по ложному следу. Попробовал удалить сначала пакет openssl, а потом libgnutls30. Пакет sylpheed попадал в список удаляемых пакетов лишь во втором случае. На самом деле sylpheed зависел не от библиотеки libgnutls30, а от библиотеки libssl1.1. Для изменения настроек OpenSSL, которые используются по умолчанию, оказалось достаточно поправить файл /etc/ssl/openssl.cnf.

    В файле были прописаны такие настройки:
    [system_default_sect]
    MinProtocol = TLSv1.2
    CipherString = DEFAULT@SECLEVEL=2
    Для того, чтобы sylpheed успешно подключился к почтовому серверу, оказалось достаточно поменять минимальный требуемый протокол, вот так:
    [system_default_sect]
    MinProtocol = TLSv1.0
    CipherString = DEFAULT@SECLEVEL=2
    • Sy chevron_right

      Использование quilt для подготовки заплат

      pubsub.slavino.sk / sysadmblog · Sunday, 22 December, 2019 - 08:00 edit

    В прошлом я уже описывал несколько заплаток, которые накладываю на Zabbix для решения различных проблем:
    Кроме этих описанных заплаток имеется ещё несколько специфичных заплаток, которые сделаны для интеграции Zabbix со сторонними системами и нигде не описаны, т.к. вряд-ли кого-то заинтересуют.

    Кроме того, накладывать заплатки приходится не только на сам Zabbix, но и на связанные с ним библиотеки:
    Правки приходится делать в разных пакетах, не только связанных непосредственно с Zabbix, из-за чего я даже завёл репозиторий для доработанных пакетов. В заметке Создание своего репозитория Debian при помощи aptly можно найти ещё несколько примеров доработанных пакетов, доработке некоторых из которых были посвящены отдельные заметки.

    Количество специфичных заплаток для Zabbix, с которыми приходится работать, со временем только увеличивается. Если aptly помогает упорядочить работу с большим количеством нестандартных пакетов, то quilt помогает упорядочить работу с большим количеством заплаток одного и того же пакета.

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

    Ниже кратко описаны основные команды quilt, которые могут пригодиться для управления заплатками.

    Создаём новую заплатку:
    $ quilt new patch permit-edit-maintainances
    Добавляем в заплатку файлы, которые собираемся менять:
    $ quilt add frontends/php/maintenance.php
    $ quilt add frontends/php/include/classes/api/services/CMaintenance.php
    Посмотреть список файлов, содержимое которых будет отслеживаться в заплате, можно при помощи команды:
    $ quilt files
    Чтобы изменения в файлах не отслеживались в заплате, можно воспользоваться такой командой:
    $ quilt remove config.guess config.sub database/mysql/create.sql database/postgresql/create.sql database/sqlite3/create.sql
    Редактируем файлы:
    $ vim frontends/php/maintenance.php
    $ vim frontends/php/include/classes/api/services/CMaintenance.php
    Посмотреть получившуюся заплатку можно при помощи следующей команды:
    $ quilt diff
    Сохранить получившуюся заплатку в каталог debian/patches можно при помощи следующей команды:
    $ quilt refresh
    Список всех заплаток можно посмотреть при помощи команды:
    $ quilt series
    Текущая редактируемая заплата в выведенном списке будет подсвечена.

    При необходимости редактировать не последнюю заплатку, можно перемещаться по списку заплат. Для перемещения по списку на предыдущую заплату можно воспользоваться командой:
    $ quilt pop
    Для применения текущей заплаты и для перехода к следующей по списку можно воспользоваться командой:
    $ quilt push
    • Sy chevron_right

      OpenSMTPd как локальный SMTP-ретранслятор

      pubsub.slavino.sk / sysadmblog · Sunday, 15 December, 2019 - 08:00 edit

    В прошлом я писал заметки Postfix как локальный SMTP-ретранслятор и Postfix как локальный SMTP-ретранслятор во FreeBSD , в которых описывал настройку Postfix для пересылки писем администратору системы.

    К списку самых распространённых полноформатных SMTP-серверов для Unix, в который входят Sendmail, Exim, Postfix и, с некоторой натяжкой, qmail, присоединился ещё один - OpenSMTPd. Этот сервер был разработан в рамках проекта OpenBSD и по архитектуре напоминает Postfix и qmail: он тоже состоит из нескольких отдельных взаимодействующих друг с другом процессов.

    OpenSMTPd появился в репозиториях Debian и я решил попробовать настроить его в качестве замены Postfix из указанных выше статей. Первым делом установим OpenSMTPd из пакетов:
    # apt-get install opensmtpd
    Приводим файл конфигурации /etc/smtpd.conf к следующему виду:
    listen on lo

    table aliases file:/etc/aliases
    table secrets file:/etc/secrets

    accept from local for local virtual <aliases> deliver to mbox
    accept from local for any relay via tls+auth://user@mail.server.net:25 auth as "user@server.net"
    В файл /etc/aliases прописываем переадресации для получателей писем:
    postmaster: root
    root: admin@domain.tld
    В файл /etc/secrets прописываем пароли для учётных записей:
    user password
    Имя user должно совпадать с указанным в URL релея tls+auth://user@mail.server.net:25.

    Поменяем права доступа к созданным файлам:
    # chown root:root /etc/aliases /etc/secrets /etc/smtpd.conf
    # chmod u=rw,go=r /etc/aliases /etc/smtpd.conf
    # chmod u=rw,go= /etc/secrets
    Проверить правильность файла конфигурации можно при помощи следующей команды:
    # smtpd -n
    Запускаем почтовый сервер:
    # systemctl restart opensmtpd
    При редактировании файлов, указанных в опциях table, нужно сообщить об изменении демону, чтобы он перечитал содержимое таблиц. Например, после редактирования таблицы aliases нужно воспользоваться такой командой:
    # smtpctl update table aliases
    К сожалению, пока что в OpenSMTPd нельзя менять тему писем таким же образом, как это было описано в заметке Смена темы письма в Postfix . У Postfix простой файл конфигурации, но большое количество настроек, взаимодействующих между собой неочевидным образом. В этом плане OpenSMTPd выглядит достойной альтернативой, т.к. сочетает простоту и наглядность настройки с безопасной архитектурой.

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

      Резервное копирование файлов и баз данных через SSH

      pubsub.slavino.sk / sysadmblog · Sunday, 8 December, 2019 - 08:00 edit · 4 minutes

    Когда-то давно на работе задумались о резервном копировании серверов. У меня к тому времени уже был наработанный подход к резервному копированию, которым пользуются многие системные администраторы Unix.

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

    Поэтому и в тот раз я решил воспользоваться тем же подходом. Единственное дополнение, которое захотелось сделать - это класть резервные копии сразу в два географически разнесённых хранилища. Одна беда - блог я тогда ещё не вёл и не имел привычки выкладывать куда-то полезные наработки, поэтому скрипт на новом рабочем месте пришлось писать заново. Сейчас подумал, что неплохо всё-таки привести этот скрипт в презентабельный вид и написать о нём статью.

    Управление архивом

    Начнём с того, что нужно организовать резервные копии в хранилище таким образом, чтобы можно было легко найти нужную резервную копию и удалять устаревшие резервные копии. Файлы будем именовать по схеме YYYYMMDD_filename. При добавлении новой резервной копии filename нужно будет удостовериться, что новый файл не пуст. Если с файлом всё в порядке, то можно снабдить его префиксом YYYYMMDD_, а затем найти и удалить устаревшие резервные копии этого файла. Для выполнения этих функций в скрипте предусмотрены переменная KEEP_DAYS и функция с не самым удачным названием clear_old, которая берёт на себя описанные задачи:
    KEEP_DAYS=14

    clear_old()
    {
    # $1 - backup filename

    # Если файл с указанным именем не существует
    if [ ! -f "$1" ]
    then
    return 0
    fi

    size=`du --bytes "$1" | cut -f1`

    # Если размер меньше 512 байт
    if [ $size -lt 512 ]
    then
    # Удаляем сам файл резервной, а старые резервные копии не трогаем
    rm $1
    return 1
    else
    # Переименовываем новый файл резервной копии
    mv $1 `date "+%Y%m%d"`_$1

    # Удаляем предыдущие файлы старше KEEP_DAYS дней
    find . -name \*$1 -mtime +$KEEP_DAYS -delete
    return 0
    fi
    }
    Чтобы добавить файл в архив, нужно вызвать функцию clear_old и передать ей имя добавляемого в архив файла.

    Резервное копирование локальных файлов

    Начнём с простого. Резервные копии нужно снимать не только с других компьютеров, но и с самого компьютера, на котором будет работать скрипт. В моём случае в разное время это был сервер FreeBSD и виртуальная машина с Linux. В дальнейшем скрипт был перемещён на выделенную виртуальную машину с Linux, которая выполняла только функции резервного копирования. Острая необходимость снимать резервные копии самой виртуальной машины, где работал скрипт резервного копирования, отпала, но функции для этого уже были разработаны, поэтому на всякий случай резервные копии локальных файлов продолжают сниматься.

    В случае с FreeBSD функция резервного копирования файлов выглядела следующим образом:
    BACKUP_USER=rbackup
    BACKUP_GROUP=rbackup

    freebsd_files_local()
    {
    # $1 - backup filename

    touch "$1"
    chown $BACKUP_USER:$BACKUP_GROUP "$1"
    chmod 0600 "$1"

    tar -cjf - -T- --exclude '*.sql.gz' --exclude 'etc/zabbix/xbackup/*' <<END 2>/dev/null > "$1"
    /etc/
    /usr/local/etc/
    /root/
    /usr/home/
    /usr/local/www/
    END

    clear_old "$1"
    if [ $? -ne 0 ]
    then
    echo "Backing up local system to file $1 failed"
    fi
    }
    Функция принимает один аргумент - имя файла, в котором нужно сохранить резервную копию файлов. Создаваемый файл архива будет иметь формат .tbz (или .tar.bz2).

    Перед созданием архива сначала создаётся пустой файл, владельцем которого становится пользователь, указанный в переменной BACKUP_USER, группой-владельцем становится группа, указанная в переменной BACKUP_GROUP, права доступа к файлу выставляются таким образом, что читать и писать его может только пользователь, указанный в переменной BACKUP_USER. Это делается для того, чтобы предотвратить чтение файла резервной копии посторонними пользователями в процессе его создания.

    В архив помещаются файлы из каталогов /etc/, /usr/local/etc/, /usr/local/www/, /root/ и /usr/home/, кроме файлов с расширением .sql.gz и файлов из каталога /etc/zabbix/xbackup/. По-сути, в архив сохраняются файлы конфигурации, файлы из домашних каталогов пользователей и файлы веб-приложений, за исключением файлов с резервными копиями баз данных.

    После создания архива для файла вызывается функция clear_old, которая переименовывает файл, снабжая его имя префиксом YYYYMMDD_ с текущей датой, и удаляет устаревшие экземпляры этого файла.

    В переменных BACKUP_USER и BACKUP_GROUP выше указаны пользователь и группа с именем rbackup. Чтобы создать их, можно воспользоваться такими командами:
    # pw add group rbackup
    # pw add user rbackup -g rbackup -c "User for backup purposes" -d /usr/home/rbackup -m
    В случае с Linux функция резервного копирования файлов была такой:
    linux_files_local()
    {
    # $1 - backup filename

    touch "$1"
    chown $BACKUP_USER:$BACKUP_GROUP "$1"
    chmod 0600 "$1"

    tar -cjf - --files-from=- --exclude 'home/*/.pycharm_helpers/*' --exclude 'root/.cpan/*' --exclude 'root/.cache/*' --exclude 'home/*/.cache/*' --exclude 'usr/local/lib/*' <<END 2>/dev/null > "$1"
    /etc/
    /root/
    /home/
    /usr/local/
    /usr/lib/zabbix/
    /usr/share/mapnik/
    /var/www/
    /var/lib/dokuwiki/
    END

    clear_old "$1"
    if [ $? -ne 0 ]
    then
    echo "Backing up local system to file $1 failed"
    fi
    }
    В целом эта функция не отличается от функции для FreeBSD. Резервному копированию подвергаются каталог с файлами конфигурации, домашние каталоги пользователей, каталог веб-приложений, а также каталог /usr/local/, и каталоги с файлами Zabbix и DokuWiki. Из резервного копирования исключаются файлы, создаваемые средой разработки PyCharm (она умеет работать по SSH), каталоги с кэшированными данными, модулями Perl.

    Аналогично FreeBSD, в Linux нужно создать пользователя и группу rbackup, который будут использоваться в качестве владельца резервных копий:
    # groupadd rbackup
    # useradd -c "User for backup purposes" -d /home/rbackup -m -g rbackup rbackup

    Резервное копирование файлов с удалённых систем

    Резервное копирование удалённых файловых систем работает аналогично, с той лишь разницей, что команды резервного копирования запускаются по SSH, а их стандартный вывод сохраняется в файл в локальной файловой системе.
    SSH_PRIVKEY=/root/.ssh/id_rsa

    freebsd_files()
    {
    # $1 - server ip or dns-name
    # $2 - server ssh port
    # $3 - backup filename

    touch "$3"
    chown $BACKUP_USER:$BACKUP_GROUP "$3"
    chmod 0600 "$3"

    ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o Port=$2 -i $SSH_PRIVKEY $BACKUP_USER@$1 "/usr/local/bin/sudo /usr/bin/tar -cjf - -T- --exclude '*.sql.gz' --exclude 'etc/zabbix/xbackup/*'" <<END 2>/dev/null > "$3"
    /etc/
    /usr/local/etc/
    /root/
    /usr/home/
    /usr/local/www/
    END

    clear_old "$3"
    if [ $? -ne 0 ]
    then
    echo "Backing up remote system $1:$2 to file $3 failed"
    fi
    }
    Функции передаются три аргумента:
    1. IP-адрес или доменное имя удалённой системы, резервную копию файлов с которой нужно снять,
    2. порт SSH-сервера на этой системе (было время, когда использовались системы с SSH-сервером на нестандартном порту),
    3. имя файла создаваемого архива.
    Файлы, подлежащие резервному копированию, без особых проблем сможет прочитать только пользователь root. Однако, в целях безопасности, не хотелось бы разрешать удалённый доступ по SSH для пользователя root, пусть аутентификация и производится с использованием ключей. Не хочется также разрешать скрипту резервного копирования выполнять по SSH какие угодно команды. Для того, чтобы скрипт резервного копирования не смог повредить систему, случайно - из-за ошибки администратора, или специально - если доступ к скрипту получил злоумышленник, резервное копирование производится с использованием учётной записи из переменной BACKUP_USER и sudo.

    Для копирования публичного SSH-ключа в домашний каталог пользователя из переменной BACKUP_USER на FreeBSD я пользовался такими командами, которые просто копировал в терминал при настройке нового сервера или виртуальной машины:
    # chown rbackup:rbackup /usr/home/rbackup
    # mkdir /home/rbackup/.ssh
    # chown rbackup:rbackup /usr/home/rbackup/.ssh
    # cat <<END > /usr/home/rbackup/.ssh/authorized_keys
    ТУТ ПУБЛИЧНЫЙ SSH-КЛЮЧ
    END
    # chown rbackup:rbackup /usr/home/rbackup/.ssh/authorized_keys
    Для того, чтобы разрешить пользователю rbackup запускать через sudo команду tar для резервного копирования файлов, я использовал запускал visudo и вставлял такие настройки:
    Defaults:rbackup !requiretty
    rbackup ALL=(root:ALL) NOPASSWD:/usr/bin/tar -cjf - -T- *
    Для Linux аналогичная функция выглядит следующим образом:
    linux_files()
    {
    # $1 - server ip or dns-name
    # $2 - server ssh port
    # $3 - backup filename

    touch "$3"
    chown $BACKUP_USER:$BACKUP_GROUP "$3"
    chmod 0600 "$3"

    ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o Port=$2 -i $SSH_PRIVKEY $BACKUP_USER@$1 "/usr/bin/sudo /bin/tar -cjf - --files-from=- --exclude 'home/*/.pycharm_helpers/*' --exclude 'root/.cpan/*' --exclude 'root/.cache/*' --exclude 'home/*/.cache/*' --exclude 'usr/local/lib/*'" 2>/dev/null <<END > "$3"
    /etc/
    /root/
    /home/
    /usr/local/
    /usr/lib/zabbix/
    /usr/share/mapnik/
    /var/www/
    /var/lib/dokuwiki/
    END

    clear_old "$3"
    if [ $? -ne 0 ]
    then
    echo "Backing up remote system $1:$2 to file $3 failed"
    fi
    }
    Для настройки публичных SSH-ключей использовались аналогичные команды:
    # mkdir /home/rbackup/.ssh
    # chown rbackup:rbackup /home/rbackup/.ssh
    # cat <<END > /home/rbackup/.ssh/authorized_keys
    ТУТ ПУБЛИЧНЫЙ SSH-КЛЮЧ
    END
    # chown rbackup:rbackup /home/rbackup/.ssh/authorized_keys
    Для настройки прав в sudo использовались такие строчки:
    Defaults:rbackup !requiretty
    rbackup ALL=(root:ALL) NOPASSWD:/bin/tar -cjf - --files-from=- *

    Резервное копирование удалённой базы данных MySQL

    MySQL является сетевым сервером, поэтому резервные копии баз данных можно снимать по сети, не прибегая к помощи SSH. Однако, для того, чтобы не заниматься настройкой фильтрации пакетов, а также не гонять по сети лишний объём данных в открытом виде, резервное копирование баз данных было решено выполнять тоже через SSH.
    PASSWORD=тут-пароль

    generic_mysql()
    {
    # $1 - server ip or dns-name
    # $2 - server ssh port
    # $3 - database name
    # $4 - backup filename

    touch "$4"
    chown $BACKUP_USER:$BACKUP_GROUP "$4"
    chmod 0600 "$4"
    ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o Port=$2 -i $SSH_PRIVKEY $BACKUP_USER@$1 "mysqldump --single-transaction -u$BACKUP_USER -p$PASSWORD $3 | bzip2" 2>/dev/null > "$4"

    clear_old "$4"
    if [ $? -ne 0 ]
    then
    echo "Backing up $3 database from system $1:$2 to file $4 failed"
    fi
    }
    Функция принимает 4 аргумента:
    1. IP-адрес или доменное имя удалённой системы, резервную копию файлов с которой нужно снять,
    2. порт SSH-сервера на этой системе (было время, когда использовались системы с SSH-сервером на нестандартном порту),
    3. имя базы данных, резервную копию которой нужно снять,
    4. имя файла создаваемого архива.
    Функция вызывает mysqldump для получения резервной копии требуемой базы данных, а для сжатия данных перед отправкой через сеть используется компрессор bzip2. Соответственно, резервная копия, которая будет получена при помощи этой функции, имеет формат .sql.bz2.

    Для снятия резервной копии используется пользователь с тем же именем, который является владельцем архивов и используется для подключения к удалённым системам по SSH. Его имя настроено в переменной BACKUP_USER. А для аутентификации этого пользователя используется пароль, указанный в переменной PASSWORD. Понятно, что этот пользователь должен создан и ему должны быть предоставлены права доступа к указанным базам данных. Для этого можно воспользоваться такими запросами:
    CREATE USER 'rbackup'@'localhost' IDENTIFIED BY 'тут-пароль';
    FLUSH PRIVILEGES;
    GRANT SHOW DATABASES, SELECT, LOCK TABLES, RELOAD ON *.* TO 'rbackup'@'localhost';
    FLUSH PRIVILEGES;
    Т.к. в функции не используется каких-то специфичных путей к файлам и нет необходимости указывать полный путь к команде, выполняемой через sudo, то эта функция пригодна для использования и с FreeBSD и с Linux.

    Резервное копирование удалённой базы данных PostgreSQL

    Функция резервного копирования базы данных PostgreSQL в целом аналогична функции для резервного копирования базы данных MySQL. Для снятия резервной копии используется утилита pg_dump:
    generic_pgsql()
    {
    # $1 - server ip or dns-name
    # $2 - server ssh port
    # $3 - database name
    # $4 - backup filename

    touch "$4"
    chown $BACKUP_USER:$BACKUP_GROUP "$4"
    chmod 0600 "$4"
    ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o Port=$2 -i $SSH_PRIVKEY $BACKUP_USER@$1 "env PGPASSWORD=$PASSWORD pg_dump -U$BACKUP_USER $3 | bzip2" > "$4" 2>/dev/null

    clear_old "$4"
    if [ $? -ne 0 ]
    then
    echo "Backing up $3 database from system $1:$2 to file $4 failed"
    fi
    }
    Для снятия резервной копии нужно создать пользователя, указанного в переменной BACKUP_USER, и предоставить ему права доступа ко всем базам данных, резервные копии которых будет необходимо снимать. Войдя в систему под пользователем postgres, создаём пользователя для резервного копирования и вводим его пароль в процессе его создания:
    $ createuser -D -R -I -S -P rbackup
    Теперь нужно подключиться к каждой из баз данных при помощи команды psql -d база-данных и выполнить следующие запросы:
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO rbackup;
    GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO rbackup;

    Группировка действий по резервному копированию

    Для снятия всех необходимых резервных копий с одного сервера можно создавать функции следующего вида:
    backup_server()
    {
    linux_files 'server.domain.tld' 22 'server.tbz'

    generic_mysql 'server.domain.tld' 22 'mysql' 'server_mysql.sql.bz2'
    generic_mysql 'server.domain.tld' 22 'base' 'server_base.sql.bz2'

    generic_pgsql 'server.domain.tld' 22 'database' 'server_database.sql.bz2'
    }

    Выполнение резервного копирования

    Теперь в скрипте имеются функции для снятия резервных копий каждого сервера. Осталось только создать каталог для хранения резервных копий и выполнить резервное копирование. Для этого создадим каталог и выставим права доступа к нему:
    # mkdir /backups
    # chown rbackup:rbackup /backups
    # chmod u=rwx,g=rx,o= /backups
    Теперь впишем в скрипт команды перехода в каталог с резервными копиями, на всякий случай поменяем маску создаваемых файлов и последовательно вызовем функции резервного копирования всех требуемых серверов:
    cd /backups
    umask 0077

    backup_server1
    backup_server2

    Резервное копирование резервных копий

    Не стоит складывать все яйца в одну корзину. На случай, если с резервными копиями в основном месте хранения что-нибудь случится, можно выполнить резервное копирование резервных копий на другой сервер. Для этого на другом сервере создаётся аналогичный каталог, в который при помощи rsync синхронизируются изменения из каталога на основном сервере:
    /usr/bin/rsync -a --delete-after -e "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o Port=22 -i $SSH_PRIVKEY" /backups/ $BACKUP_USER@backup-server.domain.tld.:/backups/
    Один процесс rsync запускается локально, один процесс rsync запускается удалённо по SSH. Оба процесса обмениваются друг с другом информацией через SSH. Взаимодействуя друг с другом, они копируют недостающие файлы, обновляют фрагменты изменившихся файлов, удаляют файлы, ставшие не нужными.

    Отладочная информация

    Для того, чтобы иметь представление о времени начала и завершения тех или иных этапов резервного копирования, можно добавлять в функции или в тело скрипта отладочные сообщения с отметками времени. Например, в свой скрипт я вставил отладочные сообщения, фиксирующие моменты начала и завершения обновления основного архива и моменты начала и завершения синхронизации резервного архива:
    echo "BACKUP FINISHED: "`date "+%Y-%m-%d %H:%M:%S"`
    # тут резервное копирование серверов
    echo "BACKUP STARTED: "`date "+%Y-%m-%d %H:%M:%S"`

    echo "RSYNC STARTED: "`date "+%Y-%m-%d %H:%M:%S"`
    # тут вызов rsync
    echo "RSYNC FINISHED: "`date "+%Y-%m-%d %H:%M:%S"`

    Заключение

    Почти каждому системному администратору Unix когда-нибудь приходилось писать свой вариант такого скрипта. Буду рад, если вы поделитесь собственными советами и наработками. Возможно кто-то может поделиться соображениями, в каких случаях такой подход не годится, и какие решения лучше использовать взамен.
    • Sy chevron_right

      Скрипты управления списком IP-адресов в iptables/ipset и ipfw/table

      pubsub.slavino.sk / sysadmblog · Sunday, 1 December, 2019 - 08:00 edit · 3 minutes

    Года 4 назад на работе перевёл всех Zabbix-агентов в активный режим, т.к. этот режим должен быть эффективнее чем опрос обычных пассивных Zabbix-агентов. Для снятия данных с обычных Zabbix-агентов сервер Zabbix сам устанавливает подключение к Zabbix-агенту, запрашивает у него необходимые метрики, после чего отключается. Для этого сервер Zabbix используют процессы poller, каждый из которых бывает занят не только во время активных действий, но и во время ожидания данных от Zabbix-агента. Если же Zabbix-агент работает в активном режиме, то сервер Zabbix не предпринимает никаких активных действий, а ждёт действий со стороны агента. Активный Zabbix-агент подключается к серверу Zabbix, запрашивает у него список метрик, за которыми нужно наблюдать, и периодичность их контроля. После этого Zabbix-агент самостоятельно собирает данные с необходимой периодичностью и отправляет их на сервер Zabbix. В этом случае сервер Zabbix использует процессы trapper, которые работают только во время приёма уже готовых данных. На самом деле на фоне общей нагрузки снижение использования ресурсов оказалось совсем незаметным, но речь сейчас не об этом.

    После перевода Zabbix-агентов в активный режим появилась другая маета (-: или муда в терминологии кайдзен) - бывает нужно вносить в сетевой фильтр IP-адреса сети, в которых есть активные Zabbix-агенты. До поры до времени это требовалось делать очень редко. Потом сеть стала расти очень быстро и вносить новые IP-адреса и сети в сетевой фильтр стало нужно с завидной регулярностью. С одной стороны, чтобы сэкономить время, можно добавлять сразу целые сети. С другой стороны - в Zabbix нет никаких средств защиты от подделки данных: протокол позволяет запросить конфигурацию любого Zabbix-агента, указав его имя, и отправить в Zabbix данные от имени любого другого Zabbix-агента. Сервер Zabbix не имеет даже средств для определения конфликтующих Zabbix-агентов, которые работают на разных компьютерах, но имеют одно и то же сетевое имя, отправляя поочерёдно разные данные.

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

    Для Linux с его iptables и ipset получился такой скрипт под названием ipset_auto.sh, который можно поместить в планировщик задач cron:
    #!/bin/sh

    AWK="/usr/bin/awk"
    SORT="/usr/bin/sort"
    UNIQ="/usr/bin/uniq"
    IPSET="/sbin/ipset"
    XARGS="/usr/bin/xargs"

    update()
    {
    SET="$1"
    NEED_IPS="$2"

    CURRENT_IPS=`$IPSET list $SET | $AWK '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ { print $0; }'`

    DIFF_IPS=`(echo "$NEED_IPS" ; echo -n "$CURRENT_IPS") | $SORT | $UNIQ -u`
    ADD_IPS=`(echo "$NEED_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`
    DEL_IPS=`(echo "$CURRENT_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`

    if [ -n "$ADD_IPS" ]
    then
    echo "--- $SET add ---"
    echo "$ADD_IPS"
    echo "$ADD_IPS" | $XARGS -n1 $IPSET add $SET
    fi

    if [ -n "$DEL_IPS" ]
    then
    echo "--- $SET del ---"
    echo "$DEL_IPS"
    echo "$DEL_IPS" | $XARGS -n1 $IPSET del $SET
    fi
    }

    # ZABBIX

    MYSQL=`$AWK '/^DBUser=/ { split($0, a, "=");
    user = a[2]; }

    /^DBPassword=/ { split($0, a, "=");
    password = a[2]; }

    /^DBName=/ { split($0, a, "=");
    db = a[2]; }

    /^DBHost=/ { split($0, a, "=");
    host = a[2]; }

    END { if (user && password && host && db)
    print "/usr/bin/mysql --connect-timeout=5 -u" user " -p" password " -h" host " " db;
    else if (user && password && db)
    print "/usr/bin/mysql --connect-timeout=5 -u" user " -p" password " " db; }' /etc/zabbix/zabbix_server.conf`

    if [ -z "$MYSQL" ]
    then
    echo "MYSQL not defined"
    exit
    fi

    NEED_IPS=`$MYSQL -N <<END 2>/dev/null
    SELECT DISTINCT interface.ip
    FROM items
    JOIN hosts ON hosts.hostid = items.hostid
    AND hosts.status = 0
    AND hosts.proxy_hostid IS NULL
    JOIN interface ON interface.hostid = items.hostid
    AND interface.type = 1
    AND interface.ip <> '127.0.0.1'
    WHERE items.type = 7
    AND items.status = 0;
    END
    `
    ERROR=$?
    if [ $ERROR -ne 0 ]
    then
    echo "Failed to execute SQL-query"
    exit
    fi

    update "zabbix_auto" "$NEED_IPS"
    Для подключения к базе данных (в данном случае это MySQL, но переделка под другие СУБД тривиальна) скрипт использует настройки из файла конфигурации /etc/zabbix/zabbix_server.conf. Список требуемых IP-адресов в переменной NEED_IPS формируется SQL-запросом, который можно переработать под свои нужды. Например, у меня в скрипте есть ещё пара SQL-запросов, управляющих списками IP-адресов в множествах tftp_auto и ciu_auto. В последней строке скрипта функция update обновляет множество zabbix_auto так, чтобы в нём были только IP-адреса из переменной NEED_IPS.

    Для создания множества IP-адресов zabbix_auto в ipset можно воспользоваться командой:
    # ipset create zabbix_auto hash:ip
    Для создания правила в iptables, которое разрешит всем IP-адресам из множества zabbix_auto взаимодействовать с сервером Zabbix, можно воспользоваться командой:
    # iptables -A INPUT -p tcp -m set --match-set zabbix_auto src -m tcp --dport 10051 -j ACCEPT
    Аналогичный скрипт для ipfw/table называется ipfw_auto.sh и выглядит следующим образом:
    #!/bin/sh

    AWK="/usr/bin/awk"
    SED="/usr/bin/sed"
    SORT="/usr/bin/sort"
    UNIQ="/usr/bin/uniq"
    XARGS="/usr/bin/xargs"

    update()
    {
    TABLE="$1"
    NEED_IPS="$2"

    IPFW=`$AWK -v TABLE="$TABLE" '{ split($0, a, "=");
    if (a[1] == TABLE)
    {
    table = a[2];
    print "/sbin/ipfw table " a[2];
    }
    }' /etc/firewall.conf`

    if [ -z "$IPFW" ]
    then
    echo "IPFW not defined"
    exit
    fi

    CURRENT_IPS=`$IPFW list | $SED -e 's/\/32 0$//'`

    DIFF_IPS=`(echo "$NEED_IPS" ; echo -n "$CURRENT_IPS") | $SORT | $UNIQ -u`
    ADD_IPS=`(echo "$NEED_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`
    DEL_IPS=`(echo "$CURRENT_IPS" ; echo -n "$DIFF_IPS") | $SORT | $UNIQ -d`

    if [ -n "$ADD_IPS" ]
    then
    echo "--- $TABLE add ---"
    echo "$ADD_IPS"
    echo "$ADD_IPS" | $XARGS -n1 $IPFW add
    fi

    if [ -n "$DEL_IPS" ]
    then
    echo "--- $TABLE del ---"
    echo "$DEL_IPS"
    echo "$DEL_IPS" | $XARGS -n1 $IPFW delete
    fi
    }

    MYSQL=`$AWK '/^DBUser=/ { split($0, a, "=");
    user = a[2]; }

    /^DBPassword=/ { split($0, a, "=");
    password = a[2]; }

    /^DBName=/ { split($0, a, "=");
    db = a[2]; }

    /^DBHost=/ { split($0, a, "=");
    host = a[2]; }

    END { if (user && password && host && db)
    print "/usr/local/bin/mysql --connect-timeout=5 -u" user " -p" password " -h" host " " db;
    else if (user && password && db)
    print "/usr/local/bin/mysql --connect-timeout=5 -u" user " -p" password " " db; }' /usr/local/etc/zabbix34/zabbix_server.conf`

    if [ -z "$MYSQL" ]
    then
    echo "MYSQL not defined"
    exit
    fi

    # ZABBIX

    NEED_IPS=`$MYSQL -N <<END 2>/dev/null
    SELECT DISTINCT interface.ip
    FROM items
    JOIN hosts ON hosts.hostid = items.hostid
    AND hosts.status = 0
    AND hosts.proxy_hostid IS NULL
    JOIN interface ON interface.hostid = items.hostid
    AND interface.type = 1
    AND interface.ip <> '127.0.0.1'
    WHERE items.type = 7
    AND items.status = 0;
    END
    `
    ERROR=$?
    if [ $ERROR -ne 0 ]
    then
    echo "Failed to execute SQL-query"
    exit
    fi

    update "table_zabbix_auto" "$NEED_IPS"
    Особенность этого скрипта заключается в том, что в ipfw таблицы не имеют имён, а нумеруются. Номер таблицы выясняется через файл /etc/firewall.conf, в котором переменной с именем таблицы присваивается соответствующий номер. Например, для таблицы table_ssh номер задаётся следующим образом:
    table_ssh=100

    Подробнее о настройке ipfw/table можно прочитать в одной из моих прошлых заметок: Настройка ipfw во FreeBSD .

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

    FreeBSD на работе постепенно заменяем на Debian, поэтому скрипт ipfw_auto.sh скоро станет мне не нужным. Что касается Debian, то netfilter/iptables в Debian Buster уже заменён на nftables/nft. Пока что утилита iptables никуда не делась и умеет работать с nftables, но в будущем скрипт ipset_auto.sh тоже утратит актуальность и потребует переработки. Оба скрипта, однако, пока что могут пригодиться кому-нибудь ещё, поэтому решил поделиться ими.
    • Sy chevron_right

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

      pubsub.slavino.sk / sysadmblog · Sunday, 24 November, 2019 - 08:00

    Ещё одна микрозаметка. Не так давно я уже писал об 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]}
    • Sy chevron_right

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

      pubsub.slavino.sk / sysadmblog · Sunday, 17 November, 2019 - 08:00 edit

    Эта очень короткая заметка представляет собой дополнение к одной из моих прошлых заметок: Использование 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)