27 декабря 2013

Postfix - отбрасывание почты, адресованной любому домену, кроме явно разрешенных

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

Эта мера достаточно эффективна, но периодически приходят уведомления о недоставленных письмах. Когда их 1-2 в день с этим можно спокойно жить, но когда их становится 30-50 штук, то читать их нет смысла, а каждый раз удалять их из почтового ящика postmaster'а лениво.

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

Для реализации мне потребовалось создать файл карты /etc/postfix/transport, который выглядит примерно так:

example.local  :
example.com    :
example.org    :
*              discard: Not allowed destination - silently discarded

создать хешированый вариант карты

# postmap hash:/etc/postfix/transport

добавить в /etc/postfix/main.cf

transport_maps = hash:/etc/postfix/transport

и перечитать конфигурацию postfix

# service postfix reload

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

Dec 27 16:13:02 server postfix/discard[2717]: 55B574E300: to=, relay=none, delay=0.06, delays=0.05/0/0/0, dsn=2.0.0, status=sent (Not allowed destination - message silently discarded)

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

26 декабря 2013

Настройка сетевого принтера HP 1022n в Linux

В офис вернулся наш старенький принтер HP1022n, который помимо прочего является сетевым и работает через ethernet. Чтобы подключить его в Debian Wheezy нужно:
  • установить пакет printer-driver-foo2zjs;
  • зайти в интерфейс CUPS (под пользователем root)
  • добавить новый принтер (Администрирование / Принтеры / Добавить принтер)
  • в группе "Другие сетевые принтеры" выбрать тип "Протокол интернет-печати (ipp)"
  • в поле "Подключение" указать socket://192.168.1.100:9100/ (192.168.1.100 - IP адрес принтера в сети)
  • указать в поле "Название" я указал hp1022n, поля "Описание" и "Расположение" можно не указывать
  • в следующем диалоге нужно указать производителя "HP" и выбрать подходящий драйвер CUPS "HP LaserJet 1022n Foomatic/foo2zjs-z1 (recommended)"
  • в диалоге конфигурации параметров печати по-умолчанию можно ничего не менять
  • после завершения можно попробовать распечатать пробную страницу (Обслуживание / Печать пробной страницы) 
P.S. Если добавить своего пользователя в группу lpadmin, то в интерфейсе CUPS можно авторизоваться своим пользователем и паролем. Если вы используете что-то отличное от Debian, то возможно вам нужно добавить своего пользователя в другую группу. Чтобы узнать нужную группу выполните

grep SystemGroup /etc/cups/cupsd.conf

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

06 декабря 2013

Последнее пристанище старых игрушек

Зашел на http://ftp.by.debian.org/debian-cd/ чтобы скачать ISO для debian squeeze netinst i386, но не нашел ничего кроме актуального wheezy. Похоже почистили все зеркала, поэтому взять нужный файл можно на http://cdimage.debian.org/cdimage/archive/.

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

04 декабря 2013

Особенности работы /usr/sbin/sendmail в разных MTA

Сегодня дебажили отправку почты в PHP приложении и наткнулись на интересную особенность эмуляции ключей /usr/sbin/sendmail в различных MTA.

Предыстория - рассматриваемое приложение отправляет письмо пользователю (user@example.net), делает Bcc: bcccopy@example.com (для внутренних нужд) и ставит Return-Path: sales@example.com. Если не лезть в дебри ООП и наслоения классов, то нужный код на PHP выглядит примерно так:

$email_to = 'user@example.net';
$email_subject = 'subject';
$email_body = 'email body';
$email_additional_headers = 'Bcc: bcccopy@example.com';
$email_additional_params = '-f sales@example.com';
mail($email_to, $email_subject, $email_body, $email_additional_headers, $email_additional_params);

Но при отправке письма из приложения приходило 3 одинаковых письма на user@example.net, на bcccopy@example.com и на sales@example.com, но при этом Return-Path: не выставлялся и выглядел примерно так: Return-Path: <www-data@www.example.com>

Нужное значание Return-Path может не выставляться, если почтовый сервер не считает пользователя доверенным, либо в вызове /usr/sbin/sendmail нет ключа -f. Проверить просто

# su - www-data
$ cat > /tmp/email.txt <_EOF_
Bcc: <bcccopy@example.com>
From: "Sales" <sales@example.com>
To: "User Full Name" <user@example.net>
Subject: subject

email body
_EOF_
$ cat /tmp/email.txt | /usr/sbin/sendmail -t -i -f sales@example.com

После выполнения пришли два письма - на user@example.net и на bcccopy@example.com, Return-Path: при этом выставлен корректно. Следовательно проблема лежит выше MTA и нужно дебажить PHP приложение.

Когда мне нужно проверить взаимодействие PHP скрипта и системы я пользуюсь sleep() и strace. Для этого в нужный PHP скрипт в самое начало вставляется sleep(60); после чего выполняется нужное действие. При этом у меня есть 60 секунд, чтобы посмотреть в http://www.example.com/server-status PID нужного мне запроса и подключиться к процессу через strace.

# strace -f -o /tmp/php-trace.log -s 8192 -p <PID>

По истечению 60 секунд приложение начнет выполнять вызовы системы, которые будут записаны в лог файле /tmp/php-trace.log. Поскольку я дебажу отправку почты через mail(), то меня интересует вызов execve()

$ grep execve /tmp/php-trace.log 
3253  execve("/bin/sh", ["sh", "-c", "/usr/sbin/sendmail -t -i  sales@example.com"], [/* 9 vars */] <unfinished ...>
3253  <... execve resumed> )            = 0
3254  execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-t", "-i", "sales@example.com"], [/* 9 vars */]) = 0
3255  execve("/usr/sbin/postdrop", ["/usr/sbin/postdrop", "-r"], [/* 2 vars */]) = 0

Вот теперь видно почему отправляется три письма вместо двух - где-то в приложении ошибка и перед sales@example.com не добавляется параметр -f. А раз sales@example.com указан без параметра, то он трактуется как еще один адрес назначения.

Но остается еще один вопрос - почему на некоторых серверах отправляется три письма, а на некоторых только два (правильный Return-Path не выставляется на любом сервере). Во всех случаях код приложения одинаковый и содержит ошибку.

На сервере, где пытались воспроизвести ошибку с отправкой трех писем вместо двух установлен WHM/cPanel и MTA там exim4. Поскольку код одинаковый, значит и функция mail() вызывается с одинаковыми параметрами, а значит и вызов /usr/sbin/sendmail должен быть одинаковый. На всякий случай сравниваю значение переменной sendmail_path в выводе phpinfo() на разных серверах - везде стоит одинаковое значение "/usr/sbin/sendmail -t -i". Но почему же тогда отличается поведение!

Смотрю дальше - на сервере, где отправляется только два письма стоит Exim4, а на остальных, где отправляется три письма, - Postfix. Сравниваю описание ключей -t и -i в man sendmail от этих MTA (взято из CentOS 6.5).

Exim4

-i        This option, which has the same effect as -oi, specifies that
          a dot on a line by itself should not terminate  an  incoming,
          non-SMTP message. I can find no documentation for this option
          in Solaris 2.4 Sendmail, but the mailx command in Solaris 2.4
          uses it. See also -ti.

-t        When  Exim is receiving a locally-generated, non-SMTP message
          on its standard input, the -t option causes the recipients of
          the message to be obtained from the To:, Cc:, and Bcc: header
          lines in the message instead of from the  command  arguments.
          The  addresses are extracted before any rewriting takes place
          and the Bcc: header line, if present, is then removed.

          If the command has any arguments, they specify  addresses  to
          which  the message is not to be delivered. That is, the argu-
          ment addresses are removed from the recipients list  obtained
          from  the  headers.  This  is  compatible with Smail 3 and in
          accordance with the documented behaviour of several  versions
          of Sendmail, as described in man pages on a number of operat-
          ing systems (e.g.  Solaris 8, IRIX 6.5, HP-UX  11).  However,
          some  versions  of  Sendmail  add argument addresses to those
          obtained from the headers, and  the  O'Reilly  Sendmail  book
          documents  it  that  way.  Exim  can  be made to add argument
          addresses instead of subtracting them by setting  the  option
          extract_addresses_remove_arguments false.

          If  there  are  any Resent- header lines in the message, Exim
          extracts recipients  from  all  Resent-To:,  Resent-Cc:,  and
          Resent-Bcc:  header lines instead of from To:, Cc:, and Bcc:.
          This is for  compatibility  with  Sendmail  and  other  MTAs.
          (Prior  to release 4.20, Exim gave an error if -t was used in
          conjunction with Resent- header lines.)

          RFC 2822 talks about different sets of Resent-  header  lines
          (for  when  a  message is resent several times). The RFC also
          specifies that they should be added at the front of the  mes-
          sage,  and  separated  by  Received:  lines. It is not at all
          clear how -t should operate in the present of multiple  sets,
          nor indeed exactly what constitutes a "set".  In practice, it
          seems that MUAs do not follow the RFC. The Resent- lines  are
          often  added  at  the  end of the header, and if a message is
          resent more than once, it is common for the original  set  of
          Resent-  headers to be renamed as X-Resent- when a new set is
          added. This removes any possible ambiguity.

Postfix

-i     When  reading  a message from standard input, don.t treat a line
       with only a . character as the end of input.

-t     Extract recipients from message headers. These are added to  any
       recipients specified on the command line.

       With Postfix versions prior to 2.1, this option requires that no
       recipient addresses are specified on the command line.

Sendmail

-i     Ignore  dots  alone on lines by themselves in incoming messages.
       This should be set if you are reading data from a file.

-t     Read message for recipients.  To:, Cc:, and Bcc: lines  will  be
       scanned  for recipient addresses.  The Bcc: line will be deleted
       before transmission.

Смысл опции -i примерно одинаков у всех MTA (хотя значение ignore у sendmail, еще нужно проверить - возможно он вовсе выкидывает строку, содержащую только символ точки). А вот после прочтения разделов про опцию -t все становится на свои места. На серверах где стоит exim4 используется отличное от sendmail и postfix поведение, когда получатели, указанные в командной строке, исключаются из итогового списка получателей письма.

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

13 ноября 2013

Срочно выйти из сна - headshot.php

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

Этот скрипт, будучи запущенным из браузера на сервере должен сделать эту работу

$ cat headshot.php
<?php
system('PHP_PPID=`ps -p $$ -o ppid=`; PHP_NAME=`ps -p $PHP_PPID -o comm=`; pkill -u `id -un` $PHP_NAME');

Почему не просто pkill -u `id -un`? Потому что имени пользователя под которым работают скрипты на сервере, может быть запущено много чего другого, которое не стоит убивать.

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

Протестируйте свою WHM/cPanel после обновления до 11.40

На прошлой неделе вышло обновление WHM/cPanel до версии 11.40. С этим обновлением связаны две проблемы, которые мне пришлось решать в понедельник, возможно сей пост кому-то сэкономит время.

У нас есть самописный плагин для cPanel, который автоматизирует установку и конфигурирование сайтов на основе magento для отдела QA. И после обновления этот плагин прекратил нормально работать.

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

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

Попробовал пересобрать конфигурацию веб-сервера

# /scripts/rebuildhttpdconf

Но в ответ выдает ругань вида

[Mon Nov 11 10:31:36 2013] [crit] (EAI 9)Address family for hostname not supported: alloc_listener: failed to set up sockaddr for ::

Configuration problem detected on line 170 of file /usr/local/apache/conf/httpd.conf.work.PsABU7IkikfQtoi0:     Listen setup failed

        --- /usr/local/apache/conf/httpd.conf.work.PsABU7IkikfQtoi0 ---

Ругается на ipv6 опцию и конфиг сервера не перезагружается. В новом релизе появилась поддержка IPv6 и следовательно, где-то нужно дать пинка, чтобы заработало. Немного пробежавшись по форумам cpanel нашел, что нужно просто пересобрать текущую конфигурацию в EasyApache.

# /scripts/easyapache

После завершения сборки все начинает работать.

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.

11 ноября 2013

Уменьшение разряда батареи при использовании Suspend-to-RAM

Мне нравится использовать suspend to ram (STR или S3 в терминологии ACPI) на ноутбуке. Помимо быстрого выхода из режима сна это самый надежный способ заблокировать экран и клавиатуру ноутбука от детей. Есть лишь один недостаток - постоянный разряд батареи, связанный с поддержанием работы оперативной памяти. Если батарея разрядится, то все несохраненные данные будут потеряны.

Для защиты от потери данных в режиме STR в linux есть режим гибридного сна (suspend-hybrid) - в этом режиме состояние памяти сохраняется на диск, как при hibernate (suspend to disk или S4 в терминологии ACPI), но вместо отключения питания система переходит в состояние S3 или suspend to ram. В этом случае при достаточном заряде батареи система мгновенно просыпается как из suspend to ram, а если батарея разрядится, то как из suspend to disk. Все хорошо, но одно НО - батарея все равно разряжается, когда ноутбук находится в режиме такого сна. Это может быть критично в поездке или когда ноутбук длительное время не используется. Поскольку сохранность данных и постоянная готовность ноутбука к работе для меня важнее скорости загрузки, то я перешел на использование hibernate и пользовался им в течении нескольких лет.

Пару дней назад я натолкнулся на элегантное решение проблемы - переключение из режима S3 в S4 без участия пользователя. Кажется я такое уже наблюдал в Windows 7, но не придавал этому значения. Оказывается все очень просто. В процессе подготовки к suspend to ram менеждер питания выполняет установку системного таймера (будильника в BIOS), который включит систему через определенное время. Затем система выполняет обычную подготовку и переходит в режим S3. Далее два варианта:
  • если пользователь включит систему до срабатывания системного будильника, то система пробуждается из режима S3 и переходит в режим нормальной работы;
  • если будильник срабатывает в режиме сна, то система загружается, определяет что пробуждение не было инициировано пользователем (система включилась по событию таймера в BIOS) и сразу же выполняется переход в режим S4 (suspend to disk).
Интервал, после которого система переходит из одного состояния в другое настраивается. Этой схемой я пользуюсь уже несколько дней и проблем пока не замечал.

Вы можете следить за обновлениями блога с помощью Atom/RSS и Telegram.