Улучшаем и дополняем советы блогеров
Сейчас появилось множество блогеров, которые дают советы по *nix-системам. Большинство из этих советов, как правило, можно улучшить или дополнить, а некоторые вообще по тем или иным причинам некорректны. Наш журнал не мог пройти мимо такого вопиющего недоразумения и в моем лице решил прошерстить блоги, выискивая данные советы и исправляя их.
Сохраняем базы данных в MySQL
Иногда бывает необходимо автоматизировать создание бэкапа БД в MySQL. И ладно бы только одной базы — с этим все просто, использовать команду вида:
$ mysqldump -u user -ppass dbname | gzip -9 > dbname-`date +%Y%m%d%H%M`.sql.gz
но ведь иногда надо все базы сохранить! Что нам предлагается сделать в блоге для этой цели? Предлагается использовать опцию –all-databases, то есть команда будет выглядеть так:
$ mysqldump -u user -ppass --all-databases | gzip -9 > all-db.sql.gz
Однако эта команда не совсем удобна — хотя бы тем, что все базы сохраняет в один файл, а это слегка затрудняет ее восстановление. Что предлагаю сделать я? На выбор два варианта: один — коротенький мини-скрипт, практически двустрочник, который можно легко зазубрить и выполнять в командной строке, и второй — уже полноценный скрипт, куда можно, например, прописывать базы, которые сохранять не требуется. Мини-скрипт выглядит так:
$ for i in `mysql -u root -ppass -e'SHOW DATABASES;' | egrep -v 'information_schema|performance_schema|Database`; do mysqldump -u root -ppass $i > `date +%Y%m%d%H%M`-$i; gzip -9 `date +%Y%m%d%H%M`-$i;done
Разберемся, что делает этот мини-скрипт. Он в цикле просматривает все базы данных, удаляет egrep’ом строчки ненужных баз, таких как information_schema, и создает для каждой базы бэкап.
Хакер #173. Уязвимости Ruby on Rails
Теперь рассмотрим кусочек полноценного скрипта (целиком его ты можешь найти на диске):
mysqldump_all.sh for db in $DBS do ... if [ "$skipdb" == "-1" ] ; then MYSQLBACKDIR="$DEST/$db" # Создаем папку для бэкапа с названием базы [ ! -d $MYSQLBACKDIR ] && mkdir -p $MYSQLBACKDIR || : FILE="$MYSQLBACKDIR/$NOW.sql.gz" # Собственно бэкап $MYSQLDUMP --opt -u $MyUSER -h $MyHOST -p$MyPASS $db | $GZIP -9 > $FILE FILECOUNT="$(find $MYSQLBACKDIR/* | wc -l)" if [ $FILECOUMT -ge 0 ] ; then # Удаляем файлы бэкапов, созданные более 20 дней назад find $MYSQLBACKDIR/* -type f -mtime 20 -exec rm -rf {} ; fi fi done
Какой из этих путей использовать — зависит исключительно от цели бэкапа. На мой взгляд, чем проще, тем лучше — но и слишком простые решения зачастую бывают неправильными.
Если у тебя уже есть полный бэкап всех баз данных и ты хочешь восстановить конкретную БД, используй следующую команду:
$ gunzip -c fulldumpname.sql.gz | mysql --one-database -u root -ppass db_to_restore
Восстановление пароля root (RHEL)
Предположим, ты забыл пароль root или вообще его не знаешь. В блогах предлагается следующий путь:
- При загрузке нажать пробел, потом «E».
- Выделить строчку kernel и, снова нажав «E», добавить в конец строки слово single.
- Загрузиться и сменить пароль.
Однако… иногда и в режиме single требуется пароль. Есть другой путь восстановления (конечно, если и на Grub не стоит пароль).
- Предыдущие пункты 1 и 2 выполняем без изменений, но вместо single пишем init=/bin/bash, так указываем ядру, что вместо /sbin/init первой программой пользовательского режима будет оболочка.
- Перемонтируем корневую ФС в режим rw:
# /sbin/mount -o remount,rw /
- Задаем пароль командой /bin/passwd.
- Жмем следующие комбинации клавиш: <Alt + SysRq + s>, <Alt + SysRq + u>, <Alt + SysRq + b> — аварийная синхронизация буферов ФС, перемонтирование ФС в режим ro и перезагрузка. Пароль изменен.
Рулим контекстом SELinux
Известно, что SELinux довольно-таки навороченная и сложная вещь, и в случае непонятных неполадок на него надо бы грешить в первую очередь. Но речь не об этом, а о том, что иногда советуют «просто изменить контекст», используя следующую команду (аргументы далее — в качестве примера):
# chcon -R -t samba_share_t /home/samba
Но все это будет действовать лишь до следующего обновления политик. Чтобы понимать, почему так, давай рассмотрим эту часть SELinux чуть подробнее. Все политики SELinux хранятся в /etc/selinux/targeted/policy, но команды наподобие chcon оперируют не с ними. Они оперируют с расширенными атрибутами файлов. Однако политики иногда обновляются, и все контексты, которые хранятся в расширенных атрибутах, при этом сбрасываются до значений, прописанных в этих политиках. Таким образом, команда chcon устанавливает контекст лишь на время. Для того чтобы его еще и добавить к файлам политик, необходимо использовать команду semanage fcontext — для приведенного выше примера она будет выглядеть так:
# semanage fcontext -a -t samba_share_t /home/samba
Кстати, если говорить о Samba, то, чтобы разрешить лезть в домашние директории без смены контекста, необходимо установить переменную samba_enable_home_dirs, для чего используем следующую команду:
# setsebool -P samba_enable_home_dirs on
Бэкап /etc
Для быстрого бэкапа /etc, пишется в одном блоге, можно использовать команду zip, при необходимости используя ключ ‘-x’ для исключения лишних каталогов — например, так:
# zip -r /home/user/backupfile /etc -x /etc/dev* /etc/selinux* /etc/alternatives*
Данный совет достаточно корректен. Но мне бы хотелось к нему кое-что добавить: в качестве дополнения к резервному копированию (но ни в коем разе не в качестве замены!) можно использовать команды Git.
- Если Git еще не стоит в системе — установи его.
- Проинициализируй репозиторий Git в /etc и добавь в него все файлы.
# cd /etc # git init # git add .
- Закоммить текущую версию.
# git commit
- По необходимости создай бранч и переключись на него.
# git checkout -b add_user_joe
- Измени все, что хотел изменить, и добавь эти изменения в репозиторий Git с последующим коммитом.
# useradd joe # passwd joe # git add . -A # git commit
- В случае успеха проведи слияние бранчей, а в случае неудачи — переключись на прежний бранч.
# git chechkout master # git merge add_user_joe
Некоторые тонкости сборки ядра в Ubuntu
Во многих статьях, посвященных сборке ядра, используется следующий метод сборки и установки:
# make dep && make clean && make && make install && make modules install
Но этот способ достаточно неудобен — он не формирует пакеты, следовательно, новое ядро так просто не удалишь. Да и заголовочные файлы ядра остаются старыми. В системах на основе Debian есть метод получше:
$ sudo apt-get build-dep linux-image $ sudo apt-get install fakeroot kernel-package $ fakeroot make-kpkg --initrd --append-to-version=-custom kernel_image kernel_headers && sudo dpkg -i ../linux-image-version.deb ../linux-headers-version.deb
Разберем эту команду подробнее. Fakeroot используется для того, чтобы перенаправить некоторые изменения владельца файла в случае отсутствия прав root, — эту команду можно и не выполнять, так как современные версии make-kpkg сами вызывают ее, но лишней она не будет. Остальное более-менее понятно.
Кроме того, если ты планируешь часто менять что-то в ядре, то для ускорения пересборки можно использовать ccache, для чего в командной строке перед вызовом fakeroot необходимо поставить CC=ccache.
Просмотр пакетов по дате установки (Ubuntu)
Допустим, тебе понадобилось просмотреть, когда какие пакеты ты устанавливал. В блогах для этого советуют заглянуть в /var/log/apt/history.log. Конечно, это наиболее удобный путь, но, в зависимости от настроек logrotate, старые логи могут быть недоступны. В этом случае придется пошаманить — поскольку в базе данных dpkg по каким-то причинам нет даты установки того или иного пакета. Зато она есть в файловой системе! Итак:
# find /var/lib/dpkg/info -name "*.list" -exec stat -c $'%nt%y' {} ; | sed -e 's,/var/lib/dpkg/info/,,' -e 's,.listt,t,' | sort > ./dpkglist.dates # dpkg --get-selections | sed -ne '/tinstall$/{s/[[:space:]].*//;p}' | sort > ./dpkglist.selections # join -1 1 -2 1 -t $'t' ./dpkglist.selections ./dpkglist.dates > ./dpkglist.selectiondates # cat dpkglist.selectiondates | awk '{print $2" "$3" "$4" " $1}' | sort > ./dpkglist.selectiondates_bydate
Разберемся, что это за трехэтажные команды.
Первая (следом за sudo) команда ищет все файлы с расширением list в каталоге /var/lib/dpkg/info, в котором находится информация о пакетах, смотрит время их создания, соответствующее времени установки пакета, удаляет из получившихся строк ненужную информацию и, сортируя, направляет в файл dpkglist.dates.
Вторая команда просматривает установленные пакеты, удаляет ненужную информацию и, опять же сортируя, направляет в файл dpkglist.selections. Третья команда объединяет два файла в один. Для чего это надо, ведь в первом файле все есть? Для удаления из конечного результата некоторых лишних строк, чтобы результат соответствовал текущему положению дел в системе.
Четвертая команда необязательна, но желательна — она перетасовывает поля файла, делая первыми поля даты и времени, и сортирует по дате установки. К слову, в системах на основе RPM это будет выглядеть гораздо проще:
# rpm -qa --last | tac | less
В пояснении нуждается разве что команда tac — она инвертирует порядок строк, поскольку первая команда выводит пакеты в порядке, обратном порядку их установки.
Ограничение процессорного времени для процесса
В блогах для этого рекомендуют применять команду (re)nice. Например, следующая команда запустит поиск с минимальным приоритетом (напомню, что чем больше значение, тем приоритет ниже, отрицательные же значения может присваивать только процесс, запущенный с root-привилегиями, либо процесс с установленным capability CAP_SYS_NICE):
$ nice -n 19 find ./ -name *.odt -print
Но у этой команды есть один недостаток: она принимает на вход некие абстрактные «приоритеты». А это не всегда удобно, в процентах от процессорного времени в большинстве случаев гораздо удобнее. В современных дистрибутивах Linux имеется утилита cpulimit, которая позволяет ограничивать именно на основе процессорного времени. Установим ее:
$ sudo apt-get install cpulimit
Применение этой команды довольно просто — но для пользователей без прав root доступно только ограничение уже запущенного процесса. Например, для команды find это будет выглядеть примерно так:
$ cpulimit -p `pidof find` -l 30
Замечу, что эта команда действует для каждого процессора, то есть если у тебя четыре ядра, то максимальная величина будет не 100, а 400.
Метод работы программы довольно оригинален: она не использует всякие нововведения наподобие cgroups, вместо этого она мониторит загрузку процессора для конкретного процесса и время от времени посылает ему сигналы SIGSTOP и SIGCONT.
Если говорить об ограничениях пользователя, нельзя не упомянуть файл /etc/security/limits.conf, который позволяет много чего ограничивать. Для его использования необходимо включить модуль pam_limits.
Ограничивать можно, например:
- размер файла;
- размер дампа памяти процесса;
- количество одновременно открытых файлов;
- количество процессов.
Существует два вида ограничения — жесткое (hard) и мягкое (soft). Жесткое задается суперпользователем и не может быть снято обычным пользователем, мягкое же может задаваться обычным пользователем с помощью команды ulimit и не может превышать жесткое. Синтаксис файла limits.conf:
<домен> <тип> <ограничение> <величина>
где <домен> — субъект ограничения (может быть пользователем, группой, диапазоном UID или GID); <тип> — тип ограничения, мягкое или жесткое; <ограничение> — собственно ограничение и есть, например, для размера файла это будет fsize; <величина> — числовое значение данного ограничения — как правило, для объема данных оно указывается в килобайтах.
Удаление битых симлинков
Некоторые пользователи для удаления битых симлинков используют такой скрипт:
rm-dead-links #!/bin/bash if `file $1 | grep -q "broken symbolic link"`; then rm -i $1 fi
Применяя его к файлам с помощью команды find:
# find / -type l -exec ls -l --color {} ; -exec ./rm_dead_links {} ;
На самом деле можно сделать гораздо проще:
# find -L / -type l -not -path '/proc' -exec rm -i {} ;
Опция -L предписывает следовать симлинкам, а в сочетании с -type l ищет битые симлинки — они никуда не следуют, поэтому тип у них остается ссылкой. В каталоге /proc их искать смысла нет, так что этот путь исключен из поиска.
Проксирование трафика
Стоит только заглянуть в любой блог, и там обязательно найдется совет о том, какой же выбрать socks5-прокси. И количество решений про различные прокси можно сравнить с длиной рулона туалетной бумаги. Они не нужны! У нас есть инструмент универсальнее и безопаснее, который умеет создавать шифрованные туннели, — SSH.
Создать скрипт и запустить его на сервере проще простого:
#!/bin/bash IP=10.1.106.55 while true do ssh -CTNv -D $IP:3128 localhost sleep 1 done
На клиенте остается прописать socks5-прокси: <IP-сервера>:3128.
Готово, мы инкапсулируем socks5 в SSH-туннель! Осталось только разобраться, что означают отдельные ключи. Опция ‘-C’ разрешает компрессию передаваемых данных через gzip, ‘-T’ выключает привязку к TTY-псевдотерминалу, ключ ‘-N’ указывает на то, что нам надо просто передавать данные, а не выполнять команды, ‘-v’ добавляет деталей в stdout вывод от SSH, и, наконец, самая главная опция ‘-D’ организует программный проброс указанного порта в защищенный туннель.
Socks5 — это замечательно, но у меня есть рецепт и для прозрачного проксирования через удаленный хост, без установки на нем дополнительного ПО, — утилита sshuttle (читается как с-шатл). Преимущество подхода в том, что тебе не нужно заботиться о правилах на удаленном сервере, все настройки в файрвол и маршрутизацию удаленного сервера sshuttle внесет сама.
Получим свежую версию с GitHub:
$ git clone git://github.com/apenwarr/sshuttle
Запускаем, указывая в ключе ‘-r’ имяsshпользователя@адрес_сервера:
# ./sshuttle -r username@server 0.0.0.0/0 -vv
Теперь весь трафик нашей машины прозрачно проходит через указанный адрес сервера. По <Ctrl + C> sshuttle заботливо уберет произведенные удаленно изменения. Из дополнительных опций внимание заслуживает ключ ‘–dns’, позволяющий завернуть DNS-трафик в этот же канал.
VPN своими руками
Бывает необходимо быстро настроить VPN — в таких случаях советуют использовать OpenVPN. Но что, если устанавливать что-нибудь на сервер ты не хочешь или не можешь? В OpenSSH уже довольно давно есть поддержка туннелирования. Чтобы его включить, найди (или добавь сам на сервере) следующие строчки в файл конфигурации sshd:
/etc/ssh/sshd_config # Снижает безопасность, но в данном случае необходимо. В качестве альтернативы вместо yes можешь использовать without-password для key-only-аутентификации root, но далее для упрощения будем считать, что аутентификация проходит по паролю PermitRootLogin yes PermitTunnel yes
Теперь на стороне клиента запускаем следующую команду:
# ssh -w 0:0 root@servername
Ключ ‘-w 0:0’ создает устройства tun0 как на клиенте, так и на сервере. Теперь данные интерфейсы нужно настроить, для чего на клиенте набираем команду:
# ifconfig tun0 10.0.0.2 pointopoint 10.0.0.1
а на сервере, соответственно, наоборот:
# ifconfig tun0 10.0.0.1 pointopoint 10.0.0.2
После этого можно пропинговать сервер, дабы убедиться, что все это работает, и настроить маршрутизацию.
Если хочешь, чтобы адреса присваивались автоматически, — смотри в сторону vtun.
Автоматическое восстановление правил iptables после перезагрузки (Ubuntu)
Имеется множество советов, как это сделать. В основном все они заключаются в создании скриптов. Однако для большинства простых конфигураций гораздо проще поступить следующим образом. Во-первых, поставить пакет iptables-persistent:
$ sudo apt-get install iptables-persistent
Во-вторых, написать скрипт для удобства конфигурирования — со всеми переменными и прочими плюшками bash. Единственное условие — скрипт не должен содержать циклов и условных выражений; в iptables они не сохраняются. После отладки скрипта выполнить следующую команду:
$ sudo service iptables-persistent save
Однако этот метод не всегда подходит для сложных случаев, когда необходимы условные операторы. Я бы посоветовал использовать Shorewall (shorewall.net). Почему не использовать просто скрипт? Если проводить аналогию с языками программирования, то iptables — ассемблер, инструмент очень мощный, но для написания больших и сложных наборов правил малопригодный. Shorewall же — своего рода язык высокого уровня, заметно облегчающий написание сложных правил и объединяющий в себе брандмауэр, роутинг и контроль трафика, но при этом позволяющий в особо критичных случаях использовать «ассемблерные вставки» — прямые вызовы iptables.
Роман Ярыженко rommanio@yandex.ru