Централизованный бэкап Mikrotik устройств при помощи bash-скрипта

Всем привет, в моей первой публикации на хабре хочу поделиться готовым решением для бэкапов устройств Mikrotik.

Для микротика предусмотрены два вида бэкапов конфигурации, это бинарный бэкап и экспорт конфигурации. Бинарный бэкап имеет свои преимущества и недостатки. Преимуществом является то, что после восстановления бинарного бэкапа у вас сохраняется вся конфигурация( с пользователями, паролями, импортированными для пользователей ssh-ключами); недостатком же является то, что такой бэкап нельзя восстановить на другого типа устройство.

В целом и общем это полноценный бэкап, его восстановление занимает мало времени и восстанавливает всю конфигурацию. Экспорт конфигурации как способ бэкапа в свою очередь отчасти сглаживает эти недостатки, являясь скриптом для ребилда микротика. Суть тут в том, что выводятся все настройки, которые могут быть экспортированы. Преимуществом является то, что можно посмотреть и пощупать, что же там экспортируется, а к недостаткам можно опять же отнести то, что это тоже упирается в модель. но чуть меньше. Иногда дело в количестве интерфейсов, иногда ещё в чём-то. Так же не все настройки могут быть экспортированы в текст(пользователи, файлы на усройстве, ssh-ключи).

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

Предложенный вариант бэкапов по почте или ftp мне не нравится тем, что тут всё либо по почте, либо по ftp, всё plaintext. Так же не нравится то, что надо держать какой-то ящик или ftp и микротики сами будут слать. Мне удобнее всё делать с сервера бэкапов.

По сути для бэкапа по ssh руками достаточно просто зайти на микротик, выполнить бэкап и забрать его. Соответственно это можно автоматизировать.

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

Итак, переходим непосредственно к скрипту.

Файлы скрипта


Для работы скрипта используется исполняемый файл и параметром к нему указывается файл конфигурации.

/usr/local/bin/mbkp /etc/mikrotik_backup/xxx-core01.cfg

Соответственно размещается бэкапный скрипт в директории /usr/local/bin/ для простоты поиска оного в PATH, а файлы конфигурации хранятся в /etc/mikrotik_backup/ в соответствии с FHS. Размещение файлов дело сугубо личное, мне просто так удобнее.

Исполняемый скрипт


Самая первая секция содержит shebang, проверки конфигурационного файла, не указав который, скрипт не выполнится. Далее идут дефолтные переменные, достаточно лаконичные, часть из которых вы можете переопределить в файле конфигурации конкретного устройства.

#!/bin/bash

# mikrotik-backup script
# author Tenhi

# Initial checks
# Config file should be provided and should be readable
[[ -z "$1" ]] && echo "ERR: no config file provided" && exit 1
! [[ -r "$1" ]] && echo "ERR: cannot  read $1" && exit 1

# Default variables ( may be overrided in custom config )
#### Connection ####################################
TGT_PORT="22"                                           # default ssh-port
TGT_USER="bkpuser"                                      # Default backup user
IDL="5s"                                                # Default idle time
#### Backup variables ##############################
BKP_BINPWD="NvLB37zchdor9Y4E8KSpxibWHATfjstnw"          # Default password for binary backup    33cr
BKP_EXPPWD="hGAEJKptcCznB2v8RaHkoxiSTYNFZ3suW"          # Default password  for export          33cr
ST_RTN="30"                                             # Default retention time
#### Storage variables #############################
ST_ROOT="/mnt/bkp_share/mikrotik"                       # Default storage root

Далее идёт строка, которая делает source конфигурационного файла:

#######################################################################################################################
# Importing target config where you can override options
source $1
#######################################################################################################################

После идёт секция с некоторыми переменными, которые лучше задавать после того, как импортируется конфигурационный файл и системные утилиты, которые я для пущей надёжности ищу через which:

#### Utils #############################################################################################
CMD_FIND=$(which find)
CMD_MV=$(which mv)
CMD_GZ=$(which gzip)
CMD_CHO=$(which chown)
CMD_CHM=$(which chmod)
CMD_MKD=$(which mkdir)" -p "
CMD_RM=$(which rm)
CMD_DATE=$(date +%Y%m%d_%H%M) # date in format YYYYMMDD_HHmm
CMD_SSL=$(which openssl)
CMD_SSH=$(which ssh)
CMD_SCP=$(which scp)

########################################################################################################
ST_FULL=$ST_ROOT/$ST_HOSTNAME"/"        # full path to .backup (/root_storage/hostname/)
ST_ARCH=$ST_FULL"archive/"              # full path to archive (/root_storage/hostname/archive)
TGT_BKPNAME_BIN=$ST_HOSTNAME"_"$CMD_DATE".backup"
TGT_BKPNAME_EXP=$ST_HOSTNAME"_"$CMD_DATE".export"

SSH_OPT=" -o ConnectionAttempts=5 -o ConnectTimeout=5s \
-o PasswordAuthentication=no -o PreferredAuthentications=publickey \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o GlobalKnownHostsFile=/dev/null -o CheckHostIP=no "
SSH_STR="$CMD_SSH -2 -4 -p $TGT_PORT -l $TGT_USER $TGT_IP $SSH_OPT"
SCP_STR="$CMD_SCP -2 -4 -B $SSH_OPT -P $TGT_PORT $TGT_USER@$TGT_IP:/$TGT_BKPNAME_BIN $ST_FULL"

После идёт определение функций скрипта, описание я дам ниже:

#### Defining functions ################################################################################

function fn_check_log {
# Function for checking need of creating logfile
LOG=$ST_ROOT/"LOG.txt"                                  # log-file location


 if [[ -r $LOG ]]
  then
   return 0
  else
   echo "
################################################
# Logfile for mikrotik backups
# The format is:
#       DATE;STATE;FILENAME
# author: Tenhi(adm@tenhi.ru)
################################################
" > $LOG
 fi
}
function fn_check_readme {
# Function for checking need of creating readme

README=$ST_ROOT"/README.txt"      # README File

 if [[ -r $README ]]
  then
   return 0
  else
   echo "
# ===
# Here you can find backups for all Mikrotiks
# Files located in:
#       hostname/...
# Archived backups are in:
#       hostname/archive/...
# You can get backup info for all jobs in LOG.txt
# ===
" > $README
 fi
}
function fn_check_directory {
# Function for checking||creating full-path dirs

 if [[ -d $ST_FULL"archive" && -r $ST_FULL"archive" ]]
  then
   return 0
  else
   $CMD_MKD $ST_FULL"archive"
   $CMD_CHO root:root $ST_FULL
   $CMD_CHM 755 $ST_FULL
 fi
}

function fn_mikrotik_cleanup {
# Function for cleaning up target mikrotik

$SSH_STR "ip dns cache flush"
$SSH_STR "console clear-history"
}
function fn_mikrotik_fixtime {
# Function for setting ntp client

$SSH_STR "ip cloud set update-time=no; system ntp client set primary-ntp=0.0.0.0 secondary-ntp=0.0.0.0 enabled=yes server-dns-names=pool.ntp.org"
}

function fn_backup_binary {
 # Function for saving binary backup
 # Changed 20170901
 T_BKPSTR="system backup save name=$TGT_BKPNAME_BIN dont-encrypt=no password=$BKP_BINPWD"
 T_BKPCLN="file remove [find name=$TGT_BKPNAME_BIN]"

 $SSH_STR $T_BKPSTR                      # Initializing backup

 sleep $IDL && $SCP_STR                  # Copy file to storage
 sleep $IDL && $SSH_STR $T_BKPCLN        # Remove created file on mikrotik
}
function fn_backup_export {
 # Function for saving exported config

 # NOTE: decrypt the file
 # openssl des3 -d -salt -in encryptedfile.txt -out normalfile.txt
 EXP_TMP_FILE="/tmp/"$RANDOM".export"

 sleep $IDL && $SSH_STR export > $EXP_TMP_FILE
 $CMD_SSL des3 -salt -k $BKP_EXPPWD -in $EXP_TMP_FILE -out $ST_FULL$TGT_BKPNAME_EXP".des3"
 $CMD_RM $EXP_TMP_FILE

}

function fn_backup_retention {
# Function for rotating old backups

$CMD_FIND $ST_FULL -mtime +$ST_RTN -type f -exec $CMD_MV {} $ST_ARCH \;
$CMD_FIND $ST_ARCH -type f -exec $CMD_GZ {} \;
}

function fn_log {
# Function for recording results to logfile

 if [[ -r $ST_FULL$TGT_BKPNAME_BIN ]]
  then
   echo $CMD_DATE";okay;"$TGT_BKPNAME_BIN >> $LOG
  else
   echo $CMD_DATE";fail;"$TGT_BKPNAME_BIN >> $LOG
 fi

 if [[ -r $ST_FULL$TGT_BKPNAME_EXP".des3" ]]
  then
   echo $CMD_DATE";okay;"$TGT_BKPNAME_EXP".des3" >> $LOG
  else
   echo $CMD_DATE";fail;"$TGT_BKPNAME_EXP".des3" >> $LOG
 fi
}

Все функции подписаны, но я немного поясню. Функции fn_check_log fn_check_readme проверяют и создают первичное наполнение файлов LOG.TXT и README.TXT

fn_check_directory проверяет создана ли директория для устройства, которое вы описываете в директиве ST_HOSTNAME="" в конфиг-файле

fn_mikrotik_cleanup выполняет очистку кэша DNS и истории команд в консоли, чтобы не тянуть это в бэкап и в секурных целях.

fn_mikrotik_fixtime вещь опциональная, которая принудительно устанавливает ntp-сервер для обновления времени. В этой функции можно написать всё, что угодно, если вы хотите постоянно это выполнять. Можно так же не пользоваться этой функцией если нет нужды.

fn_backup_binary и fn_backup_export делают бинарный и экспорт-бэкапы соответственно. При чём стоит заметить, что вместо plaintext, создаётся зашифрованный экспорт-файл используя openssl. (Соответственно нужно иметь openssl в системе)

fn_backup_retention упаковывает бэкапы старше чем ST_RTN дней, которую вы задаёте в конфиг файле, если не используете стандарт(30 дней). Функция кладёт их в папку archive

fn_log в самом конце скрипта создаёт запись в файле LOG.TXT о статусе бэкапа

Конфигурационный файл устройства


Касательно файла конфигурации минимальная конфигурация это:

# Target device address (ip or DNS)
TGT_IP="nmr01.loc.lan"
# Also will be used to form backupname like $ST_HOSTNAME_YYYYMMDD-HHmm.backup|export
ST_HOSTNAME="loc-nmr01"

Остальные директивы конфиг файлы подробно описаны и являются опциональными, то есть имеют дефолтное значение:

# Example config for mikrotik-backup script
# author Tenhi (adm@tenhi.ru)

# Required parameters are uncommented. Optional are commented
# You can easly uncomment some of them to override defaults

#### Connection ####################################
# Target device address (ip or DNS)
TGT_IP=""

# Target device ssh port
# TGT_PORT="22"

# Target ssh user
# TGT_USER="backupser"

# Idle for avoiding ban (or something else)
# IDL="1s"

#### Backup variables ##############################

# Password for binary backup ( default is in main script and there are no chance to do it without password :) )
# BKP_BINPWD=""

# Password for export ( default is in main script )
# BKP_EXPPWD=""

#### Storage variables #############################

# root backup location 
# ST_ROOT="/mnt/backup" 

# will be used to create dir $ST_ROOT/$ST_HOSTNAME
# Also will be used to form backupname like $ST_HOSTNAME_YYYYMMDD-HHmm.backup|export
ST_HOSTNAME=""

# backup retention to archive files older $ST_RTN days to $ST_ROOT/$ST_HOSTNAME/archive/
# ST_RTN="30"

#### Logging #######################################
# LOG default location
# LOG=$ST_ROOT/$ST_HOSTNAME/"LOG.txt"

Cron


Если вы хотите зашедулить выполнение, можно использовать пример cron задачи:
# Mikrotik backup script mbkp
  # Config:
  MCFG="/etc/mikrotik_backup"
  MBKP="/usr/local/bin/mbkp"
  MLOG="/var/log/mikrotik_backup/log"
  # Tasks:
00 03 * * *     $MBKP $MCFG"/xxx-core01.cfg" >>$MLOG 2>>$MLOG           # XXX DC New core router node1

Итак, сразу об использовании в планировщике cron можете копировать и пользоваться моим решеним, но я немного поясню. Естественно все пути должны существовать и файлы должны быть доступны на чтение, а скприт на выполнение. Использование переменных в кроне не возбраняется и немного улучшает читабельность, когда кроновских задач у вас больше 50.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 26
  • 0
    Делал подобное. Я сохранял еще лицензию при постановке устройство в систему (система — это 3 скрипта =)) бекапов. И все это благолепие заворачивал в git репозиторий. Коммитил раз в месяц, мне чаще не нужно было. Плюс в том, что можно было различие отследить.
    • 0
      ну тут я ещё сделал пароль по дефолту(нет шанса сделать бэкап без пароля). И костылик для экспорта с «паролем».

      Про бэкап лицензии в первый раз слышу, а зачем?
      • 0
        возможно использовать на других устройствах после смерти…
        • 0

          Можно делать бэз пароля:
          dont-encrypt=yes

      • 0
        Почему не github.com/ytti/oxidized?
        • 0
          не знаком с руби, да и не хочу быть знаком.
          Тут ещё и в простоте дело, всё с минимумом зависимостей на обычном ssh, единственное это openssl, что надо ставить.

          Нет веба, баз данных — нечему ломаться
        • 0

          Можно делать без пароля:
          dont-encrypt=yes

          • 0
            ну, это да. Но у себя я хотел сделать так, чтобы пароль был всегда.
          • 0
            Идея. А нельзя ли вытягивать пользователей, файлы и т.д. через SFTP из микротика?
            • 0
              файлы — без проблем. А пользователей никак не вытянуть.
              Пользователь на устройстве микротик не вытягивается никак(насколько я знаю). Да и смысла особого нет его вытягивать, можно написать скрипт, который его так же создаст, задаст пароль и наимпортирует ключей.

              Касательно файлов на устройстве — в самом скрипте это уже делается. Когда создаётся бэкап, он кладётся на устройство, а потом с устройства копируется, и сразу же удаляется, ибо если они накопятся, то места под новые бэкапы уже не будет.

              создаётся, копируется, удаляется:
              T_BKPSTR="system backup save name=$TGT_BKPNAME_BIN dont-encrypt=no password=$BKP_BINPWD"
               T_BKPCLN="file remove [find name=$TGT_BKPNAME_BIN]"
              
               $SSH_STR $T_BKPSTR                      # Initializing backup
              
               sleep $IDL && $SCP_STR                  # Copy file to storage
              sleep $IDL && $SSH_STR $T_BKPCLN # Remove created file on mikrotik
              

            • 0
              В чем смысл определения полных путей для всех команд, но через which? Это карго-культ — в больших кодовых базах на sh в самом деле определяют полные пути к используемым командам, но:
              1. либо явно прописывают системный путь, т.е. /bin/ln, /usr/bin/env…
              2. либо меняют текущий PATH

              У вас ничего из перечисленного не происходит, CMD_CHO etc затрудняют чтение и не добавляют функциональности.
              • 0
                пожалуй, но лично мне так показалось удобнее
                • 0
                  неудобно читать, но и не нужно задумываться, что-где лежит на разных системах.
              • 0
                Спасибо за статью, кое-что взял себе на заметку.
                У нас используется сервер мониторинга Nagios, в конфигах которого (текстовых) описаны разные железки, которые мониторятся. Так-же есть скрипт, который смотрит эти конфиги, и, в зависимости от типа устройства, вызывает тот или иной способ получения бекапа (через ssh, telnet, wget). Скрипт может быть запущен вручную, через cron или как сервис самого Nagios. Собственно сами резервируемые конфиги устройств ложатся в каталог /etc/backups/ на сервере мониторинга с установленным пакетом etckeeper, который в свою очередь коммитит их в git репозиторий и отправляет красивое письмо и нотифай в Telegram об изменениях.
                • 0
                  Идея выбора бэкапа меня заинтересовала, кажется разумной.
                  Идея скрещивания функционала системы мониторинга с системами резервного копирование — не очень. Ну как-то это не то, я думаю, что какие-то действия при триггерах настроить через Zabbix(Nagios отрицаю) можно, к примеру как переключение на резервный канал, выключение интерфейса и.т.д., но очень странно слышать про бэкап.
                  • 0
                    Просто хостов за 500+ и нужно бекапить и мониторить.
                    Мониторинг по пингу включен на всех железках. Грех не использовать этот полный список.
                    Указываем в мониторинге тип железки, а в скрипте бекапа — смотреть на этот тип, ну и соответственно вызывать тот или иной алгоритм скачивания конфига. Что-бы не потерять что-то, проще вести единую базу.
                • 0

                  И чего только люди не придумают, чтобы не ставить Noc.

                  • 0
                    ну круто, да, комплексная управлялка.
                    А вы подумали, уместна ли она везде? Вот прям везде, где у вас только, к примеру, микротик, который туннели в дц поднимает, а остальное вас не интересует?
                    Такие вещи уместны более в достаточно серьёзных сетях. Тут рассматривается нечто иное.
                    • 0

                      Я соглашусь, что возможны ситуации когда админу наплевать на варианты типа "выполнить не удалось; повторить через час".
                      Бывает что им не нужна история или историю они делают через mkrtk_22.`date +%s`.txt


                      Бывает что потом когда на сети появляется еще один вендор эти админы разводят руками и говорят "ой мой скрипт перестал работать напишу еще один" и заканчивают с 15 вендорами и богатым кроном.


                      Все эти варианты вполне…

                      • 0
                        вполне имеют место быть, я думаю) Автору хотелось попрактиковаться и сделать полезную штуку, только и всего.
                        • 0

                          Верно. больше хороших штук. Только в проде не оставляйте как наиграетесь. Пожалейте приемника ему же это поддерживать, а он ни в чем не виноват…

                          • 0
                            как раз отдал людям в прод, не считаю глупостью или чем-то таким. У всех свои бюджеты, я просто помог людям задаром ради интереса.
                            А преемник, ну тут достаточно просто виртуалку выключить или cron-джобы убрать и всё(я думаю разберутся).
                            Это просто виртуалка, в которой специальный пользователь работает по ssh, и вообще ничего криминального не вижу в этом.Работает это и с CCR и CCS, и с более простыми моделям и вообще не обламывается.
                            По мне так лучше писать скрипты, чем не писать. Инфраструктуры переменчивы и я думаю, что это вполне практичный скрипт для простого бэкапа. Мне и людям достаточен более чем.
                            • 0

                              Я вот жалею человека, которому после меня достанется нок. Он просто сетевой админ и ни в чём не виноват. Скрипт бы он поправил, а вот с ноком...


                              Печально, что не видно вменяемых альтернатив.

                    • 0
                      Микроты же сами умеют сливать свои конфиги. Скрипт кладем на роутер, настраиваем шедулер.

                      Можно и через expect какой-нибудь, удобно если на железках разные пароли
                      что-то типа такого

                      #!/usr/bin/expect
                      set remote_server [lindex $argv 0]
                      set my_password { pass1 pass2 pass3}
                      spawn ssh user@$remote_server
                      expect {
                        "(yes/no)?*" {
                          send "yes\n"
                           }
                      }
                      set try 0
                      
                      set timeout 1
                      expect {
                          "*word:*" {
                          if { $try>3} {exit 1}
                          send [lindex $my_password $try]\r
                          incr try
                          exp_continue
                      }
                      }
                      set timeout 15
                      expect " > "
                      send "/export compact file=\"file.log\"\r"
                      expect " > "
                      send "/tool fetch address=\"IP_ADDR_FTP\" src-path=\"file.log.rsc\" user=\"config\" mode=ftp password=config dst-path=\"file.log.rsc\" upload=yes\r"
                      expect " > "
                      send "\n"
                      expect " > "
                      send "quit\r"
                      

                      • 0
                        кто ж спорит, умеет. Это ещё один коммент вида habrahabr.ru/post/342060/#comment_10517694
                        Никто не спорит, что это есть, в примерах это тоже есть и я об этом написал.
                        Так же я написал почему это не стал использовать и почему это централизованный бэкап. При любом изменении вы будете на каждом микротике всё перенастраивать? Для моего скрипта разницы нет, какие там пароли на устройстве, — он авторизуется по ключу.
                        К тому же надо сервер FTP держать, а зачем?
                        • 0
                          на устройстве у вас тогда будет лежать скрипт с паролем в обычном тексте, который видно даже от аккаунта readonly. А FTP должен быть открыт и принимать соединения от кого попало по сути.
                          В моём решении достаточно чтобы устройство было доступно по ssh для создания и копирования бэкапа. Ничего городить и открывать не надо и пароли нигде не светятся.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.