Oxidized - cистема управления конфигурациями сетевого оборудования
В этой статье мы через Docker Compose соберём единый стек, включающий:
- систему бэкапов конфигураций сетевого оборудования Oxidized
- хранение конфигураций в Git
- отдельный Nginx-proxy, который ограничивает доступ по IP
- единую Docker-сеть
- удобную структуру каталогов и конфигураций
Структура проекта #
/opt/docker/oxidized-stack/
├── app
│ ├── config
│ ├── logs
│ ├── oxidized-report-git-commits
│ ├── pid
│ ├── router.db
│ └── ASA.git
│ └── Routers.git
│ └── Switchs.git
├── docker-compose.yml
└── nginx
└── conf.d
└── nginx.conf
Объединяем Nginx-proxy и Oxidized в один стек через docker-compose.yml #
mkdir -p /opt/docker/oxidized-stack/app
mkdir -p /opt/docker/oxidized-stack/nginx/conf.d/
nano /opt/docker/oxidized-stack/docker-compose.yml
version: "3.9"
services:
nginx-proxy:
image: nginx:latest
container_name: nginx-proxy
restart: always
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
networks:
- nginx-proxy
oxidized:
image: oxidized/oxidized:latest
container_name: oxidized
restart: always
environment:
CONFIG_RELOAD_INTERVAL: 900
volumes:
- ./app:/home/oxidized/.config/oxidized/
networks:
- nginx-proxy
networks:
nginx-proxy:
driver: bridge
Конфигурация Nginx #
nano /opt/docker/oxidized-stack/nginx/conf.d/nginx.conf
server {
listen 80 default_server;
server_name _;
return 200 'Site under construction';
add_header Content-Type text/plain;
}
server {
listen 80;
server_name oxidiz.asterisker.com;
allow 192.168.77.0/24;
deny all;
location / {
proxy_pass http://oxidized:8888;
}
}
Конфигурация Oxidized #
nano /opt/docker/oxidized-stack/app/router.db
# https://github.com/ytti/oxidized/blob/master/docs/Supported-OS-Types.md
# MikroTik_Office:routeros:192.168.88.1:22:Routers:username:password
# Cisco_Office:routeros:192.168.88.1:22:Routers:username:password:enable_password
### MSK
# Routers
MSK_MTK_CCR2116:routeros:192.168.88.17:22:Routers # логин и пароль берется из config
MSK_Cisco_C4431:ios:192.168.88.22:22:Routers:oxidized:Passw@rd:enable_Passw@rd
# ASA
MSK_Cisco_ASA_1:asa:192.168.88.16:22:ASA:oxidized:Passw@rd:enable_Passw@rd
# Switchs
MSK_HPE_A2530_1:procurve:192.168.88.3:22:Switchs:admin:Passw@rd
nano /opt/docker/oxidized-stack/app/config
---
username: oxidized # логин по умолчанию
password: P<pDr7GFaM#wGTY@ # пароль по умолчанию
model: routeros # промт (модель) по умолчанию
resolve_dns: true
interval: 14400
use_syslog: false
debug: false
run_once: false
threads: 30
use_max_threads: false
timeout: 20
retries: 3
prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
rest: 0.0.0.0:8888
next_adds_job: false
vars: {}
groups: {}
group_map: {}
models: {}
pid: "/home/oxidized/.config/oxidized/pid"
crash:
directory: "/home/oxidized/.config/oxidized/crashes"
hostnames: false
stats:
history_size: 10
input:
default: ssh
debug: false
ssh:
secure: false
ftp:
passive: true
utf8_encoded: true
output:
default: git
git:
user: oxidized
email: admin@asterisker.com
repo: "/home/oxidized/.config/oxidized/devices.git"
source:
default: csv
csv:
file: "/home/oxidized/.config/oxidized/router.db"
delimiter: !ruby/regexp /:/
map:
name: 0
model: 1
ip: 2
port: 3
group: 4
username: 5
password: 6
vars_map:
enable: 7
gpg: false
model_map:
juniper: junos
cisco: ios
Запуск стека:
cd /opt/docker/oxidized-stack/
docker-compose up -d
docker-compose ps
docker-compose logs -f
Проверяем: http://oxidiz.asterisker.com
Настройка пользователей на сетевом оборудовании с правами чтения #
# MikroTik
/user add name="oxidized" password="Passw@rd" group=read
# Cisco IOS
username oxidized privilege 1 secret Passw@rd
# enable secret Passw@rd должен быть уже ранее установлен
# или чтобы был auto-enable (работает только на IOS):
# line vty 0 878
# privilege level 15
# Cisco ASA
username oxidized password Passw@rd privilege 1
#This is your first login. Please set a new password before proceeding.
#Enter old password: Passw@rd
#Enter new password: Passw@rd
#Confirm new password: Passw@rd
# enable password Passw@rd должен быть уже ранее установлен
# HPE
используем учетную запись admin с ролью manager
Настройка отправки на почту изменений (diff) конфигураций через hook + curl #
nano /opt/docker/oxidized-stack/app/oxidized-report-git-commits
#!/bin/sh
trap '/bin/rm -f "$tmpfile"' EXIT
tmpfile=$(mktemp) || exit 1
subject="Oxidized updates for ${OX_NODE_NAME}"
scriptname=`basename $0`
usage()
{
echo "Usage: ${scriptname} [-f] [ -s email_subject ] [ -r email_recipient ]"
exit 1
}
email_on_gitfail=1
while getopts "fs:r:" opt; do
case $opt in
s)
subject=$OPTARG
;;
r)
recipient=$OPTARG
;;
f)
email_on_gitfail=0
;;
*)
usage
;;
esac
done
if [ "${OX_EVENT}" = "node_fail" ]; then
echo "${scriptname}: ${OX_NODE_NAME}": 'Job failed'
exit 64
fi
if [ -z "${OX_REPO_COMMITREF}" ]; then
echo "${scriptname}: "'$OX_REPO_COMMITREF not set'
exit 64
fi
if [ -z "${OX_REPO_NAME}" ]; then
echo "${scriptname}: "'$OX_REPO_NAME not set'
exit 64
fi
cat > ${tmpfile} <<EOF
Node name: ${OX_NODE_NAME}
Group name: ${OX_NODE_GROUP}
Job status: ${OX_JOB_STATUS}
Job time: ${OX_JOB_TIME}
Git repo: ${OX_REPO_NAME}
Git commit ID: ${OX_REPO_COMMITREF}
EOF
# Проверка коммита ${OX_REPO_COMMITREF}
# Если коммит есть — продолжаем, если нет — пишем в тело письма, что коммит не найден
git --bare --git-dir="${OX_REPO_NAME}" rev-parse --quiet --verify "${OX_REPO_COMMITREF}" > /dev/null 2>&1
gitret=$?
# Показывает изменения (diff) коммита ${OX_REPO_COMMITREF}
# Результат записывается в ${tmpfile}, который потом идёт в тело письма
if [ ${gitret} -eq 0 ]; then
git --bare --git-dir="${OX_REPO_NAME}" show --pretty='' --no-color "${OX_REPO_COMMITREF}" >> ${tmpfile} 2>&1
else
echo "${scriptname}: commit ${OX_REPO_COMMITREF} does not exist" >> ${tmpfile}
fi
# Отправка письма на почту используя CURL
if [ ! -z "${recipient}" -a \( ${gitret} -eq 0 -o ${email_on_gitfail} -eq 1 \) ]; then
from="oxidized@domain.com"
smtp="smtp://mail.domain.com:25"
{
printf "From: %s\r\n" "$from"
printf "To: %s\r\n" "$recipient"
printf "Subject: %s\r\n" "$subject"
printf "Content-Type: text/plain; charset=utf-8\r\n"
printf "\r\n"
cat "$tmpfile"
printf "\r\n"
} | curl -s --url "$smtp" \
--mail-from "$from" \
--mail-rcpt "$recipient" \
--upload-file -
else
cat ${tmpfile}
fi
Проверка:
OX_NODE_NAME=test \
OX_NODE_GROUP=lab \
OX_JOB_STATUS=success \
OX_JOB_TIME=1 \
# Путь к Git-репозиторию, куда Oxidized сохранил конфиги
OX_REPO_NAME=/opt/docker/oxidized-stack/app/Routers.git \
# Текущий коммит, который нужно отправить в письме (возвращаеn SHA-1 коммита HEAD)
OX_REPO_COMMITREF=$(git --git-dir=/opt/docker/oxidized-stack/app/Routers.git rev-parse HEAD) \
# Событие Oxidized, которое вызвало hook: post_store (успешно сохранено), node_fail (узел не удалось обработать)
OX_EVENT=post_store \
/opt/docker/oxidized-stack/app/oxidized-report-git-commits -r support@domain.com
Настройка хука в oxidized:
# ...
model_map:
juniper: junos
cisco: ios
# Когда есть изменения в конфигурации оборудования, то срабывает хук и выполняется скрипт отправки на почту
hooks:
email_output:
type: exec
events: [post_store, node_fail]
cmd: '/home/oxidized/.config/oxidized/oxidized-report-git-commits -r support@domain.com'
async: true
timeout: 120
Опционально: работа с Git-репозиторием #
# Посмотреть последний коммит в текущей ветке (HEAD указывает на текущую ветку)
# находясь в самом репозитории
cd /opt/docker/oxidized-stack/app/Routers.git
git show
# или указав путь к репозиторию
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git show
# Посмотреть последние 5 коммитов (их SHA)
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git log -n 5
# Посмотреть конкретный коммит по SHA
### Показывает изменения этого коммита относительно родителя, обычно это последний коммит относительно предыдущего
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git show <SHA>
# Посмотреть изменения между последними двумя коммитами
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git diff
### или
### HEAD — последний коммит
### HEAD~1 — предпоследний
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git diff HEAD~1 HEAD
# Посмотреть diff между любыми двумя коммитами
### <SHA1> — старый коммит
### <SHA2> — новый коммит
### Показывает изменения между ними, независимо от того, идут ли они друг за другом
git --git-dir=/opt/docker/oxidized-stack/app/Routers.git diff <SHA1> <SHA2>