Настройка Oxidized

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

Выделяем ПК или VM, устанавливаем Linux, я предпочитаю Debian (сейчас актуальная версия 12). Устанавливаем Docker и Docker-Compose по оф. инструкции Добавление репозиториев

apt update
apt upgrade
apt get install mc ncdu htop sudo curl
sudo apt -y install ca-certificates curl gnupg lsb-release apt-transport-https gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo “deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable” | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Установка Docker

sudo apt update
sudo apt -y install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
sudo systemctl enable docker

Установка Docker-Compose


sudo curl -L "https://github.com/docker/compose/releases/download/v2.28.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
echo PWD=${PWD} >> .env

Мне понравилась сборка Oxidized от Сергея Ершова

https://github.com/ErshovSergey/oxidized_docker-compose

mkdir /docker
git clone https://github.com/ErshovSergey/oxidized\\_docker-compose.git
cd oxidized_docker-compose
docker-compose up

Внутри Docker-Compose уже есть Nginx в качестве Reverse Proxy.

Настройка

Скопировать файлы

cp .env-default .env
cp router.db.csv-default  	router.db.csv
nano .env

Меняем имя нашего сервера и ip адрес

т.к. сервер бэкапов будет доступен только внутри локальной сети, указываем A запись внутри домена, через оснастку DNS на контроллере домена

Вернёмся к нашему Linux’у, локально на ПК (не в докере),переходим сd /path/to/data/folder/oxidized-docker редактируем наш router.db.csv

nano router.db.csv

Рассмотрим пример


Возможные модели
model_map[routeros|ios|junos|procurve|comware|datacom|vrp|airos|edgeswitch]
строка для конфига
name:ipaddr:model:username:password:enable_password:group_devices:ssh_port:ssh_proxy:ssh_proxy_port:ssh_key:ssh_no_exec
ssh_no_exec[true|false]
прямой доступ
name_host:ip_addr_host:routeros:ssh_user:ssh_user_pass:enable_pass:group_1:ssh_port
пример
name-router:11.22.33.44:routeros:ssh_read:PASS-ssh_read:empty:router:22
#Routers
MT_CCR1009_M2_Servers:10.10.0.1:routeros:oxidized:pass123:empty:router:4422

MT_CCR1009_M2_Servers - наименование 10.10.0.1 - ip адрес routeros - модель oxidized - логин pass123 - пароль empty - если не требуется привилегированный режим то оставляем так, но например у Cisco и Eltex это будет второй пароль router - группа 4422 - порт для SSH

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

Создание новой модели оборудования

Создаём папку для моделей

cd  /path/to/data/folder/oxidized-docker/ 
mkdir model

Создаём файл для нашей модели, в моём случае это будет Сisco Catalyst

nano my_ios.rb

Настройки будут приведены в спойлере ниже

class My_IOS < Oxidized::Model
  prompt /^([\w.@()-]+[#>]\s?)$/
  comment  '! '
cmd :all do |cfg|
# cfg.gsub! /\cH+\s{8}/, ‘’         # example how to handle pager
# cfg.gsub! /\cH+/, ‘’              # example how to handle pager
# get rid of errors for commands that don’t work on some devices
cfg.gsub! /^% Invalid input detected at ‘^’ marker./, ‘’
cfg.cut_both
end
cmd :secret do |cfg|
cfg.gsub! /^(snmp-server community)./, ‘\1 ’
cfg.gsub! /^(snmp-server host \S+( vrf \S+)?( informs?)?( version (1|2c|3 (noauth|auth|priv)))?)\s+\S+((\s+\S))\s/, ‘\1  \7’
cfg.gsub! /^(username .+ (password|secret) \d) .+/, ‘\1 ’
cfg.gsub! /^(enable (password|secret)( level \d+)? \d) .+/, ‘\1 ’
cfg.gsub! /^(\s+(?:password|secret)) (?:\d )?\S+/, ‘\1 ’
cfg.gsub! /^(.wpa-psk ascii \d) (\S+)/, ‘\1 ’
cfg.gsub! /^(.key 7) (\d.+)/, ‘\1 ’
cfg.gsub! /^(tacacs-server (.+ )?key) .+/, ‘\1 ’
cfg.gsub! /^(crypto isakmp key) (\S+) (.)/, ‘\1  \3’
cfg.gsub! /^(\s+ip ospf message-digest-key \d+ md5) .+/, ‘\1 ’
cfg.gsub! /^(\s+ip ospf authentication-key) .+/, ‘\1 ’
cfg.gsub! /^(\s+neighbor \S+ password) .+/, ‘\1 ’
cfg.gsub! /^(\s+vrrp \d+ authentication text) .+/, ‘\1 ’
cfg.gsub! /^(\s+standby \d+ authentication) .{1,8}/, ‘\1  \2’
cfg.gsub! /^(\s+key-string) .+/, ‘\1 ’
cfg.gsub! /^((tacacs|radius) server [^\n]+\n(\s+[^\n]+\n)\s+key) [^\n]+$/m, ‘\1 ’
cfg.gsub! /^(\s+ppp (chap|pap) password \d) .+/, ‘\1 ’
cfg
end
cmd ‘show version’ do |cfg|
comments = []
comments << cfg.lines.first
lines = cfg.lines
lines.each_with_index do |line, i|
slave = ‘’
slaveslot = ‘’
  if line =~ /^Slave in slot (\d+) is running/
    slave = " Slave:"
    slaveslot = ", slot #{Regexp.last_match(1)}"
  end

comments << “Image:#{slave} Compiled: #{Regexp.last_match(1)}” if line =~ /^Compiled (.*)$/
  comments << "Image:#{slave} Software: #{Regexp.last_match(1)}, #{Regexp.last_match(2)}" if line =~ /^(?:Cisco )?IOS .* Software,? \(([A-Za-z0-9_-]*)\), .*Version\s+(.*)$/

  comments << "ROM Bootstrap: #{Regexp.last_match(3)}" if line =~ /^ROM: (IOS \S+ )?(System )?Bootstrap.*(Version.*)$/

  comments << "BOOTFLASH: #{Regexp.last_match(1)}" if line =~ /^BOOTFLASH: .*(Version.*)$/

  comments << "Memory: nvram #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (non-volatile|NVRAM)/

  comments << "Memory: flash #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (flash memory|flash internal|processor board System flash|ATA CompactFlash)/i

  comments << "Memory: pcmcia #{Regexp.last_match(2)} #{Regexp.last_match(3)}#{Regexp.last_match(4)} #{Regexp.last_match(1)}" if line =~ /^(\d+[kK]) bytes of (Flash|ATA)?.*PCMCIA .*(slot|disk) ?(\d)/i

  if line =~ /(\S+(?:\sseries)?)\s+(?:\((\S+)\)\s+processor|\(revision[^)]+\)).*\s+with (\S+k) bytes/i
    sproc = Regexp.last_match(1)
    cpu = Regexp.last_match(2)
    mem = Regexp.last_match(3)
    cpuxtra = ''
    comments << "Chassis type:#{slave} #{sproc}"
    comments << "Memory:#{slave} main #{mem}"
    # check the next two lines for more CPU info
    comments << "Processor ID: #{Regexp.last_match(1)}" if cfg.lines[i + 1] =~ /processor board id (\S+)/i
    if cfg.lines[i + 2] =~ /(cpu at |processor: |#{cpu} processor,)/i
      # change implementation to impl and prepend comma
      cpuxtra = cfg.lines[i + 2].gsub(/implementation/, 'impl').gsub(/^/, ', ').chomp
    end
    comments << "CPU:#{slave} #{cpu}#{cpuxtra}#{slaveslot}"
  end

  comments << "Image: #{Regexp.last_match(1)}" if line =~ /^System image file is "([^"]*)"$/
end
comments << "\n"
comment comments.join "\n"

end
cmd ‘show vtp status’ do |cfg|
cfg.gsub! /^$\n/, ‘’
cfg.gsub! /Configuration last modified by.*\n/, ‘’
cfg.gsub! /^/, 'VTP: ’ unless cfg.empty?
comment “#{cfg}\n”
end
cmd ‘show inventory’ do |cfg|
comment cfg
end
cmd ‘show vlan brief’ do |cfg|
cfg.gsub! /^$\n/, ‘’
comment cfg
end
cmd ‘show running-config view full’ do |cfg|
cfg = cfg.each_line.to_a[3…-1]
cfg = cfg.reject { |line| line.match /^ntp clock-period / }.join
cfg = cfg.each_line.reject { |line| line.match /^! (Last|No) configuration change (at|since)./ unless line =~ /\d+\sby\s\S+$/ }.join
cfg.gsub! /^Current configuration : [^\n]\n/, ‘’
cfg.gsub! /^ tunnel mpls traffic-eng bandwidth[^\n]\n(
(?: [^\n]\n)*
tunnel mpls traffic-eng auto-bw)/mx, ‘\1’
cfg
end
cfg :telnet do
username /^Username:/i
password /^Password:/i
end
cfg :telnet, :ssh do
# preferred way to handle additional passwords
post_login do
if vars(:enable) == true
cmd “enable”
elsif vars(:enable)
cmd “enable”, /^[pP]assword:/
cmd vars(:enable)
end
end
post_login ‘terminal length 0’
post_login ‘terminal width 0’
pre_logout ‘exit’
end
end

После создания модели редактируем наш config по аналогии с рисунком ниже

Далее в router.db.csv добавляем запись

#Cisco 2960
Cisco2960_M2_Archive:10.10.0.10:my_ios:oxidized:P@ssw0rd-vDomene:empty:cisco_CLI:22

oxidized- это доменный пользователь

SSO (Прошивка и настройка Cisco Catalyst коммутаторов с привязкой к RADIUS NPS) SSO. Прошивка и настройка Extreme Networks x440, x450 коммутаторов с привязкой к RADIUS NPS
Изучив мои статьи, вам потребуется создать пользователя в домене с добавлением в группу доступа Switch-Read.

Проверка работоспособности Заходим в вебку (логин admin1, пароль admin) нажимаем reload и потом кнопку refrash напротив коммутатора Cisco2960_M2_Archive, нажав на “облачко” увидим загрузившуюся прошивку.

Все модели которые знает oxidized описаны на официальном сайте

Но вы поняли, что можно создавать и свои модели! ;)

P.S. Если не работает подключение по SSH, то добавляем в файле /etc/ssh/ssh_config

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
KexAlgorithms +diffie-hellman-group1-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1

Если в Вэб части не появляется новая модель,то переходим в папку /docker и выполняем

docker-compose restart

Далее прилагаю настройки моделей для Extreme, D-link, Cisco Nexus, Zyxel

Extreme Networks

nano /path/to/data/folder/oxidized-docker/model/my_xos.rb
class My_XOS < Oxidized::Model
  # Extreme Networks XOS
prompt /^\s?*?\s?[-\w]+\s?[-\w.~]+(:\d+)? [#>] $/
comment  '# ’
cmd :all do |cfg|
# xos inserts leading \r characters and other trailing white space.
# this deletes extraneous \r and trailing white space.
cfg.each_line.to_a[1…-2].map { |line| line.delete(“\r”).rstrip }.join(“\n”) + “\n”
end
cmd ‘show tech-support’ do |cfg|
comment cfg
end
cfg :telnet do
username /^login:/
password /^\r*password:/
end
cfg :telnet, :ssh do
post_login do
data = cmd ‘disable clipaging session’
match = data.match /^disable clipaging session\n\r?*?\s?[-\w]+\s?[-\w.~]+(:\d+)? [#>] $/m
next if match
  cmd 'disable clipaging'
end

pre_logout do
  send "exit\n"
  send "n\n"
end

end
end

D-link DES3200-26 rev.A1

nano /path/to/data/folder/oxidized-docker/model/my_dlink_3200_26_a1.rb
class My_Dlink_3200_26_A1 < Oxidized::Model
  # D-LINK Switches Dlink_DES-3200-26_A1
prompt /^(\r*[\w\s.@()/:-]+[#>]\s?)$/
comment '# ’
cmd :secret do |cfg|
cfg.gsub! /^(create snmp community) \S+/, ‘\1 ’
cfg.gsub! /^(create snmp group) \S+/, ‘\1 ’
cfg
end
cmd :all do |cfg|
cfg.each_line.to_a[2…-2].map { |line| line.delete(“\r”).rstrip }.join(“\n”) + “\n”
end
cmd ‘show switch’ do |cfg|
cfg.gsub! /^System Uptime\s.+/, ‘System Uptime              : “Value removed”’ # Omit constantly changing uptime info
comment cfg
end
cmd ‘show vlan’ do |cfg|
comment cfg
end
cmd ‘show config current_config’ do |cfg|
cfg.gsub! /^# ACCOUNT LIST\ncreate account( \w* \w*)\n(\w*)\n(\w*)/, “# User Account\ncreate account\1\ \n<< Value removed >>\n<< Value removed >>” #
cfg
end
cfg :telnet do
username /\r*([\w\s.@()/:-]+)?([Uu]ser[Nn]ame|[Ll]ogin):/
password /\r*[Pp]ass[Ww]ord:/
end
cfg :telnet, :ssh do
# preferred way to handle additional passwords
post_login do
if vars(:enable) == true
cmd “enable admin”
elsif vars(:enable)
cmd “enable admin” , /\r*([\w\s.@()/:-]+)?(PassWord):/i
cmd vars(:enable)
end
end
post_login ‘terminal length 0’
post_login ‘terminal clipading’
post_login 'disable clipaging'
post_login 'terminal length 0'
pre_logout 'logout'

end
end

D-link DES3200-26 rev.C1

nano /path/to/data/folder/oxidized-docker/model/my_dlink_3200_26_c1.rb
class My_Dlink_3200_26_C1 < Oxidized::Model
  # D-LINK Switches Dlink_DES-3200-26_C1
prompt /^(\r*[\w\s.@()/:-]+[#>]\s?)$/
comment '# ’
cmd :all do |cfg|
cfg.each_line.to_a[2…-2].map { |line| line.delete(“\r”).rstrip }.join(“\n”) + “\n”
end
cmd ‘show switch’ do |cfg|
cfg.gsub! /^System Uptime\s.+/, ‘System Uptime              : “Value removed”’ # Omit constantly changing uptime info
comment cfg
end
cmd ‘show vlan’ do |cfg|
comment cfg
end
cmd ‘show config current_config’ do |cfg|
cfg.gsub! /^# ACCOUNT LIST\ncreate account( \w* \w*)\n(\w*)\n(\w*)/, “# User Account\ncreate account\1\ \n<< Value removed >>\n<< Value removed >>” #
cfg
end
cfg :telnet do
username /\r*([\w\s.@()/:-]+)?([Uu]ser[Nn]ame|[Ll]ogin):/
password /\r*[Pp]ass[Ww]ord:/
end
cfg :telnet, :ssh do
# preferred way to handle additional passwords
post_login do
if vars(:enable) == true
cmd “enable admin”
elsif vars(:enable)
cmd “enable admin” , /\r*([\w\s.@()/:-]+)?(PassWord):/i
cmd vars(:enable)
end
end
post_login ‘terminal length 0’
post_login 'terminal clipading'
pre_logout 'logout'

end
end

Cisco Nexus

nano /path/to/data/folder/oxidized-docker/model/my_nxos.rb
class My_NXOS < Oxidized::Model
 prompt /^(\r?[\w.@_()-]+[#]\s?)$/
  comment '! '
def filter(cfg)
cfg.gsub! /\r\n?/, “\n”
cfg.gsub! prompt, ‘’
end
cmd :secret do |cfg|
cfg.gsub! /^(snmp-server community)./, ‘\1 ’
cfg.gsub! /^(snmp-server user (\S+) (\S+) auth (\S+)) (\S+) (priv) (\S+)/, '\1  ’
cfg.gsub! /(password \d+) (\S+)/, ‘\1 ’
cfg.gsub! /^(radius-server key)./, ‘\1 ’
cfg.gsub! /^(tacacs-server host .+ key(?: \d+)?) \S+/, ‘\1 ’
cfg
end
cmd ‘show version’ do |cfg|
cfg = filter cfg
cfg = cfg.each_line.take_while { |line| not line.match(/uptime/i) }
comment cfg.join “”
end
cmd ‘show inventory’ do |cfg|
cfg = filter cfg
comment cfg
end
cmd ‘show vlan brief’ do |cfg|
cfg = filter cfg
comment cfg
end
cmd ‘show running-config all’ do |cfg|
cfg = filter cfg
cfg.gsub! /^(show run.*)/, ‘’
cfg
end
cfg :ssh, :telnet do
post_login ‘terminal length 0’
pre_logout ‘exit’
end
cfg :telnet do
username /^login:/
password /^Password:/
end
end

Zyxel ES2108

nano /path/to/data/folder/oxidized-docker/model/my_zyos.rb
class My_ZyOS < Oxidized::Model
  prompt /^([\w.@()-]+[#>]\s\e7)$/
  comment  ';; '
cmd :all do |cfg|
cfg.gsub! /^.*\e7/,‘’
end
cmd ‘show version’
cmd ‘show running-config’
cmd ‘show aaa authentication’
cfg :ssh do
username /^User name:/i
password /^Password:/i
end
cfg :ssh do
if vars :enable
post_login do
send “enable\n”
# Interpret enable: true as meaning we won’t be prompted for a password
unless vars(:enable).is_a? TrueClass
expect /[pP]assword:\s?$/
send vars(:enable) + “\n”
end
expect /^.+[#]$/
  end
end
pre_logout 'exit'

end
end

Если вы заметили что у Сергея Ершова на github так же имеются свои наработки по моделям D-link, располагаются они по пути /docker/oxidized_docker-compose/new-devices. Пользоваться ими возможно, после копирования.

cp /docker/oxidized_docker-compose/new-devices/* /path/to/data/folder/oxidized-docker/model

Список используемых источников:
https://github.com/ytti/oxidized https://docs.docker.com/compose/install/standalone/ https://computingforgeeks.com/install-docker-and-docker-compose-on-debian/ https://github.com/ytti/oxidized/tree/master/lib/oxidized/model https://github.com/ErshovSergey/oxidized_docker-compose https://www.ekzorchik.ru/2019/03/backing-up-mikrotik-via-oxidized/