13 февраля 2014

Резервное копирование домашнего медиацентра

Сегодня наводил окончательный лоск в настройках домашнего медиацентра (Raspberry PI + OpenELEC) после переноса его директории /storage с SD карты на сервер NFS (SD карта используется только для загрузки, а все файлы конфигурации и пользовательских данных хранятся на сервере и доступны через NFS).

Когда с настройками было покончено я сделал резервную копию средствами OpenELEC, но вот что интересно - резервная копия выполняется без завершения работы XBMC, а значит часть файлов копируется в процессе использования.

Узнать список файлов, которые в данный момент используются, можно на самом медиацентре

OpenELEC:~ # lsof | grep /storage
866 /usr/sbin/connmand /storage/.cache/connman/ethernet_b827eb9ace35_cable/data
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/temp/xbmc.log
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Addons15.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Textures13.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/TV22.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Epg7.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Addons15.db

Открытые файлы *.db это базы SQLite, которые могут быть в промежуточном состоянии на момент создания резервной копии.

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

После изучения системы инициализации в OpenELEC оказалось, что самым простым способом достичь желаемого - выполнить перезагрузку всего медиацентра. В процессе завершения работы системы выполняется скрипт /storage/.config/shutdown.sh, который запускается после выключения XBMC, но до перезагрузки всей системы.

Собственно скрипт

if [ "`date +'%H'`" != "05" ]; then
 echo "Seems triggered not by cron job, simply exiting"
 exit 0
fi

STAMP=`date +'%Y%m%d%H%M%S'`

if [ ! -d /storage/backup ]; then
 mkdir /storage/backup
fi

echo "Settings backup..."
tar -cf /storage/backup/${STAMP}.tar /storage/.cache /storage/.config /storage/.xbmc

if [ "`date +'%u'`" == "7" ]; then
 echo "Flash backup..."
 tar -cf /storage/backup/${STAMP}_flash.tar /flash
fi

echo "Backup finished, syncing..."
sync
sleep 30s

Скрипт проверяет в котором часу он запущен и если сейчас не 5 часов утра, то создание резервной копии отменяется - скорее всего это перезагрузка по требованию пользователя, а не задания крона. Далее создается TAR-архив, содержащий конфигурацию OpenELEC и XBMC. Также если скрипт запущен в воскресенье, то дополнительно создается резервная копия всей системы (защита от неудачного обновления самого OpenELEC или выхода из строя SD карточки). Перед завершением скрипта выполняется сброс буферов и выжидается 30 секунд (на всякий случай).

Задание крона выглядит так

# crontab -u root -l

# Reboot to create semi-offline backup
10 5 * * * /usr/bin/xbmc-send --action="XBMC.Reboot"

Согласно этому заданию в каждый день в 5:10 утра выполняется команда перезагрузки средствами XBMC, что гарантирует корректное завершение работы.

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

Настройка PHP 5.3 для работы в FastCGI режиме в Debian Squeeze

Процесс установки описан для Debian Squeeze и в других дистрибутивах или релизах Debian процесс может незначительно отличаться. Устанавливаем необходимые пакеты

# aptitude install spawn-fcgi php5-cli php5-cgi php5-curl php5-gd php5-imap php5-mcrypt php5-mysql

Далее нужно создать скрипт инициализации FastCGI менеджера (spawn-fcgi)

# vim /etc/init.d/php5-fcgi

#! /bin/sh
### BEGIN INIT INFO
# Provides:          php5-fcgi
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: PHP FastCGI
# Description:       PHP FastCGI
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="PHP FastCGI"
NAME=php5-fcgi
DAEMON=/usr/bin/spawn-fcgi
FCGI_SOCKET=/var/run/php5-fcgi.sock
FCGI_USER=www-data
FCGI_MODE=0600
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
DAEMON_ARGS="-s $FCGI_SOCKET -M $FCGI_MODE -u $FCGI_USER -U $FCGI_USER -P $PIDFILE -- /usr/bin/php5-cgi"

[ -x "$DAEMON" ] || exit 0

[ -r /etc/default/$NAME ] && . /etc/default/$NAME

. /lib/init/vars.sh

. /lib/lsb/init-functions

do_start()
{
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec /usr/bin/php5-cgi --test >/dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
                $DAEMON_ARGS >/dev/null \
                || return 2
}

do_stop()
{
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
        RETVAL="$?"
        rm -f $PIDFILE $FCGI_SOCKET
        return "$RETVAL"
}


case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  status)
       status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
       ;;
  restart|force-reload)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
      0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
      *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
        exit 3
        ;;
esac

:

Для определения параметров работы FastCGI нужно создать конфигурацию по-умолчанию

# vim /etc/default/php5-fcgi

export PHP_FCGI_CHILDREN=10
export PHP_FCGI_MAX_REQUESTS=1000

Запускаем сервис php5-fcgi и добавляем его в автозапуск

# invoke-rc.d php5-fcgi start
# update-rc.d php5-fcgi defaults

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

11 февраля 2014

Использование Gmail в качестве почтового шлюза для Postfix

Если ваш IP адрес "проклят", то при попытке доставить почту на сервера Gmail вы увидите что-то наподобие этого

status=bounced (host gmail-smtp-in.l.google.com[173.194.70.26] said: 550-5.7.1 [87.xx.xx.xx] The IP you're using to send mail is not authorized to 550-5.7.1 send email directly to our servers. Please use the SMTP relay at your 550-5.7.1 service provider instead. Learn more at 550 5.7.1 http://support.google.com/mail/bin/answer.py?answer=10336 g47si26393674eev.48 - gsmtp (in reply to end of DATA command))

Выход прост -  если вам не нужна возможность доставлять почту от разных людей (в моем случае письма только от систем мониторинга), то можно создать почтовый ящик Gmail и слать почту через него. В этом случае локальный почтовый сервер выступает в роли авторизованного клиента Gmail. Из минусов пожалуй только перезапись From: (вроде это можно как-то настраивать).

Если postfix еще не установлен, то самое время его установить

# aptitude install postfix ca-certificates

Сказать postfix, что нужно доставлять почту через релей

# vim /etc/postfix/main.cf

relayhost = [smtp.gmail.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/postfix/cacert.pem
smtp_use_tls = yes

Указать данные авторизации в Gmail в файле /etc/postfix/sasl_passwd

# vim /etc/postfix/sasl_passwd

[smtp.gmail.com]:587    USERNAME@gmail.com:YOURPASSWORD

После нужно выставить правильные разрешения на файл паролей и создать его хешированную версию

# chmod 0400 /etc/postfix/sasl_passwd
# postmap /etc/postfix/sasl_passwd

Остается только дать postfix доступ к сертификату удостоверяющего центра Gmail (это thawte)

# cp /etc/ssl/certs/Thawte_Premium_Server_CA.pem /etc/postfix/cacert.pem

Перезагружаем конфигурацию postfix и пробуем отправить тестовое письмо

# service postfix reload
# echo 'test message' | mail -s 'relayhost test' someuser@gmail.com

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

07 февраля 2014

Получение информации о принтере через SNMP

Установите SNMP клиент и поддержку загрузки MIB баз

$ sudo aptitude install snmp snmp-mibs-downloader

Закоментируйте строку mibs : в файле /etc/snmp/snmp.conf. Это позволит использовать MIB, которые скачаются при установке пакета snmp-mib-downloader.

Скачайте IETF Printer MIB (я нашел тут) и распакуйте его в директорию ~/.snmp/mibs (создайте эту директорию, если она еще не существует). Затем добавьте в ~/.snmp/snmp.conf строку mibs +Printer-MIB.

Теперь вы можете просмотреть значение переменных

$ snmpwalk -c public -v1 192.168.100.201 SNMPv2-MIB::sysDescr
SNMPv2-MIB::sysDescr.0 = STRING: Panasonic KX-MB2020

P.S. Большая подборка MIB для различных вендоров нашлась тут.

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

26 января 2014

Эксплойт для обхода ограничений full disk encryption

Greg Kroah-Hartman упомянул эксплойт для обхода защиты full disk encryption. Для успешной эксплуатации уязвимости нужен физический доступ к компьютеру и диск должен быть разблокирован (введен пароль дешифровки диска). Для защиты ваш компьютер должен быть выключен или находиться в состоянии suspend to disk (hibernate). Более радикальным вариантом защиты будет блокировка или удаление драйверов FireWire из операционной системы.

Суть уязвимости заключается в том, что firewire шина позволяет получить прямой доступ к памяти хоста (нижние 4GB памяти) через запрос DMA (протокол SBP-2). После этого аппаратная отмычка сканирует память хоста и патчит механизм проверки пароля. Результатом успешной атаки является возможность авторизоваться по любому паролю.

Если вы считаете, что отсутствие порта firewire вас защитит, то прочтите ответы 5 и 7 в FAQ.

Чтобы отключить поддержку FireWire в Linux (инструкция для свежих ядер) добавьте в файл /etc/modprobe.d/blacklist.conf строки

blacklist firewire-core
blacklist firewire-ohci
blacklist firewire-sbp2

Выгрузите модули из памяти и обновите образ initramfs

# modprobe -r firewire-ohci
# update-initramfs -u -k all

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

24 января 2014

Преобразование формата даты 24/Jan/2014 в 2014-01-24

В процессе написания парсера логов мне потребовалось преобразовывать формат дат в shell скрипте. В логе дата указывается в виде 24/Jan/2014, а мне нужна 2014-01-24. Для преобразования формата даты я написал функцию convert_date()

# Convert date format from %d/%b/%Y to %Y-%m-%d (strftime notation)
convert_date() {
    date --date="$(echo $1 | awk -F '/' '{print $2" "$1" "$3}')" +'%Y-%m-%d'
}

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

STAMP=$(convert_date 24/Jan/2014)

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

20 января 2014

Неправильный порядок nameserver'ов в /etc/resolv.conf при работе NetworkManager через resolvconf

Недавно подключался к корпоративной сети в "полевых" условиях и наткнулся на несовместимость NetworkManager (0.9.4.0-10) и resolvconf (1.67) в Debian Wheezy. Проблема известная, но патча или какого-то грамотного workaround'а я не увидел.

Если кратко, то записи о nameserver'ах в /etc/resolv.conf прописываются в неверном порядке если для подключения к сетям использовать NetworkManager. Если подробнее, то NetworkManager неправильно взаимодействует с resolvconf и передает неверное с точки зрения resolvconf имя интерфейса (точнее сказать не передает совсем). Resolvconf требует указывать название интерфейса и название программы, которая обновляет информацию о резолверах (например /sbin/resolvconf -a ppp0.pppd). При этом добавление записи в /etc/resolv.conf выполняется согласно порядку интерфейса в /etc/resolvconf/interface-order.

Например вы подключились через 3G модем к сотовому оператору и название вашего интерфейса ppp0. Сотовый оператор выдает два nameserver'а (x.x.x.1 и x.x.x.2). Далее вы подключаетесь к корпоративной сети VPN и вам выдает еще два nameserver'a (y.y.y.1 и y.y.y.2). Чтобы все работало правильно нужно чтобы /etc/resolv.conf выглядел так:

nameserver y.y.y.1
nameserver y.y.y.2
nameserver x.x.x.1
nameserver x.x.x.2

Но вместо этого в /etc/resolv.conf записи выглядят так:

nameserver x.x.x.1
nameserver x.x.x.2
nameserver y.y.y.1
nameserver y.y.y.2

Поскольку корпоративные nameserver'а оказались в конце списка, то внутренние ресурсы оказываются недоступными.

В процессе изучения проблемы оказалось, что NetworkManager использует вызов /sbin/resolvconf -a NetworkManager (указывает все nameserver'а без указания принадлежности к интерфейсу). Один из вариантов - изменить файл /etc/resolvconf/interface-order, чтобы явно задать приоритет NetworkManager, но поскольку это NM не следует соглашению, то я решил поправить его код.

Нужный мне участок кода быстро нашелся в файле NetworkManager/src/dns-manager/nm-dns-manager.c. Для добавления и удаления записей через resolvconf используется функция dispatch_resolvconf()

#ifdef RESOLVCONF_PATH
static gboolean
dispatch_resolvconf (const char *domain,
                     char **searches,
                     char **nameservers,
                     const char *iface,
                     GError **error)
{
 char *cmd;
 FILE *f;
 gboolean retval = FALSE;

 if (! g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE))
  return FALSE;

 if (domain || searches || nameservers) {
  cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
  nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
  if ((f = popen (cmd, "w")) == NULL)
   g_set_error (error,
                NM_DNS_MANAGER_ERROR,
                NM_DNS_MANAGER_ERROR_SYSTEM,
                "Could not write to %s: %s\n",
                RESOLVCONF_PATH,
                g_strerror (errno));
  else {
   retval = write_resolv_conf (f, domain, searches, nameservers, error);
   retval &= (pclose (f) == 0);
  }
 } else {
  cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
  nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH);
  if (nm_spawn_process (cmd) == 0)
   retval = TRUE;
 }

 g_free (cmd);

 return retval;
}
#endif

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

--- NetworkManager/src/dns-manager/nm-dns-manager.c.orig 2014-01-16 12:10:52.265616559 +0300
+++ NetworkManager/src/dns-manager/nm-dns-manager.c 2014-01-16 12:15:09.069611996 +0300
@@ -416,7 +416,7 @@ dispatch_resolvconf (const char *domain,
   return FALSE;
 
  if (domain || searches || nameservers) {
-  cmd = g_strconcat (RESOLVCONF_PATH, " -a ", "NetworkManager", NULL);
+  cmd = g_strconcat (RESOLVCONF_PATH, " -a ", iface, ".NetworkManager", NULL);
   nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
   if ((f = popen (cmd, "w")) == NULL)
    g_set_error (error,
@@ -430,7 +430,7 @@ dispatch_resolvconf (const char *domain,
    retval &= (pclose (f) == 0);
   }
  } else {
-  cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
+  cmd = g_strconcat (RESOLVCONF_PATH, " -d ", iface, ".NetworkManager", NULL);
   nm_log_info (LOGD_DNS, "(%s): removing resolv.conf from %s", iface, RESOLVCONF_PATH);
   if (nm_spawn_process (cmd) == 0)
    retval = TRUE;

После пересборки пакетов и обновления системы приоритет nameserver'ов правильный.

P.S. К сожалению этот патч не будет работать на последней версии NetworkManager, поскольку там изменился прототип функции dispatch_resolvconf() и имя интерфейса более не передается. Позже попробую закинуть им баг на эту тему.

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