Запуск nodejs через systemd

Приложения nodejs собираются обычно на коленке и для них нет стандартного способа запуска как демон. Наиболее короткий и корректный способ сделать это сейчас — через systemd. Для этого создаем свежий конфиг в /etc/systemd/system/, называем по имени приложения, например /etc/systemd/system/myapp.service с содержимым:

[Service]
WorkingDirectory=/opt/myapp/
ExecStart=/usr/bin/node /opt/myapp/app.js
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myapp
User=igogoo
Group=igogoo
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Обычно в /etc кладутся не сами конфиги, а симлинки на действительные конфиги в /lib/systemd/system/, но поскольку наш демон кастомный, а директория /etc бэкапится полностью, создадим его прямо здесь.
Далее релодим конфигурацию и включаем демона на автостарт

systemctl daemon-reload
systemctl enable node-app.service

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

systemctl start myapp
systemctl stop myapp
systemctl restart myapp
systemctl status node-app.service

Перезапускаем виртуалку, проверяем, что всё работает. Готово.

Исправление графиков munin

Бывает, что на графиках мунина появляются выбивающиеся из общей картины пики, значения которых на порядки выше типичных. Из-за этого график теряет репрезентативность и на долгое время становится бесполезным. Исправить это можно, для этого нужно выкинуть выбивающиеся значения.

Графики munin хранит в rrd-формате в /var/lib/munin. Чтобы исправить заходим туда и конвертируем rrd в xml:

cd /var/lib/munin/localdomain
cp x.xml x.xml.bak
rrdtool dump x.rrd > x.xml

Открываем получившийся xml, удаляем оттуда ненужное и конвертируем обратно:

rrdtool restore x.xml > x.rrd

Изменение PATH в Debian buster

В дистрибутиве Buster зачем-то подчистили переменную PATH от важных и полезных путей, в частности /usr/sbin. В итоге полезные и весьма нередко используемые команды вроде dpkg-reconfigure, fdisk, blkid и многие другие стали недоступны без указания точного пути. Интерпретатор пишет command not found и усё на этом.

Возвращаем к обратному добавлением в .bashrc:

export PATH=$PATH:/usr/sbin

После следующего логина всё работает

Обновление с репозитария без подписи

Начиная примерно с дистрибутива buster стандартный установщик дебиана apt-get требует подписи всех используемых репозитариев. Обычный способ использования репозитария — добавление его подписи в систему. Это, однако, не всегда удобно и целесообразно. Иногда нужно что-то взять из сторонних репозитариев не добавляя подписи в систему. Это можно сделать разово при обновлении с помощью опции

apt-get update --allow-unauthenticated

Намного удобнее сделать это навсегда в отдельном списке репозитариев по типу:

deb [arch=amd64 allow-insecure=yes] http://www.deb-something-exampe.org buster main

Использование репозитериев без подписи в общем случае не безопасно, но в определённых случаях разумно обновляться именно так.

UPD: Обновленный и более хороший способ

rsync на нестандартном порту с докачкой

Бывают ситуации, когда нужно скачать несколько больших файлов по ssh/scp, а канал очень узкий, так что приходится качать несколько раз. Иногда ещё и сам ssh висит на нестандартном порту и его явно нужно указать.

Первая часть задачи — заставить rsync работать с докачкой, без дотошного сравнения файлов и быстро. Это делается ключом --append-verify, который докачивает файл полность и затем сравнивает контрольную сумму. Если она расходится, то файл копируется заново. Это практически идеальный вариант для закачки многогигабайтовых образов по тонкому каналу.

Вторая часть — заставить rsync подключаться на другой порт. Прямой опции, кторая позволяет так делать нет, но можно указать опцию для ssh, через ключ «-e».

В итоге получается команда вроде

rsync -av --append-verify -e "ssh -p 55667" backup-srv:/data/backup/ /data/backup-srv

md127

После изменения конфигурации софтового рейда, mdadm-устройств некоторые из них именуются странным образом вроде md127. Это уже с изменённым /etc/mdadm/mdadm.conf.

Решается генерацией initramfs заново:

sudo update-initramfs -u

При следующей загрузке устройства именуются как надо.

Управление supervisorctl без рутовых прав

Для разных самописных процессов, которые нужно запускать как демон, есть прекрасный инструмент supervisor. Управление запуском процесса в нём происходит с помощью конфигов предельно простого синтаксиса (и в тоже время покрывающих почти все необходимости) в /etc/supervisor.d/ и команд наподобие:

supervisorctl start myapp
supervisorctl stop myapp
supervisorctl restart myapp

т.е. интерфейс полностью аналогичный управлению демонами в ОС.

Во многих случаях процесс, который запускают как демон, запускается от непривелигерованного пользователя. И от этого же пользователя работают реальные пользователи/разработчики, которым может понадобится управление демоном. Стандартный способ сделать это — через sudo:

sudo supervisorctl restart myapp

Но если это виртуальная машина, в которой supervisor нужен именно для приложений от этого пользователя, то можно сделать и в обход root совсем. Для этого необходимо поправить в /etc/supervisor/supervisord.conf:

[unix_http_server]
file=/var/run/supervisor.sock
chmod=0770
chown=someuser:someuser

После этого supervisor будет управляться из под пользователя. У способа есть недостаток, что теперь supervisor заточен на приложения этого пользователя/группы, но часто это не является препятствием.

Поменять почтовый адрес root

Важной частью стабильной работы сервера является мониторинг событий на нём. Кроме специализированных утилит вроде logcheck и прочих автоматических анализаторов логов, есть более простые способы получаения важных событий. В частности, многие демоны и прикладные программы используют отправляют сообщения об ошибках и других важных событиях на почтовый адрес суперпользователя root@hostname самого сервера. В настроенном «из коробки» сервере эта почта складывается или в директорию в домашней директории пользователя или в файл /var/mail/$USER. В обоих случаях почта хранится локально, прочитать её можно вручную локально на сервере, что редко кто делает. Перенаправим эти сообщения на другой почтовый ящик, который регулярно проверяется и читается. Это можно сделать следующими основными способами.

/etc/aliases

Это глобальный способ для любого пользователя в системе. /etc/aliases содержит почтовые алиасы алиасы локальных пользователей. Добавляя/изменяя срочку

root: system@example.com, adm@mydomain.com

получаем перенаправление почты на соответствующие адреса. После изменения файла алиасов необходимо запустить newaliases для принятия изменений.

~/.forward

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

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

Port forward to another server

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

iptables -P FORWARD ACCEPT
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -F
iptables -X
iptables -F -t nat
iptables -X -t nat

iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination NEW_IP_ADDR:443
iptables -t nat -A POSTROUTING -j MASQUERADE

Этот же способ помогает зеркалировать нужные на другом сервере.

Прошлая дата в date для разных шеллов

В разного рода скриптах иногда нужно получить значение предыдущих дат, на несколько дней назад. В стандартном линуксовом шелле bash это делается

$ date -u "+%F" -d "-3 day"

Однако, вышеуказанный способ не обязательно работает для других интерпретаторов. Для BSD стандартный шелл обычно csh, там аналогичный результат возвращается командой

$ date -v-2d "+%F"

Бэкапы windows-шар через smbfs

Важное преимущество Linux, BSD и других никсов — наличие очень мощных базовых утилиты, с помощью которых можно автоматизировать многие необходимые задачи. В частности, ssh, rsync позволяют очень удобно и надёжно создавать, передавать (в том числе и за многие километры по интернету) и всячески управлять бэкапами, а с помощью архиваторов их можно и сжимать. Windows, к сожалению, не обладает таким функционалом «из коробки», да и сторонние решения не «из коробки» не всегда подходят по ряду параметров. Поэтому, логично организовать сервер резервных копий даже в Win-сети на основе Linux и стягивать на него в плановое время данные для бэкапа. Посмотрим, как это можно не слишком умудрённо сделать.

Будем изначально подходить наиболее экономным способом: с наименьшей конфигурацией исходных машин. Для этого монтируем нужную шару, делаем копию, размонтируем. Монтируем с помощью mkfs.cifs из пакета cifs-utils. Если на машину можно зайти анонимно, так и заходим, с помощью соответствующей опции

mount -t cifs //192.168.10.99/Data $data_dir -o user=guest,guest

Можно, конечно, добавить шару в fstab и держать постоянно смонтированной, но это скорее излишне, поэтому будем её монтировать только на время бэкапа. Также, на случай возможных ошибок, выбираем директорию каждый раз новую в /tmp, получить временную директорию можно

mktemp -d

Копию будем делать дифференциальную на основе жёстких ссылок. rsync позволяет делать делать hard-linked копии сам, на основе предыдущей копии, или их можно делать отдельно, а затем заменять новые файлы. Воспользуемся вторым способом: в случае обрыва бэкапа мы получим не обновлённый, но полный бэкап, который обновится в следующий раз. В первом случае его придётся загружать полностью, что не всегда разумно если канал тонкий.

С учётом всего вышеперечисленного пишем скрипт

#!/bin/sh

softmkdir() {
	if [ ! -d "$1" ] ; then
		mkdir "$1"
		chmod 0750 "$1"
	fi
}

# SETTINGS
host=`hostname -s`
backup_dir=/backup/$host/data_rsync
len=90

softmkdir "$backup_dir"

tmpname=new
tmp_dir="$backup_dir/$tmpname"
zero_dir="$backup_dir/d`printf "%02d" 0`"
prev_dir="$backup_dir/d`printf "%02d" 1`"
last_dir="$backup_dir/d`printf "%02d" $len`"

date=`date`
echo "================================="
echo "Starting backup: $date."

if [ -d "$zero_dir" ]; then
	echo "Rotating directories up to length of $len."

	# removing last directory
	if [ -d "$last_dir" ]; then
		rm -rf "$last_dir"
	fi

	# rotating
	for i in $(seq  $len -1 0); do
		src="$backup_dir/d`printf "%02d" $i`"
		if [ -d "$src" ]; then
			mv "$src" "$backup_dir/d`printf "%02d" $(($i+1))`"
		fi
	done
fi

if [ ! -d "$tmp_dir" ] ; then
	if [ -d "$prev_dir" ] ; then
		echo "Found previous backup. Making hard-linked copy."
		cp -al "$prev_dir" "$tmp_dir"
		echo "Hard-linked copy done."
		test -d "$tmp_dir" && touch "$tmp_dir"
	else
		echo "Previous backup not found. Creating init backup."
		softmkdir "$tmp_dir"
	fi
else
	echo "Found temporary directory (not finished last backup), resuming it."
fi

data_dir=`mktemp -d`
mount -t cifs //192.168.10.24/Data $data_dir -o user=guest,guest

echo "Starting rsync..."
rsync -azK --delete --ignore-errors --stats --numeric-ids $1 $data_dir/ "$tmp_dir/Data"

test -d "$tmp_dir" && touch "$tmp_dir"
echo "Rsync done."

# moving temporary to latest
echo "Moving temporary dir name to normal backup name."
mv "$tmp_dir" "$zero_dir"

umount $data_dir
rm -d $data_dir

echo "Backup finished: $date."
echo "================================="
echo ""

Проверяем, что всё работает корректно и добавляем в cron.

Резервирование UEFI в Linux

В большинстве случаев на серверные системы Linux ставится на RAID, железный или программный. В простом случае программный рейд создаётся при помощи mdadm, в форме зеркала. Корень и разделы системные и данных легко ставятся на получившиеся /dev/mdX-разделы и такое решение отлично защищает от потери данных в случае аппаратной неисправности жесткого диска. Однако, в стандартной установке загрузчик ставится только на один жёсткий диск и в случае его поломки система не загрузится. Поставим задачу зарезервировать ещё и загрузку, т.е. сделать систему загружаемой с любого из дисков зеркала.

Будем рассматривать наиболее простую конфигурацию и разбиение носителей — 2 NVMe-тома, на которых всего по 2 раздела: для UEFI и для корня системы. Например:

# fdisk /dev/nvme0n1
...
Device          Start       End   Sectors  Size Type
/dev/nvme0n1p1   2048    526335    524288  256M EFI System
/dev/nvme0n1p2 526336 879097934 878571599  419G Linux RAID

Имеем первый раздел для EFI и второй под корень системы. Корень уже в mdadm массиве уровня 1, с ним все уже в порядке. Второй такой же носитель /dev/nvme1n1 имеет в точности такую же разбивку.

После установки получим efi-раздел, монтируемый в /boot/efi и параметры загрузки EFI вроде

# efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0002,0006,0005
Boot0000* debian        HD(1,800,80000,0496eff8-477c-4335-ada1-ce9200ddff6e)File(\EFI\debian\grubx64.efi)
Boot0002* UEFI: Built-in EFI Shell      Vendor(5023b95c-db26-429b-a648-bd47664c8012,)..BO
Boot0005* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0006* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0007* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO
Boot0008* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO

Раздел /dev/nvme0n1p1 непустой и содержит загрузчик. Аналогичный раздел со второго носителя /dev/nvme1n1p1 пустой. Запишем загрузчик в него и добавим его в последовательность загрузки. В случае BIOS срабатывал штатный grub-install на второй диск. Здесь аналогичный способ перепишет строчку в последовательности загрузки efiboot выше и по сути переключит загрузку на второй диск. Поступим иначе, а именно наново сделаем загрузку на второй диск, её сохраним, и затем вернём загрузку с первого.

Для начала подменим уже смонтированный раздел /dev/nvme0n1p1 аналогичным со второго диска

umount /boot/efi
mount -o umask=0077 /dev/nvme1n1p1 /boot/efi

и убедимся, что смонтировался именно он. Если этот раздел не отформатирован при загрузке сделаем это сейчас

mkfs.vfat -F32 /dev/nvme1n1p1

FAT32 может не быть в системе изначально, он доустанавливается из dosfstools. Раздел должен быть пустым.

Далее стандартными средствами, по мануалу дебиана прописываем загрузчик:

apt-get install --reinstall grub-efi
grub-install /dev/nvme1n1
update-grub

В результате получаем загрузчик в /boot/efi (смонтированный на /dev/nvme1n1p1) и строчку в последовательности UEFI-загрузки

# efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0002,0006,0005
Boot0000* debian       HD(1,800,80000,37194fa6-dca2-491e-a0cf-a7fded36e2c5)File(\EFI\debian\grubx64.efi)
Boot0002* UEFI: Built-in EFI Shell      Vendor(5023b95c-db26-429b-a648-bd47664c8012,)..BO
Boot0005* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0006* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0007* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO
Boot0008* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO

Посмотрим таблицу идентификаторов UUID

# blkid
/dev/nvme0n1p1: UUID="AC77-0809" TYPE="vfat" PARTUUID="0496eff8-477c-4335-ada1-ce9200ddff6e"
/dev/nvme0n1p2: UUID="9643fce5-dcfb-1ff3-d03d-14004857da61" UUID_SUB="f5c3d323-29ec-3079-0e58-3259275e26db" LABEL="s00:0" TYPE="linux_raid_member" PARTUUID="d37e4df1-07ef-467e-acf8-ab98bbb60f87"
/dev/nvme1n1p1: UUID="4072-D808" TYPE="vfat" PARTUUID="37194fa6-dca2-491e-a0cf-a7fded36e2c5"
/dev/nvme1n1p2: UUID="9643fce5-dcfb-1ff3-d03d-14004857da61" UUID_SUB="f5e0f678-9b1d-3111-df00-74096ddf7e6d" LABEL="s00:0" TYPE="linux_raid_member" PARTUUID="660ba68e-7e25-4287-a646-3f555b510533"
/dev/md0: UUID="ce3f9f38-e7d9-4c89-ae36-2127a71670e0" TYPE="ext4"
/dev/nvme0n1: PTUUID="86a68d04-a4d9-4296-8a1a-48d02b7171fb" PTTYPE="gpt"
/dev/nvme1n1: PTUUID="1bc21b63-6011-42e9-80ab-1fd120b69907" PTTYPE="gpt"

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

efibootmgr -c -d /dev/nvme1n1 -p 1 -L "debian1" -l "\EFI\debian\grubx64.efi"

Проверяем результат

# efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0002,0006,0005
Boot0000* debian        HD(1,800,80000,37194fa6-dca2-491e-a0cf-a7fded36e2c5)File(\EFI\debian\grubx64.efi)
Boot0001* debian1       HD(1,800,80000,37194fa6-dca2-491e-a0cf-a7fded36e2c5)File(\EFI\debian\grubx64.efi)
Boot0002* UEFI: Built-in EFI Shell      Vendor(5023b95c-db26-429b-a648-bd47664c8012,)..BO
Boot0005* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0006* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0007* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO
Boot0008* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO

И меняем порядок загрузки на необходимый

# efibootmgr -o 0000,0001,0002

Вторая часть — это вернуть обратно первый загрузчик. Перемонтируем обратно раздел с ним на оригинальный с первого диска

umount /boot/efi
mount /boot/efi

Убеждаемся, что смонтировался именно он, и стандартными средствами записываем загрузчик

apt-get install --reinstall grub-efi
grub-install /dev/nvme1n1
update-grub

Аналогичное можно было сделать и с помощью efibootmgr, как это делали для второго раздела.

В любом способе в конечном варианте загрузки должны видеть примерно следующую последовательность UEFI

# efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0001,0002
Boot0000* debian        HD(1,800,80000,0496eff8-477c-4335-ada1-ce9200ddff6e)File(\EFI\debian\grubx64.efi)
Boot0001* debian1       HD(1,800,80000,37194fa6-dca2-491e-a0cf-a7fded36e2c5)File(\EFI\debian\grubx64.efi)
Boot0002* UEFI: Built-in EFI Shell      Vendor(5023b95c-db26-429b-a648-bd47664c8012,)..BO
Boot0005* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0006* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,0)MAC(MAC(0cc47ada7688,1)..BO
Boot0007* UEFI: Intel(R) I350 Gigabit Network Connection        ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO
Boot0008* UEFI: IP4 Intel(R) I350 Gigabit Network Connection    ACPI(a0341d0,0)PCI(3,3)PCI(0,1)MAC(MAC(0cc47ada7689,1)..BO

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

Несколько интерфейсов в LXC

Бывает, что в создаваемых виртуальных машинах LXC нужно доступ к разным подсетям посредством нескольких интерфейсов. А изначальный конфиг виртуалки содержит настройки одного интерфейса. Дополнительный интерфейс добавляется в том же конфиге по аналогии с первым, например:

lxc.network.type = veth
lxc.network.name = eth0
lxc.network.link = lxcbr0
lxc.network.ipv4 = 12.34.56.78
lxc.network.flags = up

lxc.network.type = veth
lxc.network.link = br1
lxc.network.ipv4 = 10.0.3.5/24
lxc.network.name = eth1
lxc.network.flags = up

Уменьшение размера образов qemu

Linux в связке с qemu является отличной платформой для виртуализации, в том числе Windows-систем. Ключевой особенностью виртуализации в случае «инородных» систем является использование под эту систему файла-образа или отдельного блочного устройства. И то и другое обычно занимает достаточно много места. Попробуем минимизировать используемое место в этом случае.

Среди поддерживаемых qemu форматов образов есть как обычные бинарные образы (img), так и сжатые и несжатые qcow/qcow2. Последний формат в сжатом варианте показывает очень хорошие результаты по использованию места: его образы занимают мало места и при этом размер «внутреннего» жесткого диска в виртуальной системе может быть очень большим. Использоавать этот формат можно изначально, при создании виртуальной машины с помощью qemu-img, или конвертацией образа другого формата в qcow2

qemu-img convert -O qcow2 -c win10.img win10.qcow2

Сжатие в формате qcow2 используется в режиме «только для чтения», т.е. если что-то из образа переписывается, то перезапись происходит уже в несжатом формате. Получается, что сжатой будет статичная часть системы и данных в образе, которые в зависимости от задачи вируталки могут занимать основную часть данных. Для таких машин это формат будет наиболее подходящим.
Руководство qemu-img также предупреждает, что при использовании сжатия несколько уменьшается производительность. Однако, субъективно разницы между несжатым и сжатым форматом мне заметить не удалось.

При конвертации в qcow2 и сжатии следует заранее позаботиться о «чистоте» образа: занулить всё неиспользуемое на нём пространство. В Руководстве Proxmox подробно описываются способы, которыми это можно сделать. Наиболее короткий — очистить диск с помощью майкросовтовской утилиты sdelete, например:

sdelete -z C:

В последней версии утилиты 2.0 есть баг — она зависает на 100%. В этом случае имеет смысл использовать более старую версию 1.6. Есть также вариант использовать графическую Eraser, но это обычно более длинный путь.

В ходе использования образ понемного увеличивается в размере. После длительного использования образ можно сократить в размере тем же самым способом: занулив свободное места и переконвертацией его в qemu-img:

qemu-img convert -O qcow2 -c win9.qcow2_backup win9.qcow2

Естественно, как и перед очисткой образа, так и его пережатием крайне желательно сделать бэкап.

В результате перечисленных выше нехитрых операций можно уменьшить размер образов на порядок, т.е. в 10 раз и более.

Удаляем в postfix лишние заголовки

Postfix — отличное типовое решение для корпоративного почтового сервера. Относительно легко ставится, хорошо интегрируется, доступно много всяких фич … что ещё нужно?

Безопасность. Если не конфигурировать явно, то postfix пропускает все заголовки, добавленные до него. Это могут быть:

  1. IP-адрес пользователя во внутренней сети, если он посылает сообщение с помощью клиента. Тогда в полученном письме увидим что-то вроде:

    Received: from [192.168.0.116] (broadband-xxx-xxx-xxx-xx.example.com [xxx.xxx.xxx.xxx])
    by srv.example.com (Postfix) with ESMTPSA id 7AE4D122165
    for ; Sun, 8 Jan 2017 19:13:57 +0000 (UTC)

    Таким образом раскрывается внутренняя структура сети, а если есть ещё и внутренний релей и он добавляет свои заголовки — то и он тоже попадает в заголовки. Ясно дело, что это нежелательно.
    Хорошее решение: срезать все заголовки Received из письма на конечном отправляющем сервере.
    Отдельно замечу, что некоторые из публичных почтовых сервисов тоже светят адрес отославшего письмо, что не всегда хорошо для его безопасности. Вообще, это отдельная тема, что лучше выбрать.

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

    Received: from localhost (localhost [127.0.0.1])
    by srv.example.com (Postfix) with ESMTP id 6466E12216D
    for ; Sun, 8 Jan 2017 19:47:46 +0000 (UTC)

  3. Полезной информации это не несёт, а значит лучше это и не дописывать в каждое сообщение.

  4. User-Agent (почтовый клиент) пользователя:

    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101
    Thunderbird/45.6.0

    Эта информация вполне безопасна, однако и её лишний раз лучше не светить — по письмам от разных пользователей одного домена можно узнать, например, какие операционные системы предпочитают в компании. Или здесь может светиться веб-почта

    User-Agent: Roundcube Webmail/1.2.3

    и сразу открывается ещё одна потенциально опасная точка входа.

  5. Разного рода автоматически добавляемые скриптами заголовки, например

    Received: by srv.example.com (Postfix, from userid 33)
    id 51C4212216F; Sun, 8 Jan 2017 20:07:31 +0000 (UTC)
    X-PHP-Originating-Script: 0:rcube.php

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

  6. Другие заголовки, которые я здесь не перечислил, но такие точно есть.

Далее →

SSH без дисконнекта

При длительной работе в терминале по SSH, особенно с длительными перерывами, очень удобно, чтобы подключение сохранялось всё это время. Если не отсылать и получать данные в открытом подключении, то через некоторое время сессия разрывается по таймауту, что обычно неудобно. Исправим это.

В протоколе реализована возможность сохранения сессии, TCPKeepAlive, клиент или сервер посылает «пустой» пакет, тем самым оживляет сессию и держит её открытой. Фича реализована как на клиенте, так и на сервере. На клиенте она включается в пользовательском конфиге ssh ~/.ssh/config

ServerAliveInterval 90

Клиент будет каждые 90 секунд отправлять пакет серверу и тем самым поддерживать соединение.

Можно настроить периодическое оживление и в общем случае, для всех клиентов сервера. В конфигурации демона /etc/ssh/sshd_config добавляем или правим

ClientAliveInterval 90
ClientAliveCountMax 960

В этих настройках сервер будет отправлять пакет каждые 90 секунд и максимум делать это 960 раз. В результате соединение продержится до суток (90*960 = 86400 = 24h).

Samba >=4.2 и Windows XP

С выходом самбы 4.2 поменялись настройки безопасности, которые идут по умолчанию из коробки. В частности, в дефолтной версии отключена поддержка старого протокола аутентификации для защиты от MTM-атак (человек по середине).
В результате этого старые ОС (Windows XP и похожего срока древности) не могут аутентифицироваться и работать с windows-шарами под samba. Тем не менее, кое-где ещё используются такие динозавры, поэтому нужно включить в самбе старый протокол и не бояться MTM.

Вот что пишут в apt-listchanges к пакету:

Suggested further improvements after patching:

It is recommended that administrators set these additional options,
if compatible with their network environment:

server signing = mandatory
ntlm auth = no

Without «server signing = mandatory», Man in the Middle attacks
are still possible against our file server and
classic/NT4-like/Samba3 Domain controller. (It is now enforced on
Samba’s AD DC.) Note that this has heavy impact on the file server
performance, so you need to decide between performance and
security. These Man in the Middle attacks for smb file servers are
well known for decades.

Without «ntlm auth = no», there may still be clients not using
NTLMv2, and these observed passwords may be brute-forced easily using
cloud-computing resources or rainbow tables.

Из некоторого анализа новых фич и изменённых настроек становится ясно, что включается поддержка нужного протокола одной строчкой global-секции:

        ntlm auth = no

которая позволяет аутентифицироваться и работать старым ОС, при этом понижая общую безопасность.

Странности nginx ssl

Параметры SSL в конфигурации nginx могут быть описаны как в глобальной http-секции, так и в настройках локальных серверов server. В контексте server по логике настройки должны применяться только на этот сервер.

Интересно, что поддержка TLS v1.2 включается только если её включить в server-секциях всех виртуальных хостов, а не только нужного.

Подключение bind9 к мониторингу munin

Для отладки да и просто для статистики полезно мониторить какие запросы идут к named-серверу, какие из них выполняются корректно и какие некорректные. Демон bind позволяет настроить гибкое логирование запросов, затем эти логи можно анализировать сторонней утилитой. В качестве такой утилиты удобно использовать систему мониторинга munin с bind-плагинами «из коробки».

Первое, что сделаем — включим нужные плагины. Их в директории с готовыми плагинами два, оба и подключим

cd /etc/munin/plugins
ln -s /usr/share/munin/plugins/bind9
ln -s /usr/share/munin/plugins/bind9_rndc

Эти плагины — перловские скрипты. Из их исходного текста можно увидеть, что первый из них (bind9) парсит стандартный лог неймсервера /var/log/bind9/query.log, второй (bind9_rndc) — парсит данные rndc из /var/run/named.stats и выдаёт расширенную статистику по запросам к DNS-серверу. В большинстве задач достаточно будет и общего плагина, однако, сконфигурируем их оба.

bind9

Конфигурируем параметры запуска скрипта мониторинга. Соответственно, общего плагина в файле /etc/plugin-conf.d/bind9:

[bind9]
user bind
env.logfile /var/log/named/query.log

Стандартная директория для логов bind /var/log/named, а мунин их читает из /var/log/bind9. Поэтому, явно указываем в конфиге путь к логу (как сделано выше). Как альтернатива, чтобы вносить меньше правок в конфигурацию, можно сделать симлинк:

cd /var/log
ln -s named bind9

Далее, настроим сбор логов от неймсервера в лог. Для этого создаем файл (например /etc/bind/logging.conf) с указанием данных, которые нужно писать в лог. Не будем мелочиться и настроим наиболее полное логирование, причем разных каналов в разные файлы.

logging {
	channel default_file {
		file "/var/log/named/default.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel general_file {
		file "/var/log/named/general.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel database_file {
		file "/var/log/named/database.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel security_file {
		file "/var/log/named/security.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel config_file {
		file "/var/log/named/config.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel resolver_file {
		file "/var/log/named/resolver.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel xfer-in_file {
		file "/var/log/named/xfer-in.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel xfer-out_file {
		file "/var/log/named/xfer-out.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel notify_file {
		file "/var/log/named/notify.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel client_file {
		file "/var/log/named/client.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel unmatched_file {
		file "/var/log/named/unmatched.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel queries_file {
		file "/var/log/named/query.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel network_file {
		file "/var/log/named/network.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel update_file {
		file "/var/log/named/update.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel dispatch_file {
		file "/var/log/named/dispatch.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel dnssec_file {
		file "/var/log/named/dnssec.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};
	channel lame-servers_file {
		file "/var/log/named/lame-servers.log" versions 5 size 10m;
		severity dynamic;
		print-category yes;
		print-severity yes;
		print-time yes;
	};

	category default { default_file; };
	category general { general_file; };
	category database { database_file; };
	category security { security_file; };
	category config { config_file; };
	category resolver { resolver_file; };
	category xfer-in { xfer-in_file; };
	category xfer-out { xfer-out_file; };
	category notify { notify_file; };
	category client { client_file; };
	category unmatched { unmatched_file; };
	category queries { queries_file; };
	category network { network_file; };
	category update { update_file; };
	category dispatch { dispatch_file; };
	category dnssec { dnssec_file; };
	category lame-servers { lame-servers_file; };
};

Для munin потребуется только лог запросов query.log, остальное — в отладочных целях и смело можно вычеркнуть.
Включаем этот файл в конфиге бинда

include "/etc/bind/logging.conf";

рестартуем демон и проверяем, что файлы лога создались и пишутся корректно. Заранее создаем директорию под логи и выставляем на неё нужные права.

Далее, нужно убедиться, что лог читаем для парсера плагина, что делается штатной муниновской командой

munin-run bind9

В случае корректной настройки тест вернёт количество запросов разного типа. Или не вернёт — тогда нужно искать ошибку в конфигурации. Типичная ошибка происходит с правами доступа, поэтому или выставляем доступные права для /var/named/* или настраиваем запуск плагина от рута.

bind9_rndc

Включаем сам плагин для сбора статистики rndc в /etc/plugin-conf.d/bind9_rndc и настраиваем его параметры:

[bind9_rndc]
user bind
env.querystats /var/cache/bind/named.stats

Путь для файла статистики намеренно указан нестандартный, поскольку apparmor в дистрибутивах Debian/Ubuntu ограничивает запись в стандартную директорию /var/run/named. Поэтому указываем файл в директории для кеша, где нет явных ограничений на запись.

Теперь включим расширенную статистику rndc со стороны bind. Включаем в конфигурации (обычно /etc/bind9/named.conf.options) строчку:

statistics-file "/var/cache/bind/named.stats";

и рестартуем bind9.

Аналогично вышеуказанному плагину проверяем, что плагин работает корректно

munin-run bind9_rndc

По окончании, чтобы мунин подхватил новые настройки, его демон нужно перезапустить. Перезапускаем мунин

/etc/init.d/munin-node restart

и проверяем, что логи корректно парсятся и в отчётах мунина присутствует статистика по DNS-запросам. В хорошем случае получаем картинки такого вида