Unbound, DNSSEC и локальные зоны

На домашнем сервере крутится Unbound, на котором я включил DNSSEC для корневых зон. Для смелых и столь же бесчеловечных экспериментов поднял в виртуалках виндовый домен homelab.local. И решил прописать в Unbound локальные зоны. Точнее — stub-зоны, чтобы с домашних компов можно было обращаться к компам из зоны homelab.local по именам.

Прописал всё как положено:

stub-zone:
   name: "homelab.local"
   stub-addr: 192.168.2.3

Но имена так и не резолвились. Начал копать вокруг файрволов — нет, не файрволы, все запросы проходят, сервера отвечают. То есть, я вижу, как идёт запрос с Unbound на DNS-сервер 192.168.2.3 (это контроллер домена homelab.local), и даже вижу, как на Unbound с контроллера приходит ответ, но фактически же Unbound мне возвращает SERVFAIL.

Что, в общем-то, логично, если подумать. Корневая зона .local не имеет подписи, соответственно, Unbound трактовал её как паленую. Указание в конфиге:

domain-insecure: "homelab.local"

указанную проблему сняло, и имена зоны homelab.local начали разрешаться.

Ремонт файловой системы UFS2

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

fsck -fy /dev/da0a

...

после одного из разделов fsck доложил, что часть ошибок исправлена, но filesystem is still dirty, please rerun fsck. Я перезапустил, а в ответ мне:

fsck -fy /dev/da0d

ioctl (GCINFO): Inappropriate ioctl for device

fsck_ufs: /dev/da0d: can't read disk label Хотя визуально все разделы на месте, о чём подтверждали все утилиты. Поиск по интернету конкретного рецепта не принёс, но навёл на размышления о повреждённом суперблоке. В итоге вылечил таки.

Сначала надо выяснить, где размещаются копии суперблока. Делаем раз:

newfs -N /dev/da0d

/dev/da0d: 1024.0MB (2097152 sectors) block size 32768, fragment size 4096

    using 4 cylinder groups of 256.03MB, 8193 blks, 32896 inodes.

super-block backups (for fsck -b #) at:

192, 524544, 1048896, 1573248 Нужно починить файловую систему с правильным суперблоком. Сделать это можно командой fsck_ffs::

fsck_ffs -b 1573248 /dev/da0d

Ключ -b 1573248 указывает, что нужно при ремонте ФС нужно использовать не первый суперблок, а тот, который лежит по смещению 1573248. После этого система починилась нормально. указывает, что нужно при ремонте ФС нужно использовать не первый суперблок, а тот, который лежит по смещению 1573248. После этого система починилась нормально.

Зашифровать файл

Задался вопросом, а как можно зашифровать отдельный файл на фре. Про geli или gbde всё ясно, а вот так, чтобы отдельный файл? Раньше шифровал с помощью rar — запихивал в зашифрованый архив. В свете того, что нужно на домашнем хранилище кое-что бэкапить, причём желательно так, чтобы «никто не нашёл», а лишние сущности плодить (сиречь ставить rar) не хочется, изыскал на просторах интернетов рецепт щастья шифрования отдельных файлов. Рецепт незамысловат — функцией шифрования обладает openssl. Чтобы зашифровать файл, нужно сделать раз: шифрования отдельных файлов. Рецепт незамысловат — функцией шифрования обладает openssl. Чтобы зашифровать файл, нужно сделать раз:

openssl enc -aes-256-xts -in file.dat -out file.dat.enc Шифрует файл file.dat с помощью алгоритма AES-256 и сохраняет шифрованный файл в файле с помощью алгоритма AES-256 и сохраняет шифрованный файл в файле file.dat.enc

Чтобы расшифровать, делаем два:

openssl enc -d -aes-256-xts -in file.dat.enc -out file.dat Ещё у openssl enc есть прикольная функция — оно умеет преобразовывать бинарники в base64. Делается это с помощью ключа -a. Например, рассмотрим ситуацию, когда надо передать бинарник, а воспользоваться scp или подобными утилитами нет возможности. Тогда можно сделать так:

openssl enc -a -in file.dat -out file.dat.base64 И получившийся в результате file.dat.base64 можно просто передать хоть прямо через буфер между двумя терминалами, а на принимаемой стороне потом сделать: можно просто передать хоть прямо через буфер между двумя терминалами, а на принимаемой стороне потом сделать:

openssl enc -a -d -in file.dat.base64 -out file.dat

rtorrent + ruTorrent + HTTPRPC + scgi_local

На домашнем сервере запилил себе rtorrent+ruTorrent. Сначала делал по старинке — через mod_scgi. Потом обнаружил, что ruTorrent периодически жалуется, что, дескать, «rtorrent не отвечает». В интернетах утверждают, что связаны эти грабли с тем, что для mod_scgi нынешние скорости закачки (а следовательно, и обновления информации) зело велики и оно не справляется. Не то что бы это сильно влияло на функционал, но ведь косячок-с налицо. Поэтому решил с этим побороться. Для борьбы рекомендуется использовать специальный плагин для ruTorrent, который называется HTTPRPC..

Установка плагина нехитрая — нужно просто скопировать папку плагина в подкаталог plugins внутри каталога ruTorrent. После этого можно отключить в конфигах апача модуль mod_scgi (ежели был подключён) и убрать строку

SCGIMount /RPC2 127.0.0.1:5000

Я решил пойти ещё дальше. Вычитал, что работа через сетевой сокет (даже если он висит на localhost) — это «несекурненько». И есть возможность организовать обмен информацией между rtorrent и ruTorrent через файловый сокет. Для этого требуется в конфиге rtorrent вписать такое:

scgi_local = /mnt/storage0/sys/tmp/rtorrent-rpc.socket

Где /mnt/storage0/sys/tmp/rtorrent-rpc.socket — путь к файлу сокета. — путь к файлу сокета.

После перезапуска rtorrent будет создан файл /mnt/storage0/sys/tmp/rtorrent-rpc.socket

Чтобы ruTorrent вкурил, что ему теперь надо работать не через сетевой сокет, а через файловый, нужно в конфиге config.php от ruTorrent изобразить следующее: от ruTorrent изобразить следующее:

$scgi_port = 0;
        $scgi_host = "unix:///mnt/storage0/sys/tmp/rtorrent-rpc.socket";

Строчку

$XMLRPCMountPoint = "/RPC2";

ОСТАВЛЯЕМ КАК ЕСТЬ. То есть, её не трогаем.

После этого запускаем rtorrent, перезапускаем апач (если вносились изменения в его конфиг) и наслаждаемся.

У меня ещё возникли грабли с тем, что права на создаваемый сокет не позволяли туда писать пользователю www, от которого работает апач. Пришлось немного доработать скрипт запуска rtorrent.

Добавил в rc.conf переменную rtorrent_socket:

rtorrent_socket="/mnt/storage0/sys/tmp/rtorrent-rpc.socket"

А в скрипте запуска добавил проверку на существование сокета при запуске rtorrent, и если сокет существует, то меняем ему владельца на rtorrent:www. В целом скрипт запуска rtorrent выглядит так (нужные строки, относящиеся к правам на файловый сокет выделены красным):

#!/bin/sh

# PROVIDE: rtorrent
# REQUIRE: NETWORKING SERVERS
# BEFORE: DAEMON
# KEYWORD: shutdown

#
# Add the following lines to /etc/rc.conf to enable rtorrent at startup
# rtorrent (bool): Set to "NO" by default.
#                Set it to "YES" to enable rtorrent
# rtorrent_user (str): Set to user running rtorrent
#                    (default 'p2p')
# rtorrent_home (str): Set to home directory of user running rtorrent
#                    (default /home/${rtorrent_user})

. /etc/rc.subr

name="rtorrent"
rcvar=rtorrent_enable

load_rc_config $name

rtorrent_enable=${rtorrent_enable:-"NO"}
rtorrent_user=${rtorrent_user:-"rtorrent"}
rtorrent_home=${rtorrent_home:-"/home/$rtorrent_user"}
rtorrent_socket=${rtorrent_socket:-"/tmp/rtorrent-rpc.socket"}

required_dirs=${rtorrent_home}
required_files="${rtorrent_home}/.rtorrent.rc"

start_cmd="${name}_start"
stop_cmd="${name}_stop"

rtorrent_start()
{
  if [ ! -f /var/run/${name}.run ]
  then
    cd ${rtorrent_home}
    su ${rtorrent_user} -c "/usr/local/bin/screen -dmS rtorrent_init /usr/local/bin/rtorrent"
    touch /var/run/${name}.run
    echo "Started ${name}."
  else
    echo "${name} seems to be already running — remove /var/run/${name}.run manually if needed."
  fi
  sleep 3
  if [ -e ${rtorrent_socket} ]
  then
    chown rtorrent:www ${rtorrent_socket}
  fi
}

rtorrent_stop()
{
  if [ -f /var/run/${name}.run ]
  then
    kill -SIGINT `ps -xa| awk '/rtorrent/ && ! /rtorrent_init/ && ! /stop/ { print $1 }'`
    rm -f /var/run/${name}.run
    echo "Stopped ${name}."
  else
    echo "${name} doesn't seem to be running — create /var/run/${name}.run if needed."
  fi
}

run_rc_command "$1"

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

L2TP+IPSec

Решил наконец-то разобраться с этой мутной доселе темой — IPSec. И в частности — с её реализацией на FreeBSD.

Если не вдаваться в кровавые подробности, то в общих чертах дело обстоит так: L2TP настраивается как обычно, абсолютно без какой-либо оглядки на то, что должен быть ещё и IPSec. Я это делаю обычно на mpd5, путём небольшой правки дефолтного конфига, который идёт с mpd.

IPSec в данном случае логически представляет собой некую надстройку, в которой нужно указать, что «трафик, идущий с такого-то IP на такой-то по таким-то портам и такого-то протокола — требует шифрования (это так называемый „транспортный режим“ работы IPSec). В случае с L2TP эти параметры выставляются так: трафик с любого IP и любого порта, идущий на IP-адрес сервера и порт 1701 (стандартный порт L2TP) по протоколу UDP, потребно шифровать.

Во фре IPSec реализуется с помощью пакета ipsec-tools (он же racoon).

После установки ipsec-tools потребуются следующие модификации в /etc/rc.conf:

% cat /etc/rc.conf
...
ipsec_program="/sbin/setkey"
ipsec_file="/usr/local/etc/racoon/setkey.conf"
racoon_enable="YES"
racoon_flags="-l /var/log/racoon.log"
...

Далее идут конфиги самого racoon и утилиты setkey, а также список pre-shared key по-клиентно:

% cat /usr/local/etc/racoon/setkey.conf
flush;
spdflush;
spdadd 0.0.0.0/0[any] 192.168.2.2[1701] udp -P in  ipsec esp/transport//require;
spdadd 192.168.2.2[1701] 0.0.0.0/0[any] udp -P out ipsec esp/transport//require;
 % cat /usr/local/etc/racoon/racoon.conf
path    pre_shared_key "/usr/local/etc/racoon/psk.txt";
log     debug;
listen
{
        isakmp          192.168.2.2 [500];
        isakmp_natt     192.168.2.2 [4500];
        strict_address;
}

remote anonymous
{
        exchange_mode    main;
        passive          on;
        proposal_check   obey;
        support_proxy    on;
        nat_traversal    off;
        ike_frag         on;
        dpd_delay        20;

        proposal
        {
                encryption_algorithm  aes;
                hash_algorithm        sha1;
                authentication_method pre_shared_key;
                dh_group              modp1024;
        }

        proposal
        {
                encryption_algorithm  3des;
                hash_algorithm        sha1;
                authentication_method pre_shared_key;
                dh_group              modp1024;
        }
}

sainfo anonymous
{
        encryption_algorithm     aes,3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm    deflate;
        pfs_group                modp1024;
}
 # cat /usr/local/etc/racoon/psk.txt
192.168.2.4     Password
192.168.2.5     Password1
192.168.1.4     Password2

Клиентом выступает маршрутизатор Mikrotik. Там схема та же — настраивается как обычно L2TP-клиент, а затем топаем в раздел IP ==> IPSec и добавляем записи на вкладке Peers и Policies. Вкладка Peers:

Собственно, здесь я изменил только поля Address (в нём указываем IP VPN-сервера) и Secret (это и есть pre-shared key), а также поле Hash algorithm (по дефолту стоял md5). Остальные поля оставил как есть.

И добавляем запись в разделе Policies:

«Как вы понимаете из названия» (с), поля Src. address и SA src. address — это IP-адрес клиента, а Dst. address и SA dst. address — это адрес VPN-сервера.

Часовой пояс во FreeBSD

Запилил тут на Амазоне сервер на фре, и возник вопрос — а как выставить часовой пояс? Раньше как-то на этапе установки системы в инсталляторе выставлял, и всё. И поэтому не в курсе, как это физически реализуется. Оказывается, нужно выбрать файл со своим часовым поясом в /usr/share/zoneinfo и скопировать его в /etc. То есть, для Москвы нужно выполнить команду:

# cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime

Как рулить инсталляциями server core

Нашёл мега-полезную ссылку про то, как можно жить с server core. Прямо так и называется: «Сore 2012: Руководство по выживанию». Много всяких полезных штук — как настраивать сетку, как просматривать логи и так далее, через powershell. про то, как можно жить с server core. Прямо так и называется: «Сore 2012: Руководство по выживанию». Много всяких полезных штук — как настраивать сетку, как просматривать логи и так далее, через powershell.

Минимальное ядро и загрузка с ZFS

Попытался тут оторвать все модули и жёстко впаять необходимое в ядро. Система грузится с ZFS, а также используется AIO для самбы, ибо сильно увеличивает скорость работы самбовых шар. Проверил как-то ради шутки — без AIO гиговый файл копировался 9 минут 10 секунд, с включенным AIO — 1 минуту 45 секунд. Разница примерно 6 раз.

В общем, пока (на момент версии FreeBSD 9.2) выясняется, что zfs.ko и  и opensolaris.ko в ядро не включить, можно только модулями ядра грузить. Добавил в ядро только AIO: в ядро не включить, можно только модулями ядра грузить. Добавил в ядро только AIO:

options VFS_AIO

И прописал в make.conf:

MODULES_OVERRIDE=zfs opensolaris

Собрал ядро, установил, попытался перезагрузиться и получил облом. При загрузке система стала утверждать, что не знает, откуда смонтировать root. Оказывается, нужен ещё модуль krpc.ko, отвечающий за реализацию RPC в солярке (откуда и портирована ZFS). Причём судя по обрывочным сведениям, нужен он только на 64-битной фре, на 32-битной вроде как нет. Проверять, честно говоря, влом :-), отвечающий за реализацию RPC в солярке (откуда и портирована ZFS). Причём судя по обрывочным сведениям, нужен он только на 64-битной фре, на 32-битной вроде как нет. Проверять, честно говоря, влом :-)

Правим make.conf:

MODULES_OVERRIDE=zfs opensolaris krpc

Пересобираем ядро, ставим, перезагружаемся — и всё взлетает нормально.

Удалённое выполнение команд по ssh

В свете полученных знаний из ссылок в предыдущем посте решил начать жить по-новому. А именно — бэкапные файлы с фри на фрю складывать не по SMB или FTP, как раньше, а по scp или ssh. Подумал, и решил, что удобнее будет через ssh — туда загоняется поток через pipe, а на удалённой стороне из pipe складывается в локальнный (для удалённого сервера-хранилища бэкапов) файл. Проверил даже скорость — в конкретной сети гиговый файл через SMB скопировался за  1 минуту 48 секунд, а то же самое, но через ssh — за 1 минуту 35 секунд. Профит налицо :-)

Запустил mcedit и начал ваять. Со скриптом бэкапа системы проблем не возникло:

# cat system_backup.sh
#!/bin/sh
backup_dir="/home/backup_operator/backups/os/buhserver-ttr"
keyfile="/home/gateway/service/keyfile.ppk"
username="backup_operator@backup-ttr"

DATE=`date +%Y.%m.%d`

dump -0 -L -u -C 32 -f  / | gzip -5 | ssh  -q -i ${keyfile} ${username} "cat > ${backup_dir}/${DATE}_root.dump.gz"
dump -0 -L -u -C 32 -f  /usr | gzip -5 | ssh  -q -i ${keyfile} ${username} "cat > ${backup_dir}/${DATE}_usr.dump.gz"
dump -0 -L -u -C 32 -f  /var | gzip -5 | ssh -q -i ${keyfile} ${username} "cat > ${backup_dir}/${DATE}_var.dump.gz"
dump -0 -L -u -C 32 -f  /home | gzip -5 | ssh -q -i ${keyfile} ${username} "cat > ${backup_dir}/${DATE}_home.dump."

keyfile — это private key для авторизации на удалённом сервере, backup_operator — имя пользователя на том сервере, а backup-ttr — имя самого сервера, где у нас бэкапы хранятся. Получается следующее — dump дампит раздел в stdout, откуда это перегоняется в gzip через pipe, откуда перегоняется вот в эту конструкцию:

ssh -q -i ${keyfile} ${username} "cat > ${backup_dir}/${DATE}_root.dump.gz" Т. е. мы подключаемся к серверу backup-ttr, и выполняем там команду "cat > {backup_dir}/${DATE}_root.dump.gz". Которая читает из stdin сжатый gzip'ом и прокинутый по сети поток и сохраняет его в файл {backup_dir}/${DATE}_root.dump.gz.". Которая читает из stdin сжатый gzip'ом и прокинутый по сети поток и сохраняет его в файл {backup_dir}/${DATE}_root.dump.gz.

В общем, тут всё просто. А вот с бэкапом почты пришлось повозиться. Было принято решение не просто каждый день сваливать полный бэкап почтовой базы и удалять старые копии, а сделать инкрементный бэкап. А для этого нужно производить определённые манипуляции на стороне хранилища бэкапов. Можно, конечно, сделать второй скрипт, и запускать его по крону на стороне хранилища. Но «Шурик, это же не наш метод!». Это придётся контролировать две точки выполнения одной операции — бэкапа почты, так как задействованы будут два скрипта, два крона, два компа и т. д.

Суть задачи такова. Допустим, у нас в день 1 выполняется полный бэкап почтовой базы. В день 2 — только то, что изменилось или добавилось со времени исполнения 1-го бэкапа, отработавшего в день 1. В день 3 — только изменения с момента бэкапа в день 2, и так далее до конца недели. Допустим, за первую неделю у нас сделано 7 архивов, с именами, соответственно, mail1.tgz, mail2.tgz, ... mail7.tgz. Где mail1.tgz — это полная копия базы, а остальные — дельты изменений. Неделя закончилась, надо как-то сделать ротацию бэкапов. То есть, куда-то деть эти 7 архивов. Можно их тупо удалить. Но как-то хочется, чтобы у нас хотя бы двухнедельная история бэкапов хранилась. Самое простое — эти архивы переименовать. Например, по схеме mail1.tgz.bak, mail2.tgz.bak и так далее. Тут-то и началось самое интересное.

Сначала решил сделать как-нибудь так:

for i in `ssh -q -i ${keyfile} ${username} "ls ${backup_dir}/*.gz"`
do
    ssh -q -i ${keyfile} ${username} "mv ${i} ${i}.bak"
done

Но это получается 1 вызов ssh для получения списка файлов, и потом ещё в цикле 7 раз запускается ssh, чтобы переименовать каждый отдельный файл. Касичок кагбэ, неоптимальненко.

Камрады в интернете посоветовали сделать как-нибудь так:

ssh -q user@remoteserver 'ls -f /path/*.tgz | while read f; do "$"» };done' Я попробовал, не прокатило. Полез искать точный синтаксис однострочного оформления циклов в shell. Нашёл несколько примеров, подставил, но ни один не прокатил. Я так понял, что приведённые примеры — в основном для линуксового bash, а у меня фрёвая /bin/sh, поэтому и не прокатывает. Потом нашёл обрывочные сведения, что циклы типа for, foreach, while — «is not single-line friendly». Но коллективный разум мне сурово возразил, что я не прав.

Копаясь дальше, я обнаружил, что реально, однострочные конструкции типа

ls | while read f;do echo ${f};done работают, будучи запущены в /bin/sh и локально. Но не работают удалённо. Потом до меня дошло, что локально-то я запустил /bin/sh, и в ней пускаю все эти конструкции. Но по дефолту у пользователя backup_operator оболочка прописана /bin/csh, и синтаксис там может отличаться.

Не вопрос, топаем на удалённый сервер и делаем там:

pw usermod backup_operator -s /bin/sh Меняем, иными словами, ему оболочку на sh вместо csh. После этого конструкция

ssh -i keyfile.ppk backup_operator@backup-ttr "ls /home/ | while read f; do echo ${f}; done;" просто обязана была заработать. Но не заработала... А написала, что f: Undefined variable.

В процессе дальнейшего общения с коллективным разумом выяснилось следующее:

The shell will expand variables in double-quotes, but not in single-quotes.

что если команду для удалённого исполнения заключать в двойные кавычки, то оболочка подставляет значение переменной вместо имени переменной. А в этой ситуации нужно, чтобы имя переменной передавалось как есть на дальнюю сторону, и значение вычислялось и использовалось уже там. Поэтому эту команду нужно заключать в ОДИНАРНЫЕ кавычки. То есть, вот так:

ssh -i keyfile.ppk backup_operator@backup-ttr 'ls /home/ | while read f; do echo ${f}; done;' Но есть и другой путь — просто ставить backslash перед символом переменной. То есть, можно сделать вот так:

ssh -i keyfile.ppk backup_operator@backup-ttr "ls /home/ | while read f; do echo \${f}; done;' Мне так пришлось сделать, потому что по итогу потребовалось, чтобы в одной команде были и локальные и удалённые переменные. Т. е. финальная рабочая конструкция выглядит вот так:

ssh -i keyfile.ppk backup_operator@backup-ttr "cd ${backup_dir}; ls ./ | while read f; do mv \${f} \${f}.bak; done"