Смена кодировки файлов в utf8

В поддержке legacy-кода и обновлении скритов нередко возникает задача их перекодирования в utf-8. Это хорошо выполняется стандарной утилитой iconv, вот её использование для одного файла:

iconv -f cp1251 -t utf8 orig-in-cp1251.php -o out-in-utf8.php

При этом не удастся указать одинаковое входное и выходное названия: iconv начнёт писать до получения входа и получится файл нулевой длины. В этом случае станартный вывод полезно «замочить в губке» с помощью sponge. Сделаем однострочный скрипт, который делает всё сразу, он понадобится далее (назовём его to-utf8.sh):

#!/bin/sh
/usr/bin/iconv -f cp1251 -t utf8 "$1" | sponge "$1"

В качестве единственного аргумента он берет имя файла и рекодит его полностью.

Обычно требуется сконвертить все файлы определённого расширения в целой директории. Подключаем ещё find:

find /here-is-my-files/ -type f -iname "*.js" -exec to-utf8.sh "{}" \;

На часть файлов iconv может ругнуться в STDERR и эти файлы целесообразно проверить/перекодировать вручную.

Ещё более сложный (и достаточно частый) случай, когда есть файлы в разных кодировках и их необходимо перевести в одну. iconv в этом случае может сконвертировать ещё раз и испортить файл. То есть, нужно распознать исходную кодировку и рекодить только «не те» файлы.

Тогда помогает самый лучший вариант: утилиты enca/enconv. Из них первая только распознаёт кодировку, а вторая ещё и перекодирует.Интерфейс enconv до безобразия простой: в случае одного параметра она перекодирует файл из исходной (ЛЮБОЙ распознанной!) кодировки в ту, что указана в locale. Кодировку можно указать явно, тогда вся задача решается одной строкой

find /here-is-my-files/ -type f -iname "*.js" -exec enca "{}" \;

Для улучшения точности язык также можно «подсказать», с помощью параметра (-L ru).

Последний, enca-способ, похоже самый оптимальный для большинства задач.

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

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

Первое, что стоит сделать в типовой конфигурации сервера — скрыть внутреннюю структуру сети в отправляемых сообщениях, поскольку exim оставляет в заголовках оригинального отправителя. В типичном случае получатель увидит примерно следующее:

Received: from srv12 ([10.0.0.12])
	by mymail.example.com with esmtp (Exim 4.84)
	(envelope-from )
	id 1cPEo3-0003N7-Jj
	for user@example.com; Thu, 05 Jan 2013 20:38:39 +0000

или следующее

x-originating-ip: [10.54.14.36]

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

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

X-PHP-Originating-Script: 0:myscript.php

Поэтому, также вырезаем заголовок X-PHP-Originating-Script.

Если же почтовый сервер обслуживает офис, то из заголовков можно узнать используемый почтовый клиент. Например,

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:45.0)
 Gecko/20100101 Thunderbird/45.5.1

или

X-Mailer: Microsoft Outlook 14.0

Обычно, эта информация вполне безопасна. Информацию о клиенте оставляют многие публичные почтовые сервисы. Однако, её тоже не всегда имеет смысл раскрывать. Особенно, если это веб-клиент или внутренняя CRM. Таким образом, можно ещё срезать заголовок User-Agent.

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

system_filter = /etc/exim4/filter

А в самом /etc/exim4/filter прописать вырезаемые заголовки. Например:

headers remove X-PHP-Originating-Script
headers remove Received

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

Отключение рекламы в Skype

Последние версии скайпа не только очень тяжёлые и тормозные, но и качают рекламу. Наиболее простой способ устранить это — отключить корректное разрешение имён хостов, к которым скайп обращается за рекламой. Список таких хостов легко гуглится, чтобы отключить обращение к ним прописываем в местном хостс (находится в %SystemRoot%\system32\drivers\etc\hosts) следующие строки:

127.0.0.1 rad.msn.com
127.0.0.1 apps.skype.com
127.0.0.1 api.skype.com
127.0.0.1 static.skypeassets.com
127.0.0.1 adriver.ru
127.0.0.1 devads.skypeassets.net
127.0.0.1 devapps.skype.net
127.0.0.1 qawww.skypeassets.net
127.0.0.1 qaapi.skype.net
127.0.0.1 preads.skypeassets.net
127.0.0.1 preapps.skype.net
127.0.0.1 serving.plexop.net
127.0.0.1 preg.bforex.com
127.0.0.1 ads1.msads.net
127.0.0.1 flex.msn.com

Аналогичным образом можно отключить рекламу и сбор данных в других программах.

Для скайпа также очень рекомендуется запускать его под другим пользователем для избежания сбора данных.

Копирование с сохранением жёстких ссылок

Ранее я описывал отличную схему инкрементных резервных копий, основанную на использовании жёстких ссылок. Схема позволяет хорошо сохранять место на разделе для бэкапов и на уровне приложений резервные копии видны полностью. Обратной стороной такого удобства является сложность переноса таких бэкапов с одного раздел на другой в случаях, когда это потребуется, например, при физической смене жёстких дисков или рейда для резервирования. Стандартные утилиты вроде cp воспринимают всю директорию с бэкапами как «нормальную» директорию и копируют полностью, что в результате даёт копию большого, примерно n*[размер одного бэкапа] размера. Кроме объёма, такое копирование занимает огромное количество времени.

Как скопировать быстро и сохранением симлинков? Задача, оказывается, хорошо решается всё той же проверенной утилитой rsync, которая может работать как локально, так и удалённо. При запуске ее с ключами:

rsync -aH /src-path /dst-path

она выполняет в точности, что и требуется.

Наиболее оптимальным набором параметров для локальных копий показал себя следующий

rsync -aH --delete --numeric-ids /src-path /dst-path

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

Естественно, утилита может копировать и по сети, что делает способ ещё более удобным, особенно со сжатием (-z).

nginx HTTP/2

Отлично! В энжинксе сделали полноценную поддержку http2, а если точнее, то заменили SPDY на HTTP/2. Поддержка есть в последних версиях (>=1.9.5), проверяем

$ nginx -V

и если в ответе есть --with-http_v2_module, то можно включать. Включается, как и все в nginx, очень просто — дописываем в конфигурации SSL:

listen 443 ssl http2;

или меняем spdy, если оно там стояло ранее.

PS: Если поддержка https на вашем сайте не настроена, то скорее настраиваем.

Копирование пользователей и прав MySQL

В ряде случаев бывает необходимо скопировать пользователей и их права (привилегии) на другой сервер БД. Типичная ситуация, когда это требуется — подготовка чистого сервера БД в качестве реплики, при этом пользователи берутся с мастера. Гугл предлагает готовым решением Percona Toolkit, однако, это дополнительно устанавливаемый софт, попробуем обойтись без него. Для этого скрипт, который вытягивает пользователей из базы-источника и формирует sql-файл для импорта.

#!/bin/sh
mysql -e"SELECT DISTINCT CONCAT('SHOW GRANTS FOR ', '\'', user, '\'@\'', host, '\';') AS QUERY FROM mysql.user" | sed '1d' | while read query; do
	mysql -e"$query" | sed '1d' | awk '{print $0";"}'
done
echo -e "FLUSH PRIVILEGES;\n";

На выходе получится sql-код для исполнения на слейве. Естественно, по файлу стоит пройтись вручную и удалить пользователей, которых добавлять не надо.

Confluence + nginx

Стандартная установка Atlassian Confluence вешает его по умолчанию на 8090 http-порт. Софтину в целях совместимости и безопасности целесообразно ставить в виртуалку и обращаться к ней по http, при этом сама виртуалка может находится в недоступной извне локальной сети. Поэтому, возникает задача проксировать траффик 8090 порта конфлюенса вовне. Ниже описывается, как это меньшими средствами сделать с помощью nginx.

В документации Atlassian рассматривается вариант проксирования траффика, в том числе энжинксом. Предлагаемый в документации способ предполагает правки в xml-конфиге самого Confluence, что есть не лучшее решение. Можно обойтись настройкой только nginx, без правок оригинального конфига конфлюенса, что обычно намного разумнее. Вот пример проксирования:

	location / {
		proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Server $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $remote_addr;
		port_in_redirect off;
		proxy_pass http://confluence-vhost:8090;
		proxy_redirect http://confluence-vhost:8090/ /;
		proxy_connect_timeout 600;
		#proxy_redirect off;
	}

Аналогичным способом можно завернуть траффик в SSL и отдавать его через https, что существенно повышает безопасность работы.

/etc/network/if-up.d/ в CentOS

На Debian-based операционках есть очень удобная директория /etc/network/if-up.d/, в которой находятся скрипты, запускаемые системой при интерфейса. В CentOS и других RHEL-based системах подход директорий conf.d не такой популярный и запуск скриптов после поднятия интерфейса реализован по другом. А именно, внимательным глазом в конце скрипта /etc/sysconfig/network-scripts/ifup-post можно обнаружить строки:

if [ -x /sbin/ifup-local ]; then
     /sbin/ifup-local ${DEVICE}
fi

Таким образом, если создать файл /sbin/ifup-local, в него можно прописать собственный firewall или что-то иное, необходимое в сети. Можно (и обычно удобнее) пойти и дальше, и запускать из этого скрипта готовые стандартные скрипты:

test -x /usr/local/bin/fw && /usr/local/bin/fw
test -x /usr/local/bin/fw6 && /usr/local/bin/fw6

Так можно реализовать в центе модульность, которой изначально нет.

Загрузка на разваленном mdadm-RAID массиве

Типичным решением для создания отказоустойчивых систем является установка корня Linux на зеркальный RAID. Это решение позволяет защитить как данные на корневом разделе, так и возможность загрузки в случае отказа одного из дисков.

В случае использования для этой цели mdadm настройка массива производится ещё при установке. К сожалению, установщик Debian ставит загрузчик grub только на первый (hd0) из физических дисков, второй при этом остается незагрузочных (хотя и зеркалирует данные). Если разметка диска делается через GPT, то стандартная команда grub-install /dev/sdb хотя и прописывает загрузчик на второй диск, но загрузочным разделом все равно указывает hd0, а значит система без hd0 не грузится и задача резервирования загрузчика проваливается.

Для резервирования нужно на каждом из физических дисков прописать в качестве загрузочного раздела — раздел с того же физического диска. Штатным путем в grub-install указать загрузочный раздел не получается, но можно пойти следующим обходным путем. Изначально по установке ОС имеет корректный загрузчик на /dev/sda (hd0), и этот загрузчик указывает на тот же hd0. Сделаем то же для sdb. Это можно сделать «обманув» grub-install. А именно:

  1. Создаем с помощью grub-mkdevicemap файл /boot/grub/device.map.
  2. Подменяем в device.map диски (hd0) и (hd1) местами так, чтобы (hd0) теперь указывал на /dev/sdb.
  3. Делаем grub-install /dev/sdb.
  4. Удаляем device.map (или, в качестве альтернативы, прописываем в нем (hd0) на оба диска sda и sdb).

Всё! В результате при отказе любого из загрузочных sda или sdb система загрузится с массивом в degraded-режиме, но полностью сохранит работоспособность.

hubbed_hosts в exim4

Иногда настраиваемый сервер имеет FQDN имя, которое им обрабатываться не должно, но именоваться он должен именно так. В тривиальной настройке exim будет трактовать почту на этот домен локальной и складывать в /var/mail или подобную директорию для локальной почты. Чтобы домен и соответствующая почта не считалась локальной можно это явно прописать в /etc/exim4/hubbed_hosts, например

example.com:  mx.example.com

Ошибка Munin INDIVIDUAL BUFFER POOL INFO

При обновлении MySQL от версии 5.6 его показатели не читаются мунином. В логах появляется ошибка:

Unknown section: INDIVIDUAL BUFFER POOL INFO at /etc/munin/plugins/mysql_innodb_rows line 1098.

Ошибка возникает в результате того, что мунин не распознает новую секцию (INDIVIDUAL BUFFER POOL INFO) в ответе СУБД. Наикратчайший (однако, не лучший) путь вылечить — отключить эту секцию. Для этого в скрипте, который парсит параметры БД /usr/share/munin/plugins/mysql_, находим немного перед 1098 строчкой определение секции

my %section_map = (

и добавляем в самом начале этой секции

'INDIVIDUAL BUFFER POOL INFO' => \&skip,

Проверить корректность запуска можно munin-run на любом из собирающих скриптов. Например:

cd /etc/munin/plugins
munin-run mysql_innodb_rows

Reboot

Иногда Linux отказывается перезагружаться стандартными коммандами. Тогда бывает полезным следующий низкоуровневый способ с помощью механизма SysRq. Для ребута комманда такая:

echo b >/proc/sysrq-trigger

Если команда не проходит сразу, то возможность использования Magic SysRq надо включить

echo 1 > /proc/sys/kernel/sysrq

Также есть полезные для размонтирования ФС (u) и синхронизации дисков (s), которые можно сделать до ребута.

Конфигурация пакетов через dialog

В минимальной установке дебиана конфигурация пакетов после установки происходит с помощью текстового интерфейса, что не всегда удобно. Перевод в «менюподобный» интерфейс (с использованием libncurses) происходит доустановкой пакета dialog:

apt-get install dialog

Оригинальные JDK и JRE в Debian

Есть ряд ПО под java, которое лучше работает на оригинальной оракловской жаве. Дебиан содержит открытый пакет openjdk, однако его недостаточно. А оригинальные оракловские бинарники Java отсутствуют в дебиановских репозитариях вследствие строгой лицензионной политики. Поставим вручную.

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

mkdir /opt/jdk
tar -zxf jdk-8u5-linux-x64.tar.gz -C /opt/jdk

Проверяем, что все распаковалось, а далее делаем симлинк на исполняемые файлы

update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_66/bin/java 100
update-alternatives --install /usr/bin/javaс javaс /opt/jdk/jdk1.8.0_66/bin/javaс 100

Проверяем, что все поставилось корректно

update-alternatives --display java
update-alternatives --display javaс

Или альтернативный способ

java -version
javac -version

Done!

Из быстрых и чистых способов поставить жаву, это скорее всего самый короткий.

No irq handler for vector (irq -1)

После обновления линуксового ядра с очень старого дистрибутива Debian на 3.16.xxxx в jessie иногда вылазит следующая ошибка

No irq handler for vector (irq -1)

Решается использованием опций pci=nomsi,noaer при загрузке ядра. Чтобы опции использовались постоянно целесообразно их прописать в /etc/default/grub:

GRUB_CMDLINE_LINUX="pci=nomsi,noaer"

Без правки системы загрузки также помогает остановка демона irqbalance

/etc/init.d/irqbalance stop

С учётом, что сервер относится к категории Legacy и на нём старый софт, а перезагрузки его в лучшем случае происходят раз в год, второй способ в этом случае предпочтителен.

grep без комментариев и пустых строк

Часто нужно посмотреть «активные» строчки в конфиге, т.е. исключить из него комментарии и пустые. Это просто делается грепом

grep -v '^$\|^\s*\#' some-config.conf

Или более коротко, не учитывая комментариев, начинающихся не с начала строки:

grep -v '^$\|^#' some-config.conf

dont_blame_nrpe

Оказывается, в разработке и поддержке серьезных открытых проектов тоже встречаются мудаки.

Кто обновляет nagios-nrpe-server из wheezy в jessie и использует в конфигах опцию dont_blame_nrpe — опция работать не будет. Чтобы она работала нужно пересобрать пакет.