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

в 3:24, , рубрики: python

В прошлой статье я рассказал о возможном способе снимать централизованные резервные копии конфигураций с маршрутизаторов 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"
# keep backups for 4 weeks
backtime = datetime.timedelta(weeks=-4)
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 за дельный совет по организации массива с учетными данными.

Автор: diver66


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js