Перейти к основному содержимому

Развёртывание Oxidized + Nginx-proxy

·957 слов·5 минут
DevOps • Networks • Security • Infrastructure
Автор
DevOps • Networks • Security • Infrastructure
DevOps/Network/Infra Engineer, CyberSecurity Expert

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

Интерфейс Oxidized

Настройка пользователей на сетевом оборудовании с правами чтения
#

# 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>

Related

Как устроен IPSec VPN
·768 слов·4 минут
Тестирование скорости сети с помощью iperf
·225 слов·2 минут
Как обновить прошивку на MikroTik
·425 слов·2 минут