среда, 28 ноября 2012 г.

Многопользовательский VNC-сервер на RHEL5


Из коробки в RHEL5 (опыты проводились на Oracle Linux 5.8) для запуска Xvnc предназначена служба vncserver, предполагающая жескую привязку запущенных процессов к пользователям и портам (ея настройки находятся в /etc/sysconfig/vncservers). В данной статье описано более интересеное поведение: Xvnc принимает множественные соединения на одном порту и выводит окно авторизации gdm. При такой конфигурации служба vncserver должна быть остановлена. Далее: добавляем порт vnc в /etc/services:

vnc        5900/tcp

Создаем /etc/xinetd.d/vncserver:

service vnc
{
disable = no
protocol = tcp
socket_type = stream
server = /usr/bin/Xvnc
wait = no
user = nobody
server_args = -inetd –query localhost –once –geometry 1024x768 \
 –depth 16 -SecurityTypes=None
}
(строки, разделенные знаком \, на самом деле составляют одну строку)

И добаляем в /etc/gdm/custom.conf (если их там еще нет) следующие строки:

[security]
AllowRemoteRoot=true
DisallowTCP=false

[xdmcp]
Enable=true
MaxSessions=30

После перезапуска xinetd и gdm должна появиться возможность подсоединиться vnc-клиентом (например, remmina) на порт 5900.
В качестве неизбежных рюшечек избавимся от  гнома по умолчанию (ибо сервер у нас не совсем резиновый). В файл /etc/sysconfig/desktop пишем:

PREFERRED=/opt/bin/xsession.sh

Ну а сам /opt/bin/xsession.sh предоставляем фантазии читателя. Мой минималисткий вариант таков (mwm ставится из пакета openmotif):

#!/bin/sh
vncconfig -iconic &
xclock -bg black -fg green -digital -strftime '%H:%M:%S %d.%m.%y' \
 -geometry -0-0 -update 1 &
xterm -bg black -fg grey -geometry +0+0 &
exec mwm

В комментариях здесь нуждается только вторая строка - без нее не будет работать клипбоард.

вторник, 13 ноября 2012 г.

Pacemaker за 5 минут

Так уж получилось, что и старое, и новое издание Clusters from Scratch описывают слегка не ту конфигурацию, что используется на наших серверах. Поэтому пишу сам себе HOWTO. Задача: поднять двухнодовый кластер с httpd. Платформой служит Oracle Linux 6.3 из коробки.
Предварительные необходимые шаги (без детализации):
  1. отключаем selinux;
  2. разрешаем весь трафик через интерфейсы, по которым узлы будут общаться друг с другом;
  3. убеждаемся, что httpd не запускается при загрузке - и кластерный ip никем не используется;
  4. создаем одинаковый беспарольный RSA-ключ для рута на обеих узлах и взаимно авторизуем его;
  5. устанавливаем pacemaker и corosync через yum.
А теперь - за дело. /etc/corosync/corosync.conf.example содержит почти все нужное. Копируем его без суффикса example, заменяем  bindnetaddr на реальный (это адрес сети, а не хоста - он будет одинаковым на обеих узлах) и добавляем в конец секцию запуска pacemaker:
service {
        name: pacemaker
        ver: 0
}
После этого запускаем corosync:
# service corosync start
И команда
# crm status
должна показать работающий кластер из двух узлов и нуля ресурсов. Corosync будет единственным из кластерных сервисов, запускаемых при загрузке:
# chkconfig corosync on
Все остальное, что мы собираемся запускать в кластере, будет стартовать из-под него. Если первоначальные настройки должны быть сделаны симметрично на обеих узлах, то дальнейшие crm-команды выполняются только на одном из узлов.
Наша примитивная конфигурация требует слегка поменять стандартные настройки кластера. Отключаем STONITH:
# crm configure property stonith-enabled=false
И кворум (иначе в двухнодовом кластере вместе с одним из узлов умрут все сервисы):
# crm configure property no-quorum-policy=ignore
Теперь можно добавлять ресурсы. Переходящий IP-адрес:
# crm configure primitive ip0 ocf:heartbeat:IPaddr2 \
    params ip=10.1.1.251 nic=eth1 cidr_netmask=32 \
    op monitor interval=10s
И веб-сервер:
# crm configure primitive www0 lsb:/etc/init.d/httpd \
    op monitor interval=30s
т.к. наши два ресурса логически связаны (и по смыслу должны запускаться на одном и том же узле), то сгруппируем их: 
# crm configure group g0 ip0 www0
И установим порядок запуска (веб-сервер всегда после адреса):
# crm configure order ip-www inf: ip0 www0
Далее следует сводка простейших команд администрирования кластера.
Если понадобилось вывести узел из кластера (например для апргрейда):
# crm node standby
(Эта и следующая команды выполняются на том узле, которым мы манипулируем - либо третьим аргументом должно быть имя узла). Возвращение в кластер после окончания обслуживания:
# crm node online
Перемещение группы ресурсов на другой узел кластера:
# crm resource move g0
После того, как перемещение закончится, надо выполнить:
# crm resource unmove g0
иначе группа никогда не сможет вернуться на прошлый узел.
Посмотреть на работающий кластер в реальном времени можно командой
# crm_mon

Нуждающиеся в более серьезной документации, чем эта, найдут ее на ClusterLabs.

четверг, 4 октября 2012 г.

/etc как репозиторий Mercurial


Безумная идея видавшего виды админа - а почему бы не приделать к Linux хранение истории правок конфигурации? Для воплощения в жизнь используем CentOS 6.3 и Mercurial 1.4 (он показался мне капельку проще, чем Subversion - и к тому же он хранит репозиторий и рабочую копию рядом - т.е. нам не придется создавать никаких дополнительных директорий и усложнять и без того непростую иерархию). Все последующие команды выполняются от имени суперпользователя - ибо в /etc немало файлов, недоступных для чтения другим пользователям:
# cd /etc
# hg init
# hg add *
# hg commit -m 'add /etc' -u root
Теперь во-первых убеждаемся, что изуродованная таким образом система успешно перезагрузится (хотя кому может помешать директория /etc/.hg?), потом не забудем закрыть эту директорию от обычных пользователей - все-таки наш репозиторий содержит немало такого, что им знать не положено:
# chmod 700 /etc/.hg
И проверяем, как у нас все работает:
# groupadd dummies
# useradd -d /home/dummy -g dummies -p h5CVem3AekREA dummy
# hg diff
Любуемся на изменения:

diff -r 2d2839e14225 group
--- a/group Thu Oct 04 14:08:10 2012 +0400
+++ b/group Thu Oct 04 14:24:47 2012 +0400
@@ -33,3 +33,4 @@
 nfsnobody:x:65534:
 sshd:x:74:
 cgred:x:499:
+dummies:x:500:
diff -r 2d2839e14225 gshadow
--- a/gshadow Thu Oct 04 14:08:10 2012 +0400
+++ b/gshadow Thu Oct 04 14:24:47 2012 +0400
@@ -33,3 +33,4 @@
 nfsnobody:!::
 sshd:!::
 cgred:!::
+dummies:!::
diff -r 2d2839e14225 passwd
--- a/passwd Thu Oct 04 14:08:10 2012 +0400
+++ b/passwd Thu Oct 04 14:24:47 2012 +0400
@@ -18,3 +18,4 @@
 rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
 nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
+dummy:x:500:500::/home/dummy:/bin/bash
diff -r 2d2839e14225 shadow
--- a/shadow Thu Oct 04 14:08:10 2012 +0400
+++ b/shadow Thu Oct 04 14:24:47 2012 +0400
@@ -18,3 +18,4 @@
 rpcuser:!!:15617::::::
 nfsnobody:!!:15617::::::
 sshd:!!:15617::::::
+dummy:h5CVem3AekREA:15617:0:99999:7:::
Если все хорошо, записываем их:
# hg commit -m 'add dummy user' -u root
Еще для наглядности можно запустить встроенный веб-сервер:
# hg serve -d
И смотреть на репозиторий станет еще приятнее (только не забудьте сперва закрыть файрволом порт 8000 - ибо едва ли содержимое вашего /etc является зрелищем, достойным внимания всего интернета).

среда, 22 августа 2012 г.

Python MIME-библиотека и строка From в Unix-стиле



При создании тела письма с помощью питоновского класса email.message.Message (и его потомков типа email.mime.base.MIMEBase или email.mime.multipart.MIMEMultipart) первой строкой автоматически добавляется  знакомая каждому старому юниксоиду строка From без двоеточия.
Например:

$ python
>>> import  email.mime.multipart
>>> msg = email.mime.multipart.MIMEMultipart()
>>> print msg

From nobody Wed Aug 22 11:02:28 2012
Content-Type: multipart/mixed; boundary="===============1046125271=="
MIME-Version: 1.0

--===============1046125271==

--===============1046125271==--

Конечно всякий нормальный емейл-клиент реагирует на эту строку спокойно: ведь она существует в мире уже третий десяток лет, но - увы - современный мир полон ненормальными почтовыми клиентами. В одном из их - Lotus Notes 8.5.1 - наличие строки From в заголовке приводит к тому, что MIME-структура письма не отображается - и письмо выводится как простой текст.   
Как заставить Message не включать в письмо данную строку? В документации обнаружено два способа:
  1. прямой. Message имеет метод as_string(self, unix_fromline=False). Если в прошлом примере вместо print msg сделать print msg.as_string() (или - для не надеющихся на умолчательное поведение - print msg.as_string(False)), то мы получим текст сообщения без строки From.
  2. прикольный. Строку From можно заменить любой своей строкой - для этого у нашего класса есть метод set_unixfrom(self, string). Т.к. по стандарту мы можем беспрепятственно добавлять в письмо любые заголовки, начинающиеся с "X-", то сделав что-то типа: msg.set_unixfrom("X-Mailer: Python Is Not Lotu$ Note$") мы получим тело письма, которое ничем не смутит нашего капризного клиента. 

среда, 15 августа 2012 г.

Заработавший rp_filter в CentOS 6.3

Продолжающиеся разборки со свежеустановленной CentOS 6.3 принесли новые неожиданные результаты. Примерная конфигурация такая:
Компьютер администратора имеет ip-адрес 10.1.1.101 в сети 10.1.1.0/24;
Сервер (как раз CentOS 6.3 x86_64) имеет интерфейс eth0 адресом 10.1.1.201 в той же сети и eth1 адресом 10.1.2.201 в сети 10.1.2.0/24.
Маршрутизация между этими сетями настроена, с роутера все три адреса успешно пингуются.
С компьютера администратора пингуется 10.1.1.201, но никак не пингуется 10.1.2.201.
В принципе такое поведение нельзя назвать неправильным - на интерфейсе eth1 оказывается пакет, который по всем правилам должен был прийти на eth0 - и ответа на него не посылается. Другое дело, что за прошлую жизнь админ так привык к неправильному поведению, что правильное ему кажется неудобным. Поиск по настройкам sysctl достаточно скоро находит виновного:
net.ipv4.conf.eth1.rp_filter = 1
Из документа http://linuxgazette.net/issue77/lechnyr.html как раз и явствует что:
The rp_filter can reject incoming packets if their source address doesn't match the network interface that they're arriving on, which helps to prevent IP spoofing.
Установка:
net.ipv4.conf.eth1.rp_filter = 0
приводит к тому, что желанный пинг появляется.
А напоследок - как всегда - самое смешное. В CentOS 5-ой ветки, на которых автор этих строк собственно и успел пристраститься к "неправильным" пингам, установки net.ipv4.conf.*.rp_filter по умолчанию - точно такие же, как и в 6.3. Только не вызывают они заявленного поведения. Одно из двух - либо давно объявленную фичу наконец-то заставили работать, либо где-то таится еще какая-то настройка, которая говорит системе, реагировать ей на rp_filter - или нет.

вторник, 14 августа 2012 г.

Неуловимый LANG в CentOS 6.3

При установке CentOS 6.3 с образа minimal меня удивили сообщения на русском языке (при установке был явно выбран английский). Переменная LANG действительно содержала ru_RU.UTF-8, но рекурсивный поиск в /etc и $HOME не давал ответа, каким образом подобное значение устанавливается - такой подстроки просто-напросто не было в файлах этих директорий. Проблема выглядела невероятной, пока не был произведен рекурсивный поиск слова LANG. Ответ нашелся в файле /etc/ssh/sshd_config:
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE \\
 LC_MONETARY LC_MESSAGES
(Это одна строка - переноса в оригинальном файле нет)
Т.е. получается, что установки языка передавались на сервер с моего компьютера по ssh. Действительно, если убрать LANG из этой строки, то при следующем входе в систему языком сообщений делается английский.
Следует добавить, что аналогичные настройки имеются и в клиентском файле /etc/ssh/ssh_config:
SendEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE \\
LC_MONETARY LC_MESSAGES
Так что, вероятно, наиболее правильным решением проблемы является как раз изменение SendEnv на том компьютере, откуда производится вход по ssh - а то вдруг другие администраторы сервера захотят видеть русский интерфейс? Хотя - с другой стороны - а что им мешает явно установить LANG в своих профайлах?
Ну а самое смешное заключается в том, что данная строка имеется в /etc/ssh/sshd_config и в более ранних версиях CentOS (например, в 5.3) - но там она почему-то никак на язык консольных сообщений не влияет. 

четверг, 26 июля 2012 г.

HTTP basic авторизация из таблицы MySQL

Некая closed-source биллинговая система хранит логины и пароли для онлайн-плательщиков в таблице менеджеров, причем пароли зашифрованы с помощью MySQL-функции PASSWORD(). Если бы там был plaintext или банальный crypt, ничего бы не стоило периодически создавать из этой таблицы файл htpasswd - и не мучиться. А так придется водружать на апач mod_auth_mysql. Настройка его достаточно тривиальна. В httpd.conf (или в любой из conf-файлов, вкладываемых в него) добавляем такие строки:

LoadModule mysql_auth_module modules/mod_auth_mysql.so
<Directory /var/www/protected/dir/>

    AuthName "MySQL authenticated zone"
    AuthType Basic
    AuthMYSQLEnable on
    AuthMySQLHost my.db.ho.st
    AuthMySQLUser apache
    AuthMySQLPassword PaSsW0Rd
    AuthMySQLDB mybilldb
    AuthMySQLUserTable paysys
    AuthMySQLPwEncryption scrambled
    require valid-user

<Directory>

Наиболее интересна тут опция AuthMySQLPwEncryption - она задает метод шифрования паролей в базе. Значение scrambled как раз означает результат работы MySQL-функции PASSWORD() (причем поддерживаются как старый 16-символьный формат, так и новый 41-символьный). Еще возможные значения: none, crypt, md5, aes и sha1.
И последнее. mod_auth_mysql не поддерживает проверку логинов/паролей произвольным запросом к БД (как это разрешается, например, во freeradius) - нужно обязательно иметь таблицу с именем, указанным в AuthMySQLUserTable, содержащую поля user_name (AuthMySQLNameField по умолчанию) и user_password (AuthMySQLPasswordField по умолчанию). Можно указать еще условие WHERE в опции AuthMySQLUserCondition, но лично мне более гибким кажется путь создания в базе представления:

$ mysql -u root mybilldb -p


mysql> create view paysys as select login as user_name, pass as user_password from managers where payments > 0 and archive = 0 and template = 0;
mysql> grant select on mybilldb.paysys to 'apache'@'my.apache.ho.st' identified by 'PaSsW0Rd';

Полную справку по опциям mod_auth_mysql можно прочитать тут: http://modauthmysql.sourceforge.net/CONFIGURE

пятница, 4 мая 2012 г.

Обуздание неистового DHCP в Debian 6


Случаются ситуации, когда интрефейс, конфигурируемый по DHCP, не единственный в системе - и настройки маршрутизации и DNS, получаемые автоматически, не должны перезаписывать статических настроек с других интерфейсов. Ниже приводятся два способа, как можно побороться с этой проблемой в Debian 6:

Первый способ. Требует установки дополнительных пакетов.
Для того, чтобы маршрут по умолчанию не перезаписывал значение, сконфигурированное на другом интерфейсе, ставим пакет ifmetric - и в файле /etc/network/interfaces указываем для наших интерфейсов числовые значения metric. Чем значение ниже, тем выше приоритет маршрута через данный интерфейс.
Для того, чтобы DNS и доменное имя, полученные по DHCP, не перезаписывали насмерть файл /etc/resolv.conf, ставим пакет resolvconf - и в файле /etc/network/interfaces в разделе статического интерфейса добавляем строки:
  dns-nameservers A.A.A.A B.B.B.B
 dns-search mydom1.ru mydom2.ru

Второй способ. Правим файл /etc/dhcp/dhclient.conf
В нем есть длинная строчка, начинающаяся со слова request.
Удалив из нее слова: routers, domain-name, domain-name-servers, domain-search, host-name и rfc3442-classless-static-routes, мы как раз запретим запрос этих параметров по DHCP. Способ безусловно проще, но менее гибкий - в случае проблем со статическим интерфейсом мы остаемся вообще без DNS и без маршрута по умолчанию.

А если не лень чуть-чуть попрограммировать, можно изменять поведение DHCP-интерфейса с помощью скриптов, указываемых в соответствующем разделе /etc/network/interfaces в параметрах up и down. Например, нижеприводимый скрипт применяет маршрут, отдаваемый DHCP сервером как маршрут по умолчанию, только к определенному списку сетей:

#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
mynet="10.0.0.0/8 172.16.1.0/24"
case "$MODE" in
start)
gw=`ip route show dev $IFACE | 
  awk '{if ($1 == "default") {print $3; exit(0)}}'`
if [ -n "$gw" ]
then
ip route delete default dev $IFACE
for n in $mynet
do
ip route add $n via $gw dev $IFACE
done
fi
;;
stop)
for n in $mynet
do
ip route delete $n dev $IFACE
done
;;
esac

суббота, 28 апреля 2012 г.

Хост VirtualBox на сервере CentOS без GUI



Т.к. репозитории CentOS не содержат пакетов VirtualBox, то RPM для RHEL5 берется с https://www.virtualbox.org/wiki/Linux_Downloads - каких-то невероятных зависимостей он не требует, только для установки модулей ядра необходимо иметь kernel-devel.
Ниже описывается работа с виртуальными машинами с помощью CLI утилиты VBoxManage. Подробная справка по ее опциям (на английском) имеется здесь: https://www.virtualbox.org/manual/ch08.html
Смотрим допустимые типы машин:
$ VBoxManage list ostypes
Создаем и регистрируем машину для solaris:
$ VBoxManage createvm --name sol11 --ostype OpenSolaris_64 --register --basefolder $HOME/vbox-sol11/
(последняя опция необязательна, но мне кажется, лучше всегда явно задавать, где будут храниться наши машины).
Какие ресурсы выделены машине по умолчанию, можно увидеть командой:
$ VBoxManage list -l vms
Наверняка нам захочется что-нибудь поменять. Придаем машине 2 процессора, 1 Гб памяти, системные часы, идущие по Гринвичу, и вторую сетевую карту типа bridge (первая типа nat создается автоматически):
$ VBoxManage modifyvm sol11 --memory 1024 --cpus 2 --rtcuseutc on --nic2 bridged --bridgeadapter2 eth1
(последняя опция указывает сетевой интерфейс хост-машины, к которому прицепится виртуальный интерфейс).
Если машин много, лучше смотреть настройки конкретной машины командой:
$ VBoxManage showvminfo sol11
Дисковый контроллер (в нашем случае - SCSI) добавляется командой:
$ VBoxManage storagectl sol11 --name scsi0 --add scsi
Создаем жесткий диск на 10 Гб (тип образа по умолчанию - vdi):
$ VBoxManage createhd --filename $HOME/vbox-sol11/sd0 --size 10240
и прицепляем его на контроллер:
$ VBoxManage storageattach sol11 --storagectl scsi0 --medium $HOME/vbox-sol11/sd0.vdi --port 1 --type hdd
Потом прицепляем образ загрузочного CD-ROM:
$ VBoxManage storagectl sol11 --name ide0 --add ide
(IDE контроллер пришлось создать, потому что диск типа dvddrive почему-то отказался присоединяться к SCSI)
$ VBoxManage storageattach sol11 --storagectl ide0 --medium $HOME/tmp/oi-dev-151a-text-x86.iso --device 0 --port 0 --type dvddrive
На время инсталляции придадим машине RDP-консоль:
$ VBoxManage modifyvm sol11 --vrde on --vrdeport 3389 --vrdeaddress 10.10.10.10
(две последних опции необязательны)
Для того, чтобы работал VRDP возможно придется установить Extension Pack - скачивается он там же, откуда мы брали rpm, и ставится командой:
$ VBoxManage extpack install
Теперь машину можно запускать:
$ VBoxManage startvm sol11 --type headless
Остановка машины:
$ VBoxManage controlvm sol11 poweroff
(еще возможные действия: acpipowerbutton, acpisleepbutton, pause, reset, resume, savestate)
Отцепить ненужный более cdrom можно командой:
$ VBoxManage storageattach sol11 --storagectl ide0 --port 0 --device 0 --medium none
(вместо none можно сделать emptydrive - такая операция допустима на запущенной машине).
Если понадобится проброс ssh c NAT-интерфейса на порт хост-машины (в данном случае 127.0.0.1:2222), то он делается так:

$ VBoxManage modifyvm sol11 --natpf1 sshd,tcp,127.0.0.1,2222,,22