Поддержка подогретого бэкапа средствами двух MikroTik'ов

    В прошлой статье я рассказал о возможном способе снимать централизованные резервные копии конфигураций с маршрутизаторов MikroTik. Казалось бы на этом можно и успокоиться, но для некоторых филиалов руководство требует сиюминутного восстановления работы в случае выхода из строя любого участка сети. Первое, что приходит в голову — само собой, нужно иметь рядом две одинаковые железки. Однако вторая логичная мысль — использовать VRRP не подходит в силу ряда причин, одна из которых использование IPSec. Значит нужно снова шаманить костыль и синхронизировать конфигурацию в полуручном режиме, то есть скриптоваться.

    Логика


    1. Подключаемся по ssh к активному маршрутизатору и меняем адрес физического интерфейса, через который подключен подогретый маршрутизатор на 10.0.0.1/24
    2. Используя проброс порта фаерволом, подключаемся по ssh к подогретому маршрутизатору через активный
    3. Сверяем серийный номер, чтобы удостовериться что подключились на самом деле к подогретому маршрутизатору, дабы не угрохать активный
    4. Забираем бэкап по ftp с активного маршрутизатора и перезагружаем подогретый
    5. Подключаемся по ssh к активному маршрутизатору и возвращаем изначальный адрес физического интерфейса на 10.0.0.2/24, через который подключен подогретый маршрутизатор
    Манипуляции с изменением адреса мне нужны по той причине, что раз в день я снимаю бэкап со всех маршрутизаторов. Далее когда я буду заливать бэкап на подогретый маршрутизатор, мне до него необходимо будет как-то добраться, для этого у него уже будет настроен адрес 10.0.0.2/24, а на активном я временно выставлю адрес 10.0.0.1/24, этот IP в бэкапах никогда фигурировать не будет. Таким образом мы добиваемся идентичности конфигураций.

    Подключаем подогретую железку


    image

    … так, как показано на картинке, а именно — не суть важно как в вашем случае вы подключите интернет и локалку, важно лишь то, чтобы интерфейсы, которыми мы соединим активный и подогретый роутеры, были одинаковыми. Дальше будет понятно почему.

    Настроим интерфейсы идентичным образом — да да, ничего криминального в этом нет.

    /add address=10.0.0.2/24 comment=backup disabled=no interface=ether10 network=10.0.0.0

    Еще нам понадобится SSH на борту обоих железок:

    /set ssh address="" disabled=no port=2222

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

    Сделаем также проброс порта:

    /ip firewall nat add action=dst-nat chain=dstnat comment="dst-nat to knock-to hotbackup" disabled=no dst-port=2422 protocol=tcp to-addresses=10.0.0.2 to-ports=2222
    /ip firewall nat add action=src-nat chain=srcnat comment="to knock-to hotbackup we either need to change source address from ssh being connected" disabled=no dst-address=10.0.0.2 dst-port=2222 protocol=tcp to-addresses=10.0.0.1


    Именно так, вторым правилом меняя source address, ведь на резервном маршрутизаторе уже будет какой-то default gateway и он будет пытаться ответить через него, а нам надо в обратку, через активный маршрутизатор.

    Кроме этого нужен FTP, но мы его прикроем от посторонних:

    /set ftp address=10.0.0.0/24 disabled=no port=21

    На этом подготовительная работа закончена, рисуем скрипт.

    Скрипт


    Уже есть заготовленная болванка из предыдущего опыта, нужно лишь добавить некоторые штрихи:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    # for SSH
    import paramiko
    from paramiko import SSHClient
    from paramiko import AutoAddPolicy
    # for versioning
    import datetime
    # for file operations
    import os
    # for sleep
    import time
    # for strip()
    import string
    
    # versioning
    Version = datetime.date.today()
    print "\n" + str(Version)
    
    # credentials array
    arrCreds = (\
    ("R0", "11.22.33.44", "user0", "password0", "serial0"), \
    ("R1", "1.1.1.1", "user1", "password1", "serial1"), \
    ("R2", "2.2.2.2", "user2", "password2", "serial2"), \
    )
    # FTPD IP
    FtpdIP = "10.0.0.1/24"
    BackupIP = "10.0.0.2/24"
    sshCli = SSHClient()
    sshCli.set_missing_host_key_policy(AutoAddPolicy())
    LogFile = "/var/log/remotes/scripts/scripts1.log"
    paramiko.util.log_to_file(LogFile, level=10)
    
    print "header done"
    
    # loop adresses inside given network
    for (site, host, user, Password, SerialNumberA) in arrCreds:
        print datetime.datetime.now()
        print "\n" + host
        # define operations
        ChangeBackupIfAddr = '/ip address set [/ip address find address="' + BackupIP + '"] address="' + FtpdIP + '"'
        SSHToBackup = "/system ssh address=" + BackupIP.replace('/24', '') + " port=2222 user=" + user + "\n"
        GetSerial = ':put [system routerboard get value-name=serial-number]'
        GetBackupFromFtp = "/tool fetch address=" + FtpdIP.replace('/24', '') + " mode=ftp src-path=" + site + "_" + host + "_" + str(Version) + ".backup" + " user=" + user + " password=" + Password
        ApplyBackup = "/system backup load name=" + site + "_" + host + "_" + str(Version) + ".backup"
        ChangeBackupIfAddrA = '/ip address set [/ip address find address="' + FtpdIP + '"] address="' + BackupIP + '"'
        # try for not to fail the whole script on one error
        try:
            print "connecting to active router.." + site + "_" + host + "@"  + user + ":" + Password
            sshCli.connect(str(host), port=2222, username=str(user), password=str(Password))
            time.sleep(2)
            print "..done"
            print "changing backup interface IP address form 10.0.0.2/24 to 10.0.0.1/24.. " + ChangeBackupIfAddr
            sshCli.exec_command(ChangeBackupIfAddr)
            print "..done"
            print "connecting to hotbackup router.. "
            sshCli.connect(str(host), port=2422, username=str(user), password=str(Password))
            time.sleep(2)
            print "..done"
            # we need to check if we're not still on active router
            print "checking router serial number.. " + GetSerial
            stdin, stdout, stderr = sshCli.exec_command(GetSerial)
            type(stdin)
            SerialNumberCurrent = stdout.read()
            SerialNumberCurrent = SerialNumberCurrent.strip()
            print "SNA=" + SerialNumberCurrent
            # if SerialNumber == Active router SN, so we are still on active router and must stop script
            if SerialNumberCurrent == SerialNumberA:
                print "we are still on active device, aborting host processing"
                continue
            else:
                print "successfully connected to hotbackup device, going on futher host processing"
            print "downloading backup from active router ftp.. /" + GetBackupFromFtp
            sshCli.exec_command(GetBackupFromFtp)
            time.sleep(2)
            print "..backup downloaded from active router"
            print "apply backup on HotBackup.. /" + ApplyBackup
            sshCli.exec_command(ApplyBackup)
            # and say yes
            sshCli.exec_command("y")
            time.sleep(2)
            print "..done"
            print "connecting to active router.. "
            sshCli.connect(str(host), port=2222, username=str(user), password=str(Password))
            time.sleep(2)
            print "..done"
            print "giving backup interface address 10.0.0.2/24.."
            sshCli.exec_command(ChangeBackupIfAddrA)
            time.sleep(2)
            print "..done"
        except:
            print "Error connecting to host", host
    


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

    Минусы


    — схема работы такова, что будет маслать встроенную флешку при каждом выполнении, тем самым уменьшая её lifetime, поэтому я бы не советовал выполнять такую операцию чаще, чем один раз в неделю
    — способ будет работать только с железом одинаковой модели и одинаковой версии прошивки, в противном случае может сместиться нумерация интерфейсов
    — это тот еще костыль и нужно это понимать

    Респект хабраюзеру just_wow за дельный совет по организации массива с учетными данными.
    Поделиться публикацией

    Комментарии 1

      0
      Спасибо за статью. Напишете что-нибудь еще в подобном ключе?

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