пятница, 18 февраля 2011 г.

Backup конфигов интернет-сервера. Update 21.02.2011

Итак, приступим. Идея подсмотрена в /etc/init.d/named RHEL6. Конфигурационные файлы/директории монтируются в определенную папку с помощью mount --bind, затем делается архив с сохранением прав текущих владельцев. Скрипт хранится в /home/jack/scripts/backup.
Предварительный этап(ы):
1. Изменение пароля для пользователя root, либо добавление пользователя с правами экспорта нужных таблиц mysql (права LOCK, UNLOCK, SELECT и др.)
mysql> use mysql;
mysql> update user set password=password('PASSWORD') where user='root';
mysql> flush privileges;
mysql> quit

2. Сделать скрипт испоняемым
chmod +x backup.sh

3. Добавить пользователя на сервер
groupadd backups
useradd -g backups -s /bin/sh -d /var/backups backups

4. Сгенерировать rsa-ключ и добавить его на сервер, куда будут складываться бэкапы
ssh-keygen -q
scp -P 1919 /root/.ssh/id_rsa.pub backups@SERVER:/var/backups
На сервере SERVER
cat id_rsa.pub >> /var/backups/.ssh/authorized_hosts

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

Теперь сам скрипт. Ничего сложного. Переменные, функции. Я разделил скрипт на клиентскую и основную части. Краткий обзор (клиент):
1. Запускает скрипт по крону, подставляя свои переменные
2. Копирует основной скрипт с сервера в локальную папку
3. Подключает и выполняет основной скрипт
4. Удаляет из локальной папки скрипт

#!/bin/sh
# Script for backing up configs from server

##############################################
# Variables #
##############################################

PATH=$PATH:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin
export PATH

BRANCH=Donetsk
DOMAIN=domain.tld
PREFIX=MX
SSH_PORT=1919
SSH_DEST=backups@odin:/var/backups/$BRANCH
CORE_SCRIPT=bc.script
MAILTO=root
TEMP1=`pwd`

scp -P $SSH_PORT $SSH_DEST/$CORE_SCRIPT .
if [ $? -ne 0 ] ; then
echo "Can't copy $CORE_SCRIPT from $SSH_DEST. Exiting." | mail -s "Backup problem with $BRANCH-$PREFIX ($DOMAIN)" $MAILTO
exit 1
fi;
if [ -s $TEMP1/$CORE_SCRIPT ]; then
. $TEMP1/$CORE_SCRIPT
else
echo "Can't include $CORE_SCRIPT. It has zero size. Exiting." | mail -s "Backup problem with $BRANCH-$PREFIX ($DOMAIN)" $MAILTO
exit 1;
fi;
rm -f $TEMP1/$CORE_SCRIPT >/dev/null 2>&1
exit 0;

Краткий обзор (основная часть):
1. задать переменные
2. проверить запуск от root или нет
3. создать директории, если их нет в папке $ROOTDIR
4. смонтировать нужные файлы/папки $ROOTDIR_MOUNTS в $ROOTDIR_MOUNTS/папки/файлы для сохранения текущей иерархии
5. дамп таблиц mysql
6. создание архива папки $ROOTDIR
7. размонтирование ранее смонтированных файлов/папок
8. копирование архива и лог-файла на удаленный сервер
9. удаление архива и лог-файла
10. конец скрипта.

DATE=`date +%Y.%m.%d`
DATABASES='exim_db mysql rcubemail'
MYSQL_AUTH=' -u root -pPASSWORD '
MYSQLDUMP_OPTS=' --opt '
USER=admin

ROOTDIR=~/backups
ROOTDIR_MOUNT='/boot/grub/grub.conf /boot/config-* /root/.ssh
/home/jack/scripts /home/jack/.ssh /home/'$USER'/scripts /home/'$USER'/.fetchmailrc
/etc/dovecot/* /etc/dovecot/conf.d/* /etc/exim/* /etc/exim/dkim/* /etc/clamav/*
/etc/cron.d/* /etc/dspam/* /etc/httpd/conf/* /etc/httpd/conf.d/* /etc/free-sa/*
/etc/init.d/* /etc/ld.so.conf.d/* /etc/logrotate.d/* /etc/nagios/* /etc/php.d/*
/etc/pki/tls/certs/*'$DOMAIN'.crt /etc/pki/tls/private/*'$DOMAIN'.key /etc/racoon/* /etc/samba/*
/etc/squid/squid.conf /etc/squid/acl/* /etc/ssh/ssh* /etc/vsftpd/* /etc/yum.repos.d/*
/etc/sysconfig/exim /etc/sysconfig/kernel /etc/sysconfig/keyboard /etc/sysconfig/named
/etc/sysconfig/network /etc/sysconfig/squid /etc/sysconfig/network-scripts/route-*
/etc/sysconfig/network-scripts/ifcfg-* /etc/dovecot* /etc/fstab /etc/group
/etc/gshadow /etc/hosts /etc/inittab /etc/krb5.conf /etc/ld.so.conf /etc/mdadm.conf
/etc/my.cnf /etc/named.* /etc/nsswitch.conf
/etc/ntp.conf /etc/passwd /etc/php.ini /etc/redhat-release /etc/resolv.conf /etc/rndc.key
/etc/rsyslog.conf /etc/syslog.conf /etc/shadow /etc/sudoers /etc/sysctl.conf /etc/yum.conf
/var/named/master/* /var/named/slave/* /var/lib/dspam/group /var/spool/cron/*
/var/named/chroot/var/named/master/* /var/named/chroot/var/named/slave/*'

#echo "ROOTDIR_MOUNT=$ROOTDIR_MOUNT"
#exit 0
LOG=$ROOTDIR/backup-$PREFIX-$BRANCH-$DATE.log

make_dirs()
{
cat << EOF >> $LOG
Current variables:
PATH=$PATH

BRANCH=$BRANCH
PREFIX=$PREFIX
DOMAIN=$DOMAIN
CORE_SCRIPT=$CORE_SCRIPT
SSH_PORT=$SSH_PORT
SSH_DEST=$SSH_DEST
DATE=$DATE
DATABASES=$DATABASES
MYSQL_AUTH=
MYSQLDUMP_OPTS=$MYSQLDUMP_OPTS
USER=$USER

ROOTDIR=$ROOTDIR
ROOTDIR_MOUNT=$ROOTDIR_MOUNT
LOG=$LOG

EOF

echo "Function make_dirs" >> $LOG
echo "##############################" >> $LOG
echo `date` >> $LOG
mkdir -p $ROOTDIR
echo "Created $ROOTDIR";
mkdir -p $ROOTDIR/{etc,home,root,var,boot}
mkdir -p $ROOTDIR/boot/grub
mkdir -p $ROOTDIR/root/.ssh
mkdir -p $ROOTDIR/home/{jack,$USER}
mkdir -p $ROOTDIR/home/jack/scripts
mkdir -p $ROOTDIR/home/jack/.ssh
mkdir -p $ROOTDIR/home/admin/scripts
mkdir -p $ROOTDIR/etc/{clamav,cron.d,dspam,dovecot,exim,free-sa,httpd,init.d,ld.so.conf.d,logrotate.d,nagios,pam.d,php.d,pki,racoon,samba,squid,ssh,sysconfig,vsftpd,yum.repos.d}
mkdir -p $ROOTDIR/etc/exim/dkim
mkdir -p $ROOTDIR/etc/dovecot/conf.d
mkdir -p $ROOTDIR/etc/httpd/{conf,conf.d}
mkdir -p $ROOTDIR/etc/pki/tls
mkdir -p $ROOTDIR/etc/pki/tls/{certs,private}
mkdir -p $ROOTDIR/etc/squid/acl
mkdir -p $ROOTDIR/etc/sysconfig/network-scripts
mkdir -p $ROOTDIR/var/{lib,named,spool}
mkdir -p $ROOTDIR/var/lib/dspam
mkdir -p $ROOTDIR/var/spool/cron
mkdir -p $ROOTDIR/var/named/{etc,var}
mkdir -p $ROOTDIR/var/named/{master,slave,chroot}
mkdir -p $ROOTDIR/var/named/var/named
mkdir -p $ROOTDIR/var/named/var/named/{master,slave}
mkdir -p $ROOTDIR/var/named/chroot/var
mkdir -p $ROOTDIR/var/named/chroot/var/named
mkdir -p $ROOTDIR/var/named/chroot/var/named/{master,slave}
echo "Created all required directories."; >> $LOG
echo "Created all required directories."
echo "##############################" >> $LOG
}

mysql_backup()
{
echo "Function mysql_backup" >> $LOG
echo "##############################" >> $LOG
echo `date` >> $LOG
for i in $DATABASES; do
echo "Backing up $i database" >> $LOG
rm -f $ROOTDIR/$BRANCH-$DATE-$i.sql >/dev/null 2>/dev/null
mysqldump $MYSQL_AUTH $MYSQLDUP_OPTS $i > $ROOTDIR/$BRANCH-$DATE-$i.sql
done
echo "##############################" >> $LOG
}

mount_chroot_conf()
{
echo "Function mount_chroot_conf" >> $LOG
echo "##############################" >> $LOG
echo `date` >> $LOG
if [ -n "$ROOTDIR" ]; then
for all in $ROOTDIR_MOUNT; do
# Skip nonexistant files
[ -e "$all" ] || continue

# If mount source is a file
if ! [ -d "$all" ]; then
# mount it only if it is not present in chroot or it is empty
if ! [ -e "$ROOTDIR$all" ] || [ `stat -c'%s' "$ROOTDIR$all"` -eq 0 ]; then
touch "$ROOTDIR$all"
mount -o ro --bind "$all" "$ROOTDIR$all" >/dev/null 2>/dev/null
echo "Mounting $all on $ROOTDIR$all" >> $LOG
fi
else
# Mount source is a directory. Mount it only if directory in chroot is
# empty.
if [ -e "$all" ] && [ `ls -1A $ROOTDIR$all 2>> $LOG| wc -l` -eq 0 ]; then
mount -o ro --bind "$all" "$ROOTDIR$all" >/dev/null 2>/dev/null
echo "Mounting $all on $ROOTDIR$all" >> $LOG
fi
fi
done
fi
echo "##############################" >> $LOG
}

umount_chroot_conf()
{
echo "Function umount_chroot_conf" >> $LOG
echo "##############################" >> $LOG
echo `date` >> $LOG
for all in $ROOTDIR_MOUNT; do
# Check if file is mount target. Do not use /proc/mounts because detecting
# of modified mounted files can fail.
if mount | grep -q '.* on '"$ROOTDIR$all"' .*'; then
umount "$ROOTDIR$all" >/dev/null 2>> $LOG
echo "Umounting $all on $ROOTDIR$all" >> $LOG
# Remove temporary created files
[ -f "$all" ] && rm -f "$ROOTDIR$all"
fi
done
echo "##############################" >> $LOG
}

create_archive()
{
echo "Function create_archive" >> $LOG
echo "##############################" >> $LOG
echo `date` >> $LOG
echo "Enter in $ROOTDIR"
cd $ROOTDIR

echo "Creating fdisk -l file"
echo "Creating fdisk -l file" >> $LOG
rm -f $ROOTDIR/fdisk-l >/dev/null 2>/dev/null
fdisk -l > $ROOTDIR/fdisk-l

echo "Creating df -h file"
echo "Creating df -h file" >> $LOG
rm -f $ROOTDIR/df-h >/dev/null 2>/dev/null
df -h > $ROOTDIR/df-h

rm -f ../backup-$BRANCH-$DATE.tar.bz2 >/dev/null 2>/dev/null
tar --exclude=backup-$PREFIX-$BRANCH-$DATE.log -cjpf ../backup-$PREFIX-$BRANCH-$DATE.tar.bz2 .

cd ..
echo "Leaving $ROOTDIR. pwd=`pwd`"

for i in $DATABASES; do
echo "Deleting $BRANCH-$DATE-$i.sql" >> $LOG
echo "Deleting $BRANCH-$DATE-$i.sql"
rm -f $ROOTDIR/$BRANCH-$DATE-$i.sql >/dev/null 2>/dev/null
done

echo "Deleting other non-required files" >>$LOG
echo "Deleting other non-required files"
rm -f $ROOTDIR/df-h >/dev/null 2>/dev/null
rm -f $ROOTDIR/fdisk-l >/dev/null 2>/dev/null

echo "##############################" >> $LOG
}

echo "Creating $ROOTDIR"
mkdir -p $ROOTDIR
touch $LOG

if [ -e /etc/redhat-release ]
then
echo "Backing up system on `hostname` (Red Hat `uname -m` system)..."
echo "Backing up system on `hostname` (Red Hat `uname -m` system)..." > $LOG
fi

# make sure you're root:
ID=$(id -u)
if [ "$ID" != "0" ]
then
echo "Must run this script as root!"
echo "Must run this script as root!" >> $LOG
exit 1
fi

echo "Making required dirs";
make_dirs

echo "mount_chroot_conf";
mount_chroot_conf;

if [ $BRANCH == "Odessa" ] && [ $PREFIX == "MX" ]; then
DATABASES='exim_db mysql'
else
if [ $BRANCH == "Odessa" ] && [ $PREFIX == "GW" ]; then
DATABASES='rcubemail mysql'
fi;
fi;
echo "Backing up "$DATABASES" databases";
mysql_backup;

echo "Creating archive.";
create_archive;

echo "umount_chroot_conf";
umount_chroot_conf;

echo "pwd = `pwd`"
echo "Copying backup-$PREFIX-$BRANCH-$DATE.tar.bz2 to remote server" >> $LOG
scp -P $SSH_PORT backup-$PREFIX-$BRANCH-$DATE.tar.bz2 $SSH_DEST
scp -P $SSH_PORT $LOG $SSH_DEST

echo "Deleting backup-$PREFIX-$BRANCH-$DATE.tar.bz2" >> $LOG
rm -f backup-$PREFIX-$BRANCH-$DATE.tar.bz2 >/dev/null 2>/dev/null

echo "Deleting $LOG"
rm -f $LOG >/dev/null 2>&1

Добавить в crontab
Перезапустить crond - service crond restart

Здравая критика приветствуется.
Литература: 
/etc/init.d/named в RHEL6

среда, 9 февраля 2011 г.

Установка exim в rhel 6.

Как вам известно Redhat заменила пакет exim на postfix. Печально, но не об этом будет заметка. Опишу, как установить из исходников последнюю версию exim. Ради спортивного интереса. Тесты проводил на машине, установленной в VirtualBox. Голая система, без X-ов, только базовые пакеты.

1. service postfix stop && chkconfig postfix off && mount /dev/cdrom /mnt
2. добавить exclude=postfix в yum.conf, чтоб при обновлении системы не обновлять пакет
3. cat << EOF > /etc/yum.repos.d/cdrom.repo
[cdrom]
name=cdrom
baseurl=file:///mnt/Server
enabled=1
gpgcheck=0
EOF
4. yum -y install gcc gcc-c++ pcre-devel mysql-devel openldap-devel db4-devel ncurces-devel
5. скопировать exim-4.74.tar.bz2 libspf2-1.2.9.tar.gz libsrs_alt-1.0.tar.bz2 Makefile
6. распаковать, mv Makefile exim-4.74/Local
7. groupadd -g 93 exim, useradd -g exim -G mail -d /var/spool/exim -s /sbin/nologin -u 93 exim
8. cd libsrs_alt-1.0 && ./configure --disable-debug --enable-static && make && make install
9. cd libspf2-1.2.9 && ./configure --disable-debug --enable-static && make && make install
10. echo "/usr/local/lib" > /etc/ld.so.conf.d/spf.conf
11. ldconfig -v | grep spf, чтоб убедиться что ld подхватил путь
12. cd exim-4.74 && make && make install
13. cat <<EOF >> /var/lib/alternatives/mta
/usr/sbin/sendmail.exim
10
/usr/bin/mailq.exim
/usr/bin/runq.exim
/usr/bin/rsmtp.exim
/usr/bin/rmail.exim
/etc/pam.d/exim
/usr/bin/newaliases.exim
/usr/lib/sendmail.exim
/usr/share/man/man1/postfix.1.gz

EOF
14. cp /etc/pam.d/smtp.postfix /etc/pam.d/smtp.exim
15. ln -s /usr/sbin/exim /usr/lib/sendmail.exim
ln -s /usr/sbin/exim /usr/sbin/sendmail.exim
ln -s /usr/sbin/exim /usr/bin/rmail.exim
ln -s /usr/sbin/exim /usr/bin/mailq.exim
ln -s /usr/sbin/exim /usr/bin/newaliases.exim
15. alternatives --config mta, выбираем exim
16. конфигурируем/тестируем exim (скопировать конфиги в /etc/exim, скопировать скрипт запуска ...)
17. удаляем ненужные теперь пакеты yum -y erase gcc gcc-c++ pcre-devel mysql-devel openldap-devel db4-devel ncurces-devel

понедельник, 7 февраля 2011 г.

Проблемы с grub

История не новая. Изредка каждый сталкивается с проблемами grub. Пока не забыл опишу ситуацию, случившуюся в четверг 6.01.2011, так сказать в ночь/вечер перед Рождеством. Звонит админ с филиала и радостно сообщает "сервер не грузится". Ладно, берем безлимитный телефон и начинаем общаться с админом, чтоб выяснить чтоже случилось на самом деле. Сервер используется под smtp+imap и играет важную роль.
Итак, при загрузке выдается "GRUB Loading, Please Wait Error 17".
Далее не грузится и ничего не даёт сделать. Такую проблему с grub надо решать перестановкой grub. Админ выкачивает по корпоративному каналу и записывает образ rhel 5.6. Грузится linux rescue, без поддержки сети (очень зря, чтоб сэкономить время и не рассказывать как пишутся разные команды. Андрюха привет, подучи инглиш!!!!!).
grub-install /dev/mapper/nvidia_systemp. Казалось бы, теперь все получится и наступит счастье, но следующая загрузка показывает меню grub, но выдает ошибку error 15: File not found. Такс, возвращаемся опять в rescue режим. Опять запускаем grub-install и наблюдаем ошибку error 22 при инсталляции stage 1.5. Открываем командную строчку grub:
root (hd0,0)
setup (hd0)
grub error 22: file not found

Передо мной замаячила перспектива поехать на выходные за 700 км на филиал. Админу реально было сложно объяснить, каждую команду приходилось по буквам говорить. Стараясь отогнать мысли о поездке подальше, решил подключить сеть при загрузке, чтоб самому как-то посмотреть/исправить. Итак, очередная загрузка linux rescue, с поддержкой сети, вбить ип/маску/шлюз, сделать chroot /mnt/sysimage, запустить service sshd restart и, о чудо, я смог законнектиться.
Первым делом смотрим список смонтированных систем и опции монтирования (mount), ничего интересного. Пробуем grub-install, ошибка на месте.
Стал гуглить, ища решение проблемы, куча однотипных постов, у кого-то решается занулением 446 байт загрузочного диска и переустановкой grub, у кого не помогает. И тут мне в голову приходит мысль о проверке файловой системы, поскольку на сервере fake-raid (древний сервер), то возможно проблема кроется здесь. Отмонтировать раздел /boot (umount /boot), проверить (e2fsck -y /dev/mapper/nvidia_systemp1). При проверке раздела вылезло несколько логических ошибок, которые были поправлены. Пробуем снова установить grub и он таки ставится.  Далее очередь за всеми файловыми системами. Корневую файловую систему проверять в режиме "только чтение" (mount -n -o remount,ro /). Треть файловых систем выдало ошибки при проверке. Теперь самый трепетный момент - пробуем загрузиться как обычно. Загрузились, все нормально, но от fake-raid надо избавиться при первой же возможности. Данный текст не претендует на истину в последней инстанции, здесь я просто описал конкретную ситуацию. Может кому поможет.

Перенос системы на бОльший жесткий диск.

Ничего нового не опишу, только грабли, на которые встал. Сервер используется под почту - smtp+imap, настроен программынй raid с помощью mdadm (заметка по настройке проскакивала ранее). Размер жесткого диска в 120G стал заканчиваться спустя 2 года.
Цель: перенести систему на диск 400G, создать программный raid.
Теперь, собственно, как все происходило. Во-первых, хочется заметить, что я нахожусь от сервера за 500 км, так что пришлось воспользоваться услугами местного админа (сервер не имеет ни IP-kvm, ни power-on-net). Вечером в пятницу админ выключил сервер, подкинул новый жесткий диск. Сервер загрузился со старого винчестера, затем я удаленно разбил с помощью fdisk новый жесткий диск, создал файловые системы (mkfs.ext3 -L LABELX /dev/sdbY), добавил сигнатуру на раздел со swap (mkswap /dev/sdb9), создал точки монтирования (mkdir -p /mnt/sdb{1,...10}), подмонтировал разделы (mount /dev/sdbY /mnt/sdbY), скопировал данные со старых разделов на новые, предварительно остановив некоторые сервисы, (cp -dpRx . /mnt/sdbY). Прописал загрузчик grub в mbr.
Тут, как говорится, я дал маху. Сначала скопировал, потом поменял строки в /etc/fstab и /boot/grub/grub.conf. Это да, промах.
Перезагрузка без старого винта и сервер счастливо не смог загрузить ядро. Ничего страшного - объясняем админу по телефону. что прописать в меню grub (хорошо, что админ вполне адекватный работает). Ядро загрузилось, но не смогло найти корневую файловую систему. Супер! Возвращаемся назад к меню grub (конечно, передернув сервер по питанию) и правим не только параметр root (hd0,0), но и kernel /vmlinuz*** ro root=/dev/sda2. Вроде все должно быть в порядке, НО НЕ ГРУЗИТСЯ с ошибкой "mkrootdev: label / not found" 
Тут до меня доходит, что initrd сохранил запись о том, что корневая файловая системы находится на /dev/md6 (старая информация), и соот. не может найти устройство. Что делать? Грузиться с live-cd или режиме rescue. Не вопрос, админ предварительно по моему указанию выкачал по корпоративному каналу образ RHEL5.6 (как будто я предчувствовал такие проблемы :)) и записал на DVD.
Загрузка, linux rescue, включить поддержку сети (предполагал, что зайду по сети и сам дальше сделаю), настроить нужный IP, gateway. Теперь самое главное, chroot сделать, но тут очередная засада - предыдущая инсталляция НЕ найдена и автоматически не поднялись необходимые устройства. Ладно, объясняем по телефону что надо сделать (распаковать initrd, изменить init, запаковать initrd):
mount /dev/sda2 /mnt/sysimage
mount /dev/sda1 /mnt/sysimage/boot
chroot /mnt/sysimage
mkdir -p /tt
cd /tt
cp /boot/initrd-2.6.18-194.11.3.img .
cat initrd-2.6.18-194.11.3.img | gunzip | cpio -i
правим init - убираем все, что касается старых устройств, меняем /dev/md6 на /dev/sda2, убираем resume /dev/md6, сохраняем изменения
rm -f initrd-2.6.18-194.11.3.img
find . -print | cpio -H newc -o | gzip -9 -c ../initrd_new.img
cp ../initrd_new.img /boot
Отлично.
Грузимся с жесткого диска, правим меню grub (на предыдущем этапе благополучно забыли сразу поменять). Оно грузится!!! Но опять лажа - записи в /etc/fstab старые и как дошло до проверки файловых систем - тут же выкинуло предложение ввести пароль root и занятся проверкой/монированием файловых систем самостоятельно.
Опять грузимся с диска в linux rescue, меняем записи в /etc/fstab на правильные, заодно и grub.conf. Теперь система нормально загрузилась.
Вообщем, пример довольно поучительный, правильное планирование переноса никто не отменяет + надо думать за какое-то удаленное управление системой.