среда, 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