Ежедневно перед юниксовым администратором встает масса разносторонних задач. Накопивший за многие годы солидный опыт, он каждый раз вытаскивает из потертого багажа ответов готовые решения. Все работает, но можно ли осовременить традиционные рецепты, приправив их новыми подходами? Легко!
ЭКОНОМИКА ДЛЯ ПОРТОВ
Обычно, чтобы усложнить инвентаризацию портов автоматическими средствами с целью применения эксплойтов, меняют номер прослушиваемого порта той или иной службы на какой-то нестандартный. Такой подход будет вполне успешным решением в борьбе, скажем, против червей, но вскинет кверху лапки при первой же попытке полного целенаправленного сканирования злоумышленником. Нехорошо это. Так давай попытаемся запутать неприятеля!
Служба под названием sslh делает на первый взгляд нечто невероятное — позволяет использовать на одном порту несколько сервисов, таких как SSH и HTTPS. Внимательный читатель возмутится и заявит, что невозможно реализовать такую логику, так как при попытке забиндить уже занятый порт другой программой ядро вернет сообщение об ошибке, и он будет прав. Истина кроется в известной пословице: все гениальное просто. На самом деле прослушивает порт непосредственно демон sslh, а поступающие от клиентов запросы утилита парсит, анализируя первый пришедший пакет, понимая протокол и пересылая данные на реальный порт интерфейса loopback, где работает настоящая служба. Такая техника называется мультиплексированием портов. В итоге получается, что настоящие сервисы работают, но только внутри подсети 127.0.0.0/8, то есть остаются недоступными напрямую извне.
Возможно, звучит все это пугающе, но настраивается легко, с полоборота. Давай же сделаем прямо сейчас обслуживание на 443-м порту SSH и Apache с mod_ssl (поддержка HTTPS).
К счастью, sslh уже успел попасть в репозитории самых популярных дистрибутивов, поэтому установим его с помощью менеджера пакетов:
# apt-get install sslh
Sslh принесет с собой небольшой конфиг /etc/default/sslh, состоящий всего из трех строк:
# Изменяем RUN на yes, только так sslh запустится RUN=yes # Путь к бинарнику DAEMON=/usr/sbin/sslh # Параметры запуска DAEMON_OPTS="--user sslh --listen 0.0.0.0:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:4443"
На параметрах запуска остановимся подробнее. Пользователь, от которого выполняется sslh, находится в опции —user. Ключом —listen мы обозначаем прослушиваемый демоном IP и порт на всех доступных интерфейсах (0.0.0.0). Добавление —ssh приведет к указанию IP-адреса и порта службы OpenSSH. Обрати внимание, что значение IP равно 127.0.0.1, то есть все подключения будут только по интерфейсу loopback. Соответственно, опция —ssl укажет прослушиваемый IP- и SSL-порт, а соединения также останутся в кольцевом интерфейсе.
Чтобы придать смысл нашей реализации, оставив только прослушивание
службами 127.0.0.0/8 сети, перенастроим адрес у sshd в /etc/ssh/sshd_config:
... ListenAddress 127.0.0.1 ... Так же поступим и с конфигурацией Apache, изменив секцию <IfModule mod_ssl.c> файла /etc/apache2/ports.conf: ... <IfModule mod_ssl.c> Listen 127.0.0.1:4443 </IfModule> ... Время запустить sslh: # service sslh start
Осталось насладиться результатом, приконнектившись на 443-й порт одновременно SSH-клиентом и веб-браузером.
БОНДИНГ, ДЖЕЙМС БОНДИНГ
Все мы живем миром IT, а потому знаем, насколько важен бэкап. Но это слово может употребляться не только применительно к файлам. У многих из нас есть и резервные интернет-каналы, чтобы в случае чего не терять связь с цифровым кислородом. Страховка — это хорошо, но вот минус — во время падения одного из провайдеров нам приходится вставать с мягкого кресла и перекидывать на роутере пару патчкордов. Это неудобно, и потому люди находят решение в автоматическом переключении между каналами с помощью смены маршрутов. Для этого пишется скрипт, который пингует роутер основного провайдера и, в случае неудачи, меняет шлюз на резервного прова, тем самым перенаправляя трафик по новому пути. Простой пример такого скрипта, запускаемого из крона:
#!/bin/bash # Получаем текущий маршрут по умолчанию, то есть шлюз # провайдера CURRENT=$(ip r | grep default | cut -d " " -f3) ROUTER1=10.1.1.1 # Основной шлюз первого провайдера ROUTER2=10.2.1.1 # Шлюз второго провайдера # Пробуем пинговать роутер первого провайдера if ping -c 3 $ROUTER1 > /dev/null 2>&1; then # Сервер доступен # Проверяем текущий маршрут: является ли он основным? if [ $CURRENT != $ROUTER1 ]; then # Нет, не является, значит, меняем маршрут ip r a default via $ROUTER1 fi else # Сервер ROUTER1 оказался недоступным # Проверяем текущий маршрут: является ли он путем через # второго провайдера? if [ $CURRENT != ROUTER2 ]; then # Нет, не является, значит, меняем маршрут ip r a default via $ROUTER2 fi fi exit 0
Все это по большому счету костыль на колесиках, поэтому правильнее и даже проще настроить так называемый бондинг (bonding). Под этим словом скрывается техника агрегирования (объединения) двух сетевых интерфейсов в один с целью суммировать пропускную способность. А в качестве бонуса мы получаем важную особенность — при падении одного из объединенных каналов трафик начинает ходить через другой интерфейс, а для пользователя остается абсолютно незаметным, что один из каналов недоступен. Уже хочется? Тогда создавай скрипт:
#!/bin/bash # Загружаем модуль ядра и передаем параметры modprobe bonding mode=0 miimon=100 ifconfi g eth0 down # Интерфейс первого провайдера ifconfi g eth1 down # Интерфейс второго провайдера # Указываем MAC для интерфейса bond0 ifconfi g bond0 hw ether 01:02:03:04:05:06 # Поднимаем интерфейс bond0 с указанным IP-адресом ifconfi g bond0 192.168.0.1 up # Переводим оба интерфейса в slave-режим ifenslave bond0 eth0 ifenslave bond0 eth1
Стоит внимательнее взглянуть на передаваемые модулю ядра параметры. Параметр mode=0 означает, что передача данных будет происходить поочередно через оба канала, то есть циклически. Зачем тебе оставлять простаивающие ресурсы, когда можно их безжалостно заэксплуатировать? Вторая опция — miimon=100 означает, что проверять доступность каналов нужно каждые 100 миллисекунд.
После запуска скрипта появится новый интерфейс bond0, который поможет тебе спать спокойно, больше не заботясь о проблемах с интернетом.
АРХИВ-САМОБРАНКА
Весьма часто задачи администрирования сводятся к распространению каких-либо данных на N-е количество серверов. Для автоматизации этого процесса многие админы посоветуют разместить распространяемый файл на HTTP-сервере, а затем написать скрипт, который пробежит по списку серверов, запустив Wget и распаковав кусок данных. Проблема кроется в лени, не всегда хочется вспоминать или поднимать доступный для всех машин веб-сервер, а ведь надо еще зайти на него и разместить данные. Впрочем, другая часть админов посоветует воспользоваться SCP для заливки файла на каждый сервер, чтобы затем, залогинившись через SSH, распаковать содержимое. Этот подход уже немного проще, но все равно заставит зевать в процессе. Мы не будем играть в три хода
(логин, загрузка, исполнение), а поставим мат путем размещения необходимых данных прямиком в скрипте, который останется только запустить. Давай создадим, в лучших традициях Windows, самораспаковывающийся архив. Это проще, чем кажется на первый взгляд, ведь в своей основе это обычный sh-скрипт следующего содержания:
#!/bin/sh # Для tar.bz2-архива sed -e '1,/^DATA_SECTION$/d' $0 | base64 -d | tar -jx # Для tar.gz-архива # sed -e '1,/^DATA_SECTION$/d' $0 | base64 -d | tar -zx exit DATA_SECTION
Сохраним скрипт под именем archive.sh и допишем прямо в него данные архива, закодировав предварительно все в Base64:
$ cat archive.tar.bz2 | base64 >> archive.sh
Готово! Теперь если выполнить получившийся archive.sh, то в текущем каталоге появится уже распакованный tar.bz2-архив. Удобно. Работает трюк элементарно, «sed -e ‘1,/^DATA_SECTION$/d’ $0» отрезает в запущенном скрипте все строки, находящиеся от начала файла до DATA_SECTION, а оставшиеся закодированные в Base64 данные выводит в stdout, которые через пайп передаются на декодирование «base64 -d» и уже потом попадают на работу tar, который распаковывает первоначальный архив «tar -jx» в текущий каталог. Так что путем дописывания дополнительных команд перед DATA_SECTION ты можешь запросто расширить функционал архива-самобранки под собственные нужды.
ВСПОМНИТЬ ВСЕ
Современное общество заливают огромные потоки информации, а наш мозг не в состоянии поддерживать актуальность всех поступающих в память данных. Ежедневно мы посещаем десятки сайтов, сервисов, все мы имеем пароли от собственных серверов. Рано или поздно человек приходит к тому, что важную информацию нужно где-то хранить. И обращается к специальным программам для организации и хранения паролей. Среди них оказывается и моя любимица — KeePassX. Открытый продукт, доступный под все популярные платформы, шифрует данные AES-алгоритмом с 256-битным ключом.
Да, KeePassX можно использовать просто как хранилку паролей, в которой приходится каждый раз нажимать <Ctrl + B> и <Ctrl + C> для того, чтобы скопировать имя пользователя и пароль соответственно. Но если зайти на сайт, поставить курсор в поле ввода логина, перейти в окно KeePassX и на выбранной записи нажать <Ctrl + V>, то данные аккаунта на сайте заполнятся сами. Это есть обычный автоввод, и ты, наверное, об этом знаешь. Программа, бесспорно, удобна, а всего лишь легким движением пальцев ее можно улучшить до идеала юзабилити, просто применив дополнительные фичи продвинутого режима автоввода. Секрет кроется в хитрости заполнения поля «Комментарий» — оказывается, его можно использовать как способ пошагово задать алгоритм выполнения любых действий в соседнем окне.
Например, для того чтобы залогиниться на удаленный сервер по SSH, вставь в комментарий строку:
Auto-Type: ssh -l {Username} {URL}{ENTER}{DELAY 3000}{Password}{ENTER}
Запусти терминал и нажми <Ctrl + V>. KeePassX напечатает «ssh -l имя_пользователя адрес_сервера», нажмет <Enter>, подождет три секунды, введет пароль и завершит ввод клавишей
<Enter>. Фактически ты можешь запрограммировать любую последовательность действий для любого из сервисов.
Часто используемые клавиши я представлю ниже:
Tab {TAB} Enter {ENTER} или ~ Стрелка вверх {UP} Стрелка вниз {DOWN} Стрелка влево {LEFT} Стрелка вправо {RIGHT} Insert {INSERT} или {INS} Delete {DELETE} или {DEL} Home {HOME} End {END} Backspace {BACKSPACE}, {BS} или {BKSP} Escape {ESC} Клавиша Windows: {WIN}, левая {LWIN}, правая {RWIN} Apps/Menu {APPS} Print Screen {PRTSC} Shift + Ctrl ^ Alt %
Полезные команды:
{DELAY X} Задержка в X миллисекунд {CLEARFIELD} Очищает поле, где стоит курсор
KeePassX может искать даже окна — «Auto-Type-Window-1: * — Notepad»! Таким образом ты можешь создавать на каждую запись, оберегаемую KeePassX, свой сценарий для любой из программ, чтобы меньше стирать пальцы о клавиатуру.
Ознакомиться подробнее с документацией автозаполнения в KeePassX ты можешь на официальном сайте: goo.gl/6Js2A
ПРОГРАММНЫЙ СЛЕДОПЫТ
Часто случается так, что на боевом сервере начинает твориться что-то неладное. Первое, на что посоветуют обратить внимание бородатые админы, — это логи. Логи, несомненно, основной источник информации, особенно в загадочных ситуациях, но, что грустно, не всегда они способны вскрыть раковину непонимания и показать жемчужину причины сбоев.
Если проблема в программном продукте возникает на этапе разработки, ее быстро отслеживают и исправляют девелоперы своими продвинутыми средствами отладки. Чем мы хуже?
Давай исправим несправедливость ситуации и воспользуемся хотя бы простейшими приемами дебаг-кунг-фу. Мне кажется, что каждый юниксовый админ должен быть немного программистом и научиться этому для понимания корня проблемы. К слову, именно специалисты такого уровня наиболее ценны для работодателя.
Данное вступление посвящено моей любимой утилите strace. Все, что она делает, — в реальном времени отображает трассировку всех библиотечных вызовов. Ее можно использовать при запуске программы, например:
$ strace /bin/ls -lah execve("/bin/ls", ["ls", "-lah"], [/* 40 vars */]) = 0 brk(0) = 0x90f0000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such fi le or directory) mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76e6000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such fi le or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=94794, ...}) = 0 mmap2(NULL, 94794, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76ce000 close(3) = 0 ...
Или можно запросто аттачиться к уже работающим процессам, указывая PID ключом ‘-p’, и сохранять вывод прямиком в файл ‘-o’:
# strace -o output.txt -p 1234
Как нам может помочь многосотенный вывод системных вызовов на экран? Никак :). Поэтому системные вызовы лучше фильтровать через ключ ‘-e’. Чаще всего админов интересует вызов функции открытия файлов open(), например для понимания того, откуда софтина берет конфиг или куда пишет данные.
Рассмотрим этот метод применительно к Skype, чтобы узнать, какие файлы он читает при запуске. Чтобы убрать все неудачные попытки открыть файл, мы исключим строки, содержащие код ошибки ENOENT (нет такого файла или каталога), а также отфильтруем открытие библиотек *.so.
$ strace -e open skype 2>&1 | egrep -v "ENOENT|.so" ... open("/home/dbzer0/.fonts/arial.ttf", O_RDONLY) = 39 open("/home/dbzer0/.Skype/dbzer0/confi g.xml", O_RDONLY|O_LARGEFILE) = 40 open("/home/dbzer0/.Skype/dbzer0/confi g.xml", O_RDONLY|O_LARGEFILE) = 40 ...
Как видишь, позаимствованные у разработчиков инструменты могут принести пользу карьерному бытию администратора.
ВСЕ ПРОПАЛО!
Раз уж мы затронули тему бэкапирования, то неплохо было бы нам вспомнить и о его брате — восстановлении, на этот раз файлов. Недавно на моей работе произошел трагический случай — частично потерялись данные на сервере. Так получилось, что многие библиотеки и бинарные файлы потеряли свое содержимое и стали размером по 0 байт. Серверы были вне моей юрисдикции, поэтому вначале я мирно наблюдал за тем, как проблему обсудили, а затем собрались решать мои коллеги. Кто-то предлагал переустановить систему поверх, кто-то — организовать новый сервер и перенести только файлы с нужными данными, — собственно, типичные решения, которые ты и я многократно проходили на пыльном жизненном пути. Выслушав их предложения, я, как заправский супергерой, вмешался, и через 15 минут сервер был поднят с колен и принял привычную боевую стойку. Что я сделал? Ввел всего одну строку в баше, применив лишь знания средств операционной системы, а именно менеджера пакетов.
Ежедневно устанавливая массу пакетов в свой любимый дистрибутив, многие забывают, что они содержат список контрольных сумм файлов, позволяющих проверить их целостность. Для демонстрации данного функционала в системах, построенных на deb-пакетах, мы попросим помощи у debsums:
# apt-get install -y debsums
Нагнетая обстановку, специально для тебя я поломал бинарник нашего любимого Nmap :). Пусть утилита пробежится по всем известным ей пакетам и проверит, не повреждены ли какие-то установленные файлы:
# debsums -c ... /usr/bin/nmap
Вот и нашелся наш мученик! Дальше нужно выяснить, какому пакету принадлежит измененный файл. В этом нам поможет флаг ‘-S’ программы dpkg:
$ dpkg -S /usr/bin/nmap ... nmap: /usr/bin/nmap
Фактически, получив имя пакета, в данном случае «nmap», ты уже можешь его переустановить, используя ключ ‘—reinstall’ утилиты apt-get. Но мы сведем все операции в одну команду, которая, помимо всего, выудит из «dkpg -S» через разделитель «:» имя пакета с помощью «cut -d : -f1» и переустановит его:
# apt-get install -y --reinstall $(dpkg -S $(debsums -c) | cut -d : -f 1 | uniq -u)
Поскольку RPM-based серверов уж точно не меньше дебиановских, то я буду проклят, если не рассмотрю спасение и околокрасношапочных систем. Для проверки целостности файлов воспользуемся ключами ‘-V’ (проверить) и ‘-a’ (все пакеты):
# rpm -Va ... .M....... c /etc/cups/subscriptions.conf S.5....T. /usr/bin/nmap
Есть результат! Но что это за набор букв и цифр в начале?
Первые восемь символов — флаги, обозначающие, что аномального случилось с файлом:
• S — изменился изначальный размер файла;
• M — изменились права доступа или режим;
• 5 — отличается контрольная сумма MD5;
• D — отличается старший/младший номер файла устройства;
• L — изменился путь ссылки;
• U — отличается владелец (пользователь) файла;
• G — изменился владелец группы;
• T — отличается время изменения.
Из приведенного выше примера мы можем сделать вывод о том, что у /usr/bin/nmap изменился размер, обнаружилась неверная MD5-сумма и отличается время модификации.
Помимо этих флагов, следует два пробела и символ, характеризующий тип файла. Буква «c» означает конфигурационныйфайл, также значения могут быть:
• d — файл документации;
• g — файлы, изначально отсутствующие в пакете;
• l — файл лицензии;
• r — файл readme.
Как видишь, нет специального флага для того, чтобы отдельно выделить бинарные файлы, вместо этого там стоит пробел. Поэтому напишем регулярное выражение, с помощью которого мы выведем только строки, начинающиеся с флага «^S» (размер файла изменился). Последующие восемь символов нас не интересуют «.{8}», ведь на основании предыдущего утверждения мы уже можем сделать вывод о необходимости переустановки пакета. Далее (для бинарных файлов) следуют четыре пробела « {4}». А последующий путь может быть произвольной длины и состоять из произ-
вольных символов «.*».
Как итог, получим изменившиеся бинарные файлы с помощью версии grep, полностью поддерживающей расширенные регулярные выражения, — egrep:
# rpm -Va | egrep "^S.{8} {4}.*" ... S.5....T. /usr/bin/nmap
Достаем через разделитель ‘ ‘ пятый аргумент — путь к изменившемуся файлу:
# rpm -Va | egrep "^S.{8} {4}.*" | cut -d ' ' -f5 ... /usr/bin/nmap
Осталось узнать, какому пакету принадлежит поврежденный файл, для этого у RPM есть ключи ‘-qf’:
# rpm -qf $(rpm -Va | egrep "^S.{8} {4}.*" | cut -d ' ' -f5) | uniq -u ... nmap-6.25-1.fc18.x86_64
И завершающим аккордом добавляем к предыдущим командам переустановку всех пакетов с поврежденными бинарниками с помощью yum reinstall:
# yum reinstall -y $(rpm -qf $(rpm -Va | egrep "^S.{8} {4}.*" | cut -d ' ' -f5) | uniq -u)
Вуаля, рабочая система готова продолжать служить тебе и дальше.
ПОЧЕМУ В КОНФИГЕ SSLH УКАЗАН ПОРТ 443?
443-й порт используется по умолчанию неслучайно: так как уровень доверия к SSL высок, то именно его часто пробрасывают наружу и редко фильтруют файрволом.
HAPPY END
По каждому из рецептов ты можешь сделать вывод о том, что всегда можно найти новые решения даже к традиционным, казалось бы, устоявшимся подходам. А там, где и это не получается, — смело старайся создавать что-то свое, уникальное, новое.