Существуют рекомендованные, общепринятые средства сбора логов сетевого оборудования — SNMP, syslog и иже с ними. Обычно они прекрасно работают, но временами требуется нечто большее.
Представим себе следующий сценарий: некая сетевая железка на ровном месте исчезает из сети, и через несколько минут снова появляется. «Show version» указывает на перезагрузку, вызванную неким crash, который может быть следствием тысяч причин в сотнях компонентов ОС. Файл crashinfo отсутствует. Syslog сервер не получал никаких сообщений от устройства перед самым падением. Устройство покрыто сервисным контрактом – но TAC не может воспроизвести аварию у себя, а переданной клиентом информации слишком мало для точного установления причин аварии. Непонятно даже, было ли падение вызвано программным или аппаратным сбоем. Можно заменить устройство, но это не поможет, если причина программная. Переходить на другую версию ОС? А на какую? Ведь неизвестно, какой баг вызвал падение и закрыт ли он в новой версии – а там могут найтись и новые баги. В процессе общения сотрудник TAC упоминает, что перед самым падением, когда уже отказала сеть, устройство наверняка послало сообщение в консоль с информацией о том, какая подсистема упала и в связи с чем. У вас, конечно, уже есть терминальный сервер, но используется он только для аварийного доступа к устройству, а все прилетающие с консольного порта отслеживаемой железки сообщения он игнорирует. Надо как-то собирать эти сообщения. Этим мы и займемся.
Маленькое замечание. Все, что предлагается ниже, рассматривается лишь как дополнение к традиционным средствам мониторинга (в первую очередь штатный SNMP/syslog на устройствах) и предназначено для упрощения расследования причин аварий (ну а заодно для автоматического сбора лога перезагрузки). И хочется надеяться, что собранные таким образом данные не пригодятся никогда.
За основу берем статью "Терминальный сервер на базе маршрутизатора Cisco". Там указаны правильные первые шаги. Однако, там не рассмотрен вопрос собственно логирования сообщений. Ну и для подключения используется телнет, что, конечно, недопустимо. Можно считать данную статью продолжением той.
SSH клиентом и по совместительству сборщиком логов будет отдельный сервер на *NIX. В моем случае – Centos 6-й линейки.
Для начала рассмотрим сам по себе доступ к терминальному серверу. Штатный линуксовый клиент ssh не умеет автоматически вводить пароль, будучи вызванным из скрипта (при желании это обходится весьма некрасивым способом), но нам это и не надо.
1) Обновляем роутер, работающий терминальным сервером, до 15-й версии IOS (с поддержкой фичи «SSHv2 Enhancements for RSA Keys» — для роутерных IOS платформ на первый взгляд весь софт с k9 15-х линеек поддерживает данную фичу)
2) Создаем пару ключей RSA для SSH сессий на сервере. Просьбу «Enter passphrase» игнорируем и жмем enter.
[root@centos ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
1d:60:fe:72:b5:8c:1e:b5:5e:d1:3c:9c:67:15:9c:59 root@centos
The key's randomart image is:
+--[ RSA 2048]----+
| o ..E|
| o . .*o|
| . . o .+=|
| o * o oo|
| S * + . |
| + o . |
| . . |
| |
| |
+-----------------+
3) Берем публичный ключ RSA.
[root@centos ~]# cat /root/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0w2L4YVD/V303ccFatgtJxcS+JMYlPkmyufW36fUCogGjzWLbtMZGYoAW8vgy bVgN6r7lcbrbpF6oW9beGfHIWTBfUT898sUQL9jOOki0qvUWzkbej/po6agAK3KK/Z7QCtnAkbDQDb1SzHEmTx9rmboY EZosHOchQy+dvHEoBKCOMBrGKpYgdHfImjctKS3Q02TrkTO0+BoIFc2V32R9AukWFp7+ppGy2ZdoxLv5eEjlhcHukbM yKg9Kjc72/dPNbNkvLXcWKVnkebTmTJIQQyGU2qsAy2asgPC6D02gy6tZAdqp+0umEF2gLXlq2G1p3kn+AojH8bWvYBwyL2s6Q== root@centos
4) Если роутер не настроен SSHv2 сервером, то исправляем это досадное недоразумение стандартным способом.
5) Копируем публичный ключ сервера на роутер. Важно: строка длинная, поэтому потребуется копировать его частями, в 2-3 захода, нажимая enter между кусками и каждый раз убеждаться, что все символы влезли.
termserver(config)#ip ssh pubkey-chain
! указываем того пользователя, который сможет логиниться без пароля по сертификату
termserver(conf-ssh-pubkey)#username consoleuser
termserver(conf-ssh-pubkey-user)#key-string
termserver(conf-ssh-pubkey-data)# ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQE…
termserver(conf-ssh-pubkey-data)#...BwyL2s6Q== root@centos
! все содержание файла id_rsa.pub должно быть скопировано от первой до последней буквы
termserver(conf-ssh-pubkey-data)#exit
! если на этом этапе выскочила ошибка, то нужно копировать меньшими порциями
termserver(conf-ssh-pubkey-user)#exit
termserver(conf-ssh-pubkey)#
Проверяем, работает ли аутентификация по RSA:
[root@centos ~]# ssh consoleuser@10.10.0.10
termserver>
Теперь нужно проверить, удастся ли подключиться к консольному порту. Сначала разрешаем ssh на всех line’ах (попутно запрещая telnet, если он был открыт). На моем терминальном сервере нумерация идет от 1/0 до 1/15:
termserver(config)# line 1/0 1/15
termserver(config-line)# transport input ssh
Теперь само подключение. Из второго столбца «show line» узнаем номер линии (пусть будет 75) и делаем:
[root@centos ~]# ssh consoleuser:75@10.10.0.10
Жмем еще раз enter и видим:
Username:
А это уже послало то устройство, чья консоль подключена к 75-й линии терминального сервера. Аутентифицируемся для проверки:
Username: admin
Password:
router1> exit
router1 con0 is now available
Press RETURN to get started.
Отлично, доступ по ssh к консоли есть. Осталось настроить отправку логов в консоль на отслеживаемом оборудовании. Тут есть нюанс. Многие делают «no logging console», и в общем случае это имеет смысл. Нельзя допустить, чтобы консоль была перегружена сообщениями, да и они могут мешаться. Однако для наших целей это не годится. Потому первым делом на обеих сторонах:
router1(config)# line console 0
router1(config-line)#speed 115200
termserver(config)# line 1/0 1/15
termserver(config-line)#speed 115200
В данном случае выставлено 115200. Это по опыту довольно надежное значение (и чертовски быстрое по сравнению с родным 9600), но все равно надо проверить, что при получении больших блоков текста не приходят кракозябры.
Далее, нужно определить, какого уровня записи слать в консоль командой logging console X, где X — число от 1 до 7. «6» и «7» включать категорически нельзя, там лишь информационные сообщения (чаще всего бесполезные), которых может быть много (особенно на «7» — это уровень дебагов, который должен писаться только в buffered).«5» и «4» — обычно годится, но надо проанализировать, сколько сообщений с таким уровнем попадает в буфер. Например, "%ASA-4-106023" — это сообщения о блокировании пакетов на файрволах ASA, которых может быть крайне много, и нам не нужно гнать их в консоль. Возможно, имеет смысл поменять facility отдельных сообщений syslog на самом устройстве. Нам безусловно интересно собирать любые сообщения с facility от 1 до 3, а также не помешают события поднятия/падения интерфейсов (хотя если речь идет про коммутатор с сотнями портов, то это под вопросом). В общем, есть поле для размышлений.
Терминальных серверов может быть несколько, у каждого десятки линий. Да и любой роутер без HWIC-16A является терминальным сервером на один порт (AUX). Сейчас у нас уже настроен доступ к консолям по ssh и отправка логов в консоль, но нет записи событий. Начинаем писать скрипты, причем такие, чтобы добавление новой консоли было делом простым и приятным.
Для начала, напишем скрипт, который будет парсить список хостов и линий, запуская соединения. Пусть он зовется startcon.sh
#!/bin/bash
#куда сбрасывать логи сессий. В данном примере они сохраняются в файл, но ничто не мешает сразу отправлять их хоть в syslog, хоть в почту.
LOGFOLDER="/root/logs/"
#откуда брать список хостов и линий
LIST="/root/collectconsole/consolelist.txt"
#где искать скрипт на запуск ssh
LOCATION="/root/collectconsole"
#Парсим список на предмет записей вида «75,10.10.0.1,termserver», где первая запись – линия, вторая – адрес терминального сервера, а третья – имя подключенного сетевого железа – для проставления хостнеймов на файлах логов. Номер линии может содержать от одной до трех цифр, а хостнейм по нашей политике содержит только буквы, цифры и дефис
for i in $(cat $LIST | egrep -o "[0-9]{1,3},([0-9]{1,3}.){3}[0-9]{1,3},[a-zA-Z0-9-]+") ; do
#Вырезаем первую и вторую запись из строки для скармливания их connectcon.sh – скрипту, осуществляющему подключение к линиям терминального сервера.
ARGS="$(echo $i | cut -f 1 -d ",") $(echo $i | cut -f 2 -d ",")"
#Запускаем соединение только если его еще не было
if ! ps ax | grep -v grep | grep "connectcon.sh $ARGS" > /dev/null ;
then
$LOCATION/connectcon.sh $ARGS >> $LOGFOLDER$(echo $i | cut -f 3 -d ",").log &
fi
Done
Создаем consolelist.txt с указанием номеров линий (вторая колонка из «show line» терминального сервера), адресов терминальных серверов и названий подключенных к этим линиям устройств:
nano consolelist.txt
66,10.10.0.10,router1
70,10.10.0.10,router2
71,10.10.0.10,router3
74,10.10.0.10,router4
67,10.10.0.10,router5
72,10.10.0.10,router6
75,10.10.0.10,router7
76,10.10.0.10,router8
79,10.10.0.10,router9
Создаем скрипт connectcon.sh. С ним не все просто. Изначально я пытался сделать его обычным bash скриптом, вызывающим ssh. Но как оказалось, ssh, будучи запущенным в фоне, отказывается редиректить все услышанное в файл. Решение было найдено. Сначала нужно установить интерпретатор expect – для centos это «yum install expect». Затем создать скрипт:
#!/usr/bin/expect –f
#мы ожидаем, что соединение будет висеть вечно – пока его не отобьет та сторона. Ну или пока сам процесс не будет убит.
set timeout -1
#Собираем аргументы, с которыми скрипт запускался.
set line [lrange $argv 0 0]
set ipaddr [lrange $argv 1 1]
#Теперь запуск ssh. Нам нужно, чтобы соединение держалось вечно. Как только его отобьют с той стороны – надо переподключиться. Т.е. бесконечный цикл. «expect timeout» останавливает выполнение цикла до тех пор, пока команда перед ним не отработает, что может быть вызвано разрывом ssh соединения с терминальным сервером – тогда надо переподключиться, подождав 2 минуты.
while { true } {
spawn ssh consoleuser:$line@$ipaddr
expect timeout
sleep 120
}
Как известно, несколько подключений не могут одновременно использовать одну консоль. А что делать, если нужно самому зайти на консольный порт и выполнить какие-либо действия? Решение простое: нужно зайти в обычную сессию терминального сервера и поклирить нужную линию.
termserver#clear line 75
[confirm]
[OK]
Этим мы выбили сервер логирования с терминального сервера. Команда «sleep 120» в скрипте даст нам 2 минуты на то, чтобы самим залогиниться. А сборщик логов будет продолжать раз в 2 минуты стучаться в дверь, пока мы не выйдем.
Всё. Запускаем startcon.sh:
[root@centos collectconsole]# ./startcon.sh
[root@centos collectconsole]#
Смотрим процессы:
[root@centos collectconsole]# ps -ef | grep -E "connectcon|ssh"
…
root 23151 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 66 10.10.0.10
root 23152 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 70 10.10.0.10
root 23153 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 71 10.10.0.10
root 23154 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 74 10.10.0.10
root 23155 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 67 10.10.0.10
root 23156 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 72 10.10.0.10
root 23157 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 75 10.10.0.10
root 23158 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 76 10.10.0.10
root 23159 1 0 14:54 pts/4 00:00:00 /usr/bin/expect -f /root/collectconsole/connectcon.sh 79 10.10.0.10
root 23239 23155 0 14:54 pts/2 00:00:00 ssh consoleuser:67@10.10.0.10
root 23240 23156 0 14:54 pts/3 00:00:00 ssh consoleuser:72@10.10.0.10
root 23242 23158 0 14:54 pts/6 00:00:00 ssh consoleuser:76@10.10.0.10
root 23243 23159 0 14:54 pts/7 00:00:00 ssh consoleuser:79@10.10.0.10
root 23244 23153 0 14:54 pts/8 00:00:00 ssh consoleuser:71@10.10.0.10
root 23247 23152 0 14:54 pts/9 00:00:00 ssh consoleuser:70@10.10.0.10
root 23248 23154 0 14:54 pts/1 00:00:00 ssh consoleuser:74@10.10.0.10
root 23255 23151 0 14:54 pts/10 00:00:00 ssh consoleuser:66@10.10.0.10
root 23341 23157 0 15:09 pts/5 00:00:00 ssh consoleuser:75@10.10.0.10
…
Смотрим хранилище логов (router4 что-то уже записал, остальные пока молчат):
[root@centos collectconsole]# ls -l /root/logs/
-rw-r--r-- 1 root root 58 Sep 22 14:54 router1.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router2.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router3.log
-rw-r--r-- 1 root root 115 Sep 22 14:57 router4.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router5.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router6.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router7.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router8.log
-rw-r--r-- 1 root root 58 Sep 22 14:54 router9.log
Смотрим на терминальном сервере:
termserver#sh line
Tty Line Typ Tx/Rx A Modem Roty AccO AccI Uses Noise Overruns Int
0 0 CTY - - - - - 0 0 0/0 -
1 1 AUX 115200/115200- inout - - - 0 0 0/0 -
* 1/0 66 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/1 67 TTY 115200/115200- - - - - 0 0 0/0 -
1/2 68 TTY 115200/115200- - - - - 0 0 0/0 -
1/3 69 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/4 70 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/5 71 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/6 72 TTY 115200/115200- - - - - 0 1 0/0 -
1/7 73 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/8 74 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/9 75 TTY 115200/115200- - 1 - - 2 0 2/4 -
* 1/10 76 TTY 115200/115200- - - - - 0 0 0/0 -
1/11 77 TTY 115200/115200- - - - - 0 0 0/0 -
1/12 78 TTY 115200/115200- - - - - 0 0 0/0 -
* 1/13 79 TTY 115200/115200- - - - - 0 0 0/0 -
1/14 80 TTY 115200/115200- - - - - 0 0 0/0 -
1/15 81 TTY 115200/115200- - - - - 0 0 0/0 -
Указанные в конфигурационном файле линии заняты (помечено звездочками). Отлично, скрипты работают.
Теперь к запуску скрипта startcon.sh. Можно поместить его в init.d и/или в cron.daily (тогда добавленные в список узлы начнут писаться на следующий день либо после рестарта системы). И — задача решена.
Осталось отметить несколько немаловажных моментов.
1) Имеет смысл средствами Secure ACS сервера ограничить права используемого для данных целей аккаунта. Он должен уметь логиниться, но ничего кроме этого. Причем логиниться он должен на ограниченный список устройств.
2) Для запуска скриптов лучше создать отдельный аккаунт на Linux-машине. Соответственно — под ним сгенерировать ключи RSA и именно их подсунуть на терминальный сервер. Да, в данной статье я всё время сидел под рутом, но все знают, что это нехорошо. Да и местоположение хранилища логов стоит изменить.
3) Logrotate на линуксе позволит файлам логов не достигать астрономических размеров. Лучше его включить для данных файлов. Алгоритм архивирования зависит от скорости наполнения файлов.
4) Надо жестко ограничить доступ к файлам логов.
5) Сервер сбора логов лучше разместить по отношению к терминальному серверу так, чтобы падение любой отслеживаемой железки не нарушало связь между сборщиком и терминальным сервером дольше чем на несколько секунд. Да и вообще, стройте сети так, чтобы отказ одного устройства вызывал сбой работы сети продолжительностью не более пары-тройки секунд.
6) Ничто не мешает использовать эти скрипты для записи событий любых устройств, подключенных к терминальному серверу — не только цискиных устройств.
7) У меня мало опыта написания shell-скриптов, и в предложенной конфигурации наверняка можно много чего улучшить. Буду рад любым дополнениям.
Автор: JDima