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

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

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

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

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

Минимум настроек apache2 и nginx веб-серверов для повышения безопасности

Рассмотрим типовую схему веб-сервера: nginx используется в качестве фронтэнда (front-end), apache2 — бэкэнда (back-end). Установка пакетов из репозитариев дает стандартную конфигурацию, которая применима для сервера разработки и тестирования. Однако, стоковая конфигурация изначально подстроена под большой класс задач и требует доработки. В частности, в плане безопасности. Опишем минимум настроек, которые необходимо сделать, если настраиваемый сервер выходит в интернет для широкого использования.

apache2

Отключаем потенциально опасные и просто неиспользуемые модули, а также неиспользуемые подконфиги. Почти всегда можно (и нужно) отключить:

  • модуль autoindex, позволяющий просматривать содержимое директории при отсутствии индексного файла,
  • обработку cgi-bin, которая в deb-based системах лежит в конфиге serve-cgi-bin.conf и используется очень редко,
  • Другие неиспользуемые модули (список которых зависит от выкладываемого кода). Т.е. необходимо перешерстить директории mods-enabled, conf-enabled

На продакшн-сервере пользователю не нужно знать точную версию апача. Вообще, подобной информации должно отдаваться как можно меньше. Формат этой информации регулируется параметром ServerTokens, который по умолчанию обычно выставлен в OS и в результате веб-сервер выдает что-то вроде

Apache/2.4.10 (Debian) Server at ...

Этот параметр, как и некоторые другие настройки безопасности в debian-системах обычно находится в конфиге /etc/apache2/conf-available/security.conf. Переключаем в менее информативную и, соответственно, более безопасную версию:

#ServerTokens Minimal
#ServerTokens OS
#ServerTokens Full
ServerTokens Prod

Аналогичным образом стоит поступить с подписью сервера на "ошибочных" страницах

ServerSignature Off
#ServerSignature On

и трассировкой

TraceEnable Off

Последняя обычно уже отключена, но в этом необходимо убедиться.

Код некоторых выкладываемых сайтов может содержать скрытые файлы, вроде .gitignore, .svn и других, раскрывающих стуктуру кода. Их желательно отключить здесь же, в апаче, или в фронт-энде. Можно отключить сразу все скрытые файлы:

<DirectoryMatch "/\.">
	Require all denied
</DirectoryMatch>

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

Некоторые из правил для бэкэнда закрывают те же места, что и в фронтэнде. Даже если каким-то образом будет поломана конфигурация nginx, уязвимость все равно не реализуется.

Отключим доступ к скрытым файлам — в секции виртуалхоста добавляем:

RedirectMatch 404 /\..*$

Такие файлы нередко встречаются в проектах. В частности, системы контроля версий вроде git, svn. Эта метаинформация не должна попадать в прошакш, тем не менее следует превентивно защититься от такой возможности.

Если VirtualHost на сервере не один, то также не плохо будет раскомментировать секцию


   AllowOverride None
   Order Deny,Allow
   Deny from all

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


	AllowOverride All
	Order Deny,Allow
	Allow from all

Я не описываю настройку прав доступа (пользователь:группа) на уровне ОС и файловой системы и использование модуля mpm_itk с директивой AssignUserID. Это тоже должно быть сделано для серверов, на которых такое разделение требуется.

nginx

Отключим отображение скрытых файлов:

location ~ /\. {
	deny all;
}

nginx используется как frontend, и если на нем отдается контент по https, то повышаем его безопасность. Для начала отключаем SSLv3 и другие небезопасные протоколы. Если nginx свежий, то в нем это уже и так отключено, поэтому лучше просто обновиться.
Генерируем параметры обмена ключами Diffie-Hellman (которые могут генерироваться достаточно долго, запаситесь временем)

openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096

и затем подключаем их в энжинксе

ssl_dhparam /etc/nginx/ssl/dhparam.pem;

Можно оставить только сильные шифры, но при этом старые браузеры могут не поддерживаться. Если на совместимость с ними не ориентироваться, то можно смело включать

ssl_ciphers 'AES256+EECDH:AES256+EDH:!aNULL';

Также имеет смысл включить кеширование

ssl_session_cache shared:SSL:10m;

После всех настроек желательно проверить их корректность в каком-нибудь сервисе, например, этом.

PHP

Как и в случае апача скрываем версию

expose_php = Off

В зависимости от задач можно отключить вывод ошибок:

error_reporting = 0
display_errors = Off

 

Проверки

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

HEAD example.com

Также побродим по сайту, в том числе по страницам, которые генерируют 4xx и 5xx ошибки, проверим, что версии софта не раскрываются и тем более не раскрывается внутренняя структура сервиса.

Завершающие замечания

Если на сервере хостится несколько сайтов, то многие из приведённых выше настроек повышают безопасность лишь частично. Сделать хорошую защиту между виртуалхостами на одной машине принципиально будет достаточно сложно. Если такое требуется — лучше смотреть в сторону виртуализации уровня операционной системы, по образцу OpenVZ или lxc.

Со временем пост будет пополняться.

Формирование DKIM ключей

При администрировании почтовых (да и не только почтовых, но и любых отсылающих почту) серверов время от времени требуется создавать DKIM ключи для почты. Эта, в целом несложная, процедура состоит из генерирования ключей, добавления открытого в DNS TXT-запись домена, а закрытого — в конфиг почтового сервера. Ниже по порядку.

Генерировать ключи удобно с помощью широкоиспользуемой утилиты openssl. Чтобы не делать это каждый раз руками, полезно написать скриптик

#!/bin/sh

if [ "$1" != "" ]; then
	openssl genrsa -out "$1.key" 2048
	openssl rsa -in "$1.key" -out "$1.pub" -pubout -outform PEM
	chmod o= "$1.key"
else
	echo "Usage: $0 domainname"
fi

вызывая который с параметром — доменным именем, для которого формируем ключи, получаем пару ключей: публичный (открытый) и приватный (закрытый). В приведенной конфигурации используется 2048-битный ключ, что несколько безопаснее и потому предпочтительнее.

Публичный ключ доступен всем через DNS. Для этого помещаем его в виде TXT-записи для настраиваемого домена с именем x._domainkey, где x — это селектор. Селектор выбирается произвольно и обычно он "привязан" к серверу. Содержимое записи — это сам публичный ключ и некоторые параметры перед ним. Общий формат записи в DNS:

x._domainkey.mydomain.com.   TXT "v=DKIM1; g=*; k=rsa; p=[PUBLIC_KEY]"

Если текст записи достаточно длинный (а 2048-битный таким является), то его лучше разбить на несколько строк. Например, в DNS-сервере bind9, запись будет выглядеть так:

x._domainkey.mydomain.com.	TXT ( "v=DKIM1; g=*; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqUkhw8hQQ4NfKAPkvrQqd04Ta2fyVTfrVdbud62HeSISpDzf1GWPGrN0ikqcTT+6DW5fRwOnGO0VkennMs7+d+WkpTJ6Y63ydrFaK/sa8HoESPBJHqrNfViQzlCt5ZCc4UZN1sLLoO3HukwvLRtM"
				"wKMzxIkavknl86PLcTebS3+ac7lWdPoqJbooHQglizs0YazLqQTLn/L6mqv1OgPCMU44seEp/CZilUchbLvHAsrfK8+AADGm+/U/5qt6/SC31ZN/BmAqtMMKvT8rMFw2qj43DPWSkn9ln5EsyUJhQiORNX3v+9rndZNuw2I90xXbuIflc30gLStXG1Jqg4IAXQIDAQAB" )

Первыми в записи идут опции: версия DKIM, гранулярность, тип ключа. Подробности опций лучше прочитать в соответствующем RTFM для стандарта DKIM.

После добавления открытого ключа в DNS лучше проверить его рабочесть. Из шелла это делается так

$ host -t txt x._domainkey.mydomain.com

В случае успеха команда вернет только что добавленный ключ.

Приватный ключ добавляется в настройки почтового сервера, которые несколько различаются. Обычно приватные ключи хранятся в /etc/mail, причем для безопасности их права лучше сразу поменять на 0640 и изменить владельца так, чтобы ключ смог читать почтовый демон, но не пользователи системы.

Удаленный шелл с использованием nc

Фундаментальным и очень полезным качеством всех линуксов-юниксов является их модульность и возможность сборки из "кирпичиков" нужных инструментов. Это прекрасно иллюстрируется следующим примером из мануала утилиты netcat. Эта утилита передачи и приема произвольных данных по протоколам tcp/udp позволяет сделать элементарный и полностью рабочий шелл для работы с удалённым хостом. И это только используя эту утилиту и стандартные средства Linux вроде pipe и перенаправления данных.

На удалённой машине создаем трубу

$ rm -f /tmp/f; mkfifo /tmp/f

берем из неё данные, исполняем и результат толкаем в netcat, а его ответ обратно в трубу:

$ cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f

В результате получаем упрощённую нешифрованную версию ssh.

ssh/scp под Windows

Несколько решена проблема с доступом ssh/scp из под Windows. Cофтина позволяет монтировать ssh как логический том (sshfs). С её помощью может получиться неплохое решение для удалённых бэкапов из Windows под любые никсы. Жаль, что пока нет решения под Windows 8 и серверную 2012.

Запуск крона чаще раза в минуту

Наибольшая частота, с которой cron запускается — 1 минута. При администровании серверов бывают ситуации, когда требуется более частый вызов, но конфиг crontab не позволяет этого сделать. Задача решается окольным способом, с помощью вызова нескольких инструкций и использования sleep. Например:

* * * * * /usr/bin/python /usr/local/bin/doit.py
* * * * * sleep 30; /usr/bin/python /usr/local/bin/doit.py

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

* * * * * /usr/bin/python /usr/local/bin/doit.py; sleep 30; /usr/bin/python /usr/local/bin/doit.py

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

Ограниченный SFTP-доступ на сервер

Часто возникает задача дать доступ на сервер в определенную директорию по протоколу sftp, чтобы при этом не было доступа в весь корень файловой системы. Т.е. используя технику chroot. Делается это следующим образом.

Сначала создается пользователь в системе. Обычно по sftp заходят для правки контента и группа пользователя назначается общей для этого рода пользователей (например, www-data). Всё это делается опциями к adduser или useradd.

Далее в /etc/ssh/sshd_config созданному пользователю newuser добавляется кусок конфига, который определяет параметры входа и chroot:

Match User newuser
	ChrootDirectory %h
	ForceCommand internal-sftp
	AllowTcpForwarding no
	X11Forwarding no

После изменения конфига, естественно, нужно рестартануть sshd.

На этом все не заканчивается. Вот что гласит мануал sshd_config:

ChrootDirectory
Specifies the pathname of a directory to chroot(2) to after authentication. All components of the pathname must be root-owned directories that are not writable by any other user or group. After the chroot, sshd(8) changes the working directory to the user’s home directory.

Из соображений безопасности, чтобы работал chroot, директория пользователя должна быть от пользователя root (при этом группа может быть не рутовой). Выполняем это требование, voila и всё работает.

Также для безопасности обычно имеет смысл отключить шелл пользователя

	usermod newuser -s /bin/false

PS: Это все применимо именно для sftp. К сожалению, для scp это не работает и при попытке подключиться по ssh/scp произойдет ошибка.

Полезные твики bash

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

Алиасы

Листинги

alias ll='ls -l'
alias l='ls -lA'

Часто эти опции уже включены в .bashrc, но закомментированы. В Debian, например, в конфиге это реализовано так

# export LS_OPTIONS='--color=auto'
# eval "`dircolors`"
# alias ls='ls $LS_OPTIONS'
# alias ll='ls $LS_OPTIONS -l'
# alias l='ls $LS_OPTIONS -lA'

Остается раскомментировать нужные строчки. При этом в нагрузку получаем цветовую подсветку директорий.

Часто используемая команда по поиску процесса с определенным именем:

alias pg='ps aux | grep '

Просмотр наибольших файлов/директорий в гигабайтах и мегабайтах соответственно:

alias dug='du -h | grep ^[0-9.]*G | sort -rn'
alias dum='du -h | grep ^[0-9.]*M | sort -rn'

Полезности для скриптов

Иногда бывает полезным поймать сигналы, посылаемые ОС скрипту. Например, при нажатии Ctrl-C.

trap control_c SIGINT
trap control_c SIGTERM

Часто используемые команды

Часто бывает необходимо найти файл, в котором имеется определённая подстрока. Сделать это можно с помощью стандарной утилиты grep командой:

grep -rl

Горячие клавиши bash

  • Ctrl+w — удаляет слово перед курсором в строке,
  • Ctrl+u — удаляет все символы до начала строки,
  • Ctrl+k — удаляет все символы до конца строки,
  • Ctrl+y — вставляет удалённые вышеуказанными сочетаниями символы,
  • Ctrl+r — производит обратный поиск по истории команд,
  • Ctrl+l — очищает экран,
  • Ctrl+z — останавливает исполнение текущей команды (продолжить можно с помощью bg или fg),
  • Ctrl+d — выход из сессии.

История команд bash

В администрировании Linux (да и UNIX тоже) очень удобно пользоваться командами из истории. Содержание истории просматривается с помощью вызова history. Команду из истории можно выполнить по её номеру в списке например так:

history | less
!45

При этом последняя команда вызывается более просто, без указания номера выполнением !!.

Обратный поиск по истории команд вызывается с помощью Ctrl+R, например:

(reverse-i-search)`smart': smartctl -x /dev/sdd | less

Взять аргументы из одной из предыдущих команд можно сочетанием Alt+..

По умолчанию длина истории небольшая и если нет повышенных требований к безопасности её можно несколько увеличить добавлением в .bashrc строки:

export HISTFILESIZE=5000

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

history -c

Избежать добавления команды в историю можно начав её с пробела. Тогда исполненная инструкция в историю не попадает.

Уязвимость SSLv3 POODLE

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

nginx

В энжинксе явно разрешаем только хорошие протоколы

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

apache2

В апаче и ниже — запрещаем.

SSLProtocol all -SSLv3 -SSLv2

dovecot

ssl_protocols = !SSLv3 !SSLv2

postfix

smtpd_tls_mandatory_protocols=!SSLv2, !SSLv3

Тесты SSL уязвимости

Здесь можно проверить на уязвимость свои серверы, а здесь — браузер.
Другие параметры SSL можно проверить с помощью этого теста, кроме этого легко гуглится ещё много подобных сервисов.

Дополнительно

Естественно, все демоны после изменения конфигов релодятся или рестартятся.

PS: Разработчики nginx отключили SSLv3 в конфигах. На новых версиях этого делать уже не требуется.

Экономные дифференциальные бэкапы с hard links

Часто возникает задача делать backup больших объемов редко изменяющихся файлов. Обычно для этого используют замечательную утилиту rsync, которая синхронизирует обновленные файлы, чем существенно экономит время бэкапа и сетевой траффик. Количество копий бэкапа, на которые происходит синхронизация, произвольно и чем оно больше — тем лучше. Естественное ограничение — свободное местом носителя. И здесь на помощь приходят две полезные опции рсинка:

--compare-dest=DIR also compare received files relative to DIR
--link-dest=DIR hardlink to files in DIR when unchanged

Первая из них создает чисто дифференциальный бэкап сравнением с существующим, то есть помещает в новый бэкап только изменившиеся файлы. Вторая — создает полный бэкап, при этом не изменившиеся файлы с помощью hardlinks ссылаются на забэкапленные. В плане использования свободного места бэкапного сервера различия несущественны, но чисто дифференциальный для использования требует наличия директории сравнения. Основанный на hardlinks — можно сразу использовать "как есть", что намного удобнее. Поэтому, будем рассматривать вариант именно с жесткими ссылками.

Логика бэкапов сделаем наподобие logrotate. Новая директория добавляется с номером 0, при этом существующие сдвигаются на +1 вперед. Количество бэкапов ограничим: последняя директория будет удаляться. Получаем следующий скрипт:

#!/bin/sh

servname="mailserv"

backup_dir=/backup/$servname/vmail
len=60

tmpname=--temp--

curr_dir="$backup_dir/$tmpname"
zero_dir="$backup_dir/`printf "%02d" 0`"
prev_dir="$backup_dir/`printf "%02d" 1`"

last_dir="$backup_dir/`printf "%02d" $len`"

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

if [ -d "$zero_dir" ]; then
	echo "Rotating directories up to $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/`printf "%02d" $i`"
		if [ -d "$src" ]; then
			mv "$src" "$backup_dir/`printf "%02d" $(($i+1))`"
		fi
	done
fi

if [ -d "$curr_dir" ] ; then
	echo "Warning: \"$curr_dir\" already exists. Probably it was not renamed during last run. Resuming it."
fi


if [ -d "$prev_dir" ] ; then
	echo "Making hard-linked incremental backup."
	rsync -rtzKL --delete --stats --link-dest="$prev_dir" -v $servname:/var/vmail "$curr_dir"
#	rsync -rtzKL --delete --stats --compare-dest="$prev_dir" $servname:/var/vmail "$curr_dir"
else
	echo "Making init backup."
	#rsync -rtzKL -e "ssh -i /home/bu/.ssh/id_dsa" --delete --stats $servname:/var/vmail "$curr_dir"
	rsync -rtzKL --delete --stats $servname:/var/vmail "$curr_dir"
fi

# moving temporary to latest
mv "$curr_dir" "$zero_dir"

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

В результате на файловой системе с поддержкой жестких ссылок можно разместить бэкапов размером на порядки больше, чем размер самого носителя. Например бэкап почтового сервера размером ~870 Gb состоящий из 30 копий занимает всего 970 Gb.

PS: Для систем с разделением прав доступа и разными пользователями такой бэкап желательно делать с опцией --numeric-ids, которая сохранит (цифровые) owner:group.

Основы безопасности сайта

В последнее время распространена проблема автоматизированного взлома сайтов на известных CMS. Целью взлома обычно является: рассылка с сайта спама и размещение ссылок на внешние ресурсы. Несмотря на то, что проблема обычна вызвана уязвимостями в самой CMS, она может быть решена в общем виде.

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

  1. Исполняемый код должен быть защищен от записи от пользователя веб-сервера. Наиболее очевидный вариант как это сделать: назначить ему owner:group другого пользователя, от которого и работать с кодом на запись, а веб-серверу оставить права только на чтение.
  2. Запретить исполнение кода во всех директориях разрешенных на запись от веб-сервера (контент, кеш и подобные).
  3. Забыть про ftp и не сохранять пароли в файловых менеджерах. Взамен пользоваться scp/sftp, желательно с авторизацей по ключу.
  4. Для разных virtual hosts использовать разных системных пользователей, которые не читают данные друг друга.

Первый пункт из вышеприведенных гарантирует сохранность кода, даже в движке с дырами или бэкдором. Второй затрудняет исполнение залитых файлов: их можно исполнить с помощью include(), но для этого в read-only коде должна быть такая возможность (которая по сути является бэкдором).

Кроме этого, желательно:

  • Ограничить доступ в административные части сайта по IP-адресам на уровне веб-сервера только с рабочих компьютеров. Способ помогает даже при компрометации паролей, а также при человеческих конфликтах.
  • Использовать SSL-соединения (с префиксом https://) для входа в административные части и везде, где используется пароль для входа. Это защитит от компрометации паролей и данных по каналам связи, например при работе по незащищённому вайфаю.
  • Если точек входа на сайт небольшое количество, разрешить использовать только их, остальное запретить.