Бекапим Mikrotik с помощью SSH и SCP
Если заглянуть назад в прошлое когда еще не было Ansible или других систем удаленного администрирования linux мы пользовались только своими подручными скриптами, позволяли им подключаться к системам по ssh с помощью ключей. Думаю и по сей день многие использую свои скрипты взамен системам централизованного управления.
Я и решил поделиться своим опытом.
Нужно было написать скрипт который умеет ходить на заданное количество хостов и бекапит некоторые файлы конфигураций.
Логика работы выстроилась сразу. Зайти на хост по ssh, выполнить некоторые команды для подготовки бекапов и забрать готовые файлы с помощью scp.
Первым делом нужно создать пользователя который будет иметь доступ к необходимым данным с необходимыми правами. Тут я думаю каждый сам сможет разобраться в какую группу определить пользователя. Главное помнить:
- Имя пользователя для работы входа без пароля должно быть одинаковое на всех хостах или придется для каждого хоста указывать свой логин что не есть удобно;
- Пользователь должен иметь доступ на хост без пароля (ssh-keygen в помощь) ;
- Пользователь должен иметь доступ к файлам которые необходимо забрать.
тут можно пойти несколькими путями 1) выполнить команду в удаленном shell для сбора необходимых данных подлежащих резервированию и потом их забрать 2) настроить cron на хосте и выполнять команду сбора данных, а уже потом, когда нибудь, заходить на хост и забирать готовые пирожки бекапы. Конечно мы пойдем по первому пути
Имеем такую конфигурацию —
10 хостов (Mikrotik) с которых необходимо получить два типа бекапов — бинарный (для восстановления с нуля) и конфигурацию без паролей и сертификатов для заливки на работающий конфиг. Так же в полном нашем распоряжении имеется машина с debian 8 на борту назовем ее сервер (и не важно что это контейнер, важно что это дебиан) ну и конечно куда ж без него — zabbix-server.
- IP Mikrotik — 10.10.0.1, 10.10.1.1, 10.10.2.1, 10.10.3.1, 10.10.4.1, 10.10.5.1, 10.10.6.1, 10.10.7.1, 10.10.8.1, 10.10.9.1;
- IP Zabbix-server — 10.10.10.10;
Для упрощения задачи zabbix-hostname будет в формате mik (третий октет в десятичном формате).host таким образом получаем —
- Zabbix hostnames — mik0.host, mik1.host, mik2.host, mik3.host, mik4.host, mik5.host, mik6.host, mik7.host, mik8.host, mik9.host
Если кто не помнит — zabbix hostname мы указываем в файле настроек zabbix-agent (агент тут не нужен, но все же) и на сервере zabbix в web-ui.
Первым делом создаем на нашем сервере ключ RSA. Почему RSA — да вообщем то по привычке, кстати, старые версии RB поддерживают только DSA, а все что свежее 6.35 уже работает с RSA и DSA, потому смотрите по обстановке, можно и обновиться, как сделал я :), если у вас уже есть готовый ключ — пропускайте этот шаг.
ssh-keygen -t RSA
Переносим содержимое файла $HOME/.ssh/id_rsa.pub с сервера на наши хосты. Я ленивый и для Mikrotik использовал winbox.
Для линукс можно сделать проще создаем скрипт sh и запускаем его от имени пользователя которым мы будем ходить на хост за бекапами (на хостах пользователь уже должен быть) такого содержания —
Если у вас ключ DSA то измените id_rsa.pub на id_dsa.pub
#!/usr/bin/env bash
hosts=(10.10.0.1 10.10.1.1 10.10.2.1 10.10.3.1 10.10.4.1 10.10.5.1 10.10.6.1 10.10.7.1 10.10.8.1 10.10.9.1)
username='user'
for host in ${hosts[*]}
do
cat $HOME/.ssh/id_rsa.pub | ssh -o "StrictHostKeyChecking no" ${user}@${host} 'cat >> ~/.ssh/authorized_keys'
done
Запускаем его и вводим поочередно пароли для всех 10 серверов.
В скрипте есть подвох — специально не стал добавлять проверку, а то совсем забуду как клавиши нажимать -) если вы все прочитали то подвох не сработает.
Тааак, что там дальше, а, точно, мы уже умеем ходить без пароля на все хосты под пользователем допустим user.
Мы же хотим получить конфиги Mikrotik. Собственно приступим.
Создаем на сервере вот такой вот скрипт —
#!/usr/bin/env bash
hosts=(10.10.0.1_mik0.host_22 \
10.10.1.1_mik1.host_22 \
10.10.2.1_mik2.host_22 \
10.10.3.1_mik3.host_22 \
10.10.4.1_mik4.host_22 \
10.10.5.1_mik5.host_22 \
10.10.6.1_mik6.host_22 \
10.10.7.1_mik7.host_22 \
10.10.8.1_mik8.host_22 \
10.10.9.1_mik9.host_22 )
# bash array of values. All values are arrays too, after remove splitter "_".
# Sub array content IP_ZABBIX-HOSTNAME_SSH-DAEMON-PORT
cdate=`date +%d-%m-%Y` # System date =) Hi Max
dir="/mik_backup/" # Storage for backups
cmd="/system backup save name=backup; export file=backup.rsc hide-sensitive" # command that do the preparation of backup
username="user" # SSH user
zabbix_hp=(10.10.10.10 10051) # IP then PORT
age="30" # remove all backups older then 30 days
itemname="backup" # zabbix item
error_value="1" # error value for trigger
value="0" # good value =)
for host in ${hosts[*]} # Get values from main list
do
hostname=($(echo ${host} | tr "_" " ")) # Get values from sub list
ssh ${username}@${hostname[0]} -o "StrictHostKeyChecking no" -p${hostname[2]} "${cmd}"
new_dir="${HOME}${dir}${hostname[1]}/${cdate}"
mkdir -p ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.backup ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.rsc ${new_dir}
check=`find ${new_dir} -type f -name backup.*`
if [ "${check}" == "" ]
then
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${error_value}
else
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${value}
fi
done
find ${HOME}${dir} -mindepth 2 -mtime ${age} -type d -exec rm -rf {} \; #clear dirs
Постарался максимально прокомментировать все что происходит в скрипте. Давайте разберем, что же тут такое делается.
Тут мы говорим каким интерпретатором будем выполнять код
#!/usr/bin/env bash
Тут создаем массив с необходимыми нам данными для подключения к хостам и отправки данных в zabbix
hosts=(10.10.0.1_mik0.host_22 \
10.10.1.1_mik1.host_22 \
10.10.2.1_mik2.host_22 \
10.10.3.1_mik3.host_22 \
10.10.4.1_mik4.host_22 \
10.10.5.1_mik5.host_22 \
10.10.6.1_mik6.host_22 \
10.10.7.1_mik7.host_22 \
10.10.8.1_mik8.host_22 \
10.10.9.1_mik9.host_22 )
Тут думаю только одна переменная нуждается в объяснении — $cmd. Это две команды которые выполняются на Mikrotik последовательно. Первая создает бинарный бекап
вторая создает скрипт с настройками без выгрузки паролей и ключей шифрования.
cdate=`date +%d-%m-%Y` # System date =) Hi Max
dir="/mik_backup/" # Storage for backups
cmd="/system backup save name=backup; export file=backup.rsc hide-sensitive" # command to do the preparation of backup
username="user" # SSH user
zabbix_hp=(10.10.10.10 10051) # IP then PORT
age="30" # remove all backups older then 30 days
itemname="backup" # zabbix item
error_value="1" # error value for trigger
value="0" # good value =)
Основное тело программы. На входе в цикл мы имеем массив содержащийся в переменной $hosts. Цикл работает так — берем первый элемент массива, у нас он равен 10.10.0.1_mik0.host_22 и начинаем с ним работать. Первым же действием мы заносим в переменную $hostname массив созданный из первого элемента массива $hosts. Делаем мы это с помощью команды tr по сути как в python эмитируем действие метода строки .split (). Получается вполне сносно. Мы получаем 3 элемента в массиве $hostname. Первый элемент — ip хоста. Второй элемент — zabbix-hostname. Третий элемент — ssh-port.
Дальше мы обращаемся к этим элементам с помощью индекса, опять же python.
Далее формируем древо каталогов для хранения файлов и указываем scp какие файлы забирать. Прошу не пинать — если кто подскажет как с помощью scp в такой конструкции обращаться к файлам по маске + в карму.
После того как мы получили файлы — отправляем в zabbix сообщение об успехе.
Проверка создания конфига производится простым поиском файла в каталоге назначения. Можно было сделать сравнение md5 на Mikrotik и в каталоге назначения, но это уже другая история, хотя я так делал.
for host in ${hosts[*]} # Get values from main list
do
hostname=($(echo ${host} | tr "_" " ")) # Get values from sub list
ssh ${username}@${hostname[0]} -o "StrictHostKeyChecking no" -p${hostname[2]} "${cmd}"
new_dir="${HOME}${dir}${hostname[1]}/${cdate}"
mkdir -p ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.backup ${new_dir}
scp -P${hostname[2]} ${username}@${hostname[0]}:backup.rsc ${new_dir}
check=`find ${new_dir} -type f -name backup.*`
if [ "${check}" == "" ]
then
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${error_value}
else
zabbix_sender -z ${zabbix_hp[0]} -p ${zabbix_hp[1]} -s ${hostname[1]} -k ${itemname} -o ${value}
fi
done
Тут чистим место. Переменная $age поможет нам сохранить бекапы столько сколько нам этого надо
find ${HOME}${dir} -mindepth 2 -mtime ${age} -type d -exec rm -rf {} \; #clear dirs
Теперь самое тривиальное.
Создаем на сервере zabbix шаблон или просто элемент данных типа zabbix_trapper на наших узлах которые мы заблаговременно добавили на мониторинг в zabbix.
Не буду выкладывать шаблон из одного элемента данных и одного триггера. Думаю сделать его сможет каждый. Главное помнить, если хосты мониторятся через zabbix-proxy, данные вы должны отправлять на zabbix-proxy. В ином случае отправляем все на zabbix-server.
Не важно даже какой будет IP у этих хостов в zabbix web ui. Важно чтобы совпадали hostname с данными в скрипте.
PS. На все скрипты нужно кинуть chmod +x так их можно будет запускать без вызова интерпретатора.
P.S. S Чтобы передавать scp список файлов для резервного копирования на linux можно сделать еще одни массив и вложить его в цикл for. Можно сделать все в виде получаемых параметров. Ну вообщем можно развлечься.