Quando um switch avaria numa sexta-feira e a substituição só chega na segunda, o novo equipamento chega vazio: a última configuração funcional estava no aparelho avariado e ninguém a guardou. O cenário é comum em PMEs — a configuração dos equipamentos de rede não entra no backup dos ficheiros, das bases de dados nem das VMs. O Oxidized resolve exactamente isto: um serviço que liga por SSH (ou telnet) a cada switch e router, guarda a configuração numa pasta e faz um commit num repositório Git a cada alteração — com data, autor do commit e diff linha a linha entre versões.

Suporta mais de 130 famílias de sistemas de rede (MikroTik RouterOS, Cisco IOS, Aruba, TP-Link, HP ProCurve, Fortinet, entre os mais comuns em PME), corre em Debian ou Ubuntu num VPS ou no LXC do Proxmox, e integra-se com o LibreNMS já monitorizado aqui no site — a partir desse momento, a configuração de cada equipamento fica visível na própria página do device.

⚠ Antes de produção: o serviço faz login em todos os equipamentos de rede — quem tiver essas credenciais tem a rede inteira nas mãos. Corre com utilizador próprio (nunca root), fica restrito à rede interna, e o repositório Git com as configurações deve ficar fora do acesso anónimo.

Instalação no servidor Debian/Ubuntu

O Oxidized é uma gem de Ruby e pede Debian 12 ou Ubuntu 22.04 ou mais recentes. No Ubuntu, começa por activar o repositório universe (a libssh2-1-dev está nesse repositório), instalar as dependências de compilação e a gem:

add-apt-repository universe
apt install ruby ruby-dev libsqlite3-dev libssl-dev pkg-config cmake \
  libssh2-1-dev libicu-dev zlib1g-dev g++ libyaml-dev libzstd-dev
gem install oxidized

A gem opcional oxidized-web acrescenta a interface web e a REST API (que o LibreNMS usa) — instala com gem install oxidized-web. O serviço corre com um utilizador próprio: o daemon não corre como root, e faz logins de rede — com useradd -s /bin/bash -m oxidized e su - oxidized antes de o arrancares.

Primeiro arranque e router.db

Na primeira execução, o Oxidized cria a configuração inicial em ~/.config/oxidized/config — basta correr oxidized uma vez e interromper. Um exemplo de configuração, com o intervalo de recolha, a origem dos equipamentos (source) e o destino das configurações (output):

username: backup
password: SenhaMuitoForte2026!
interval: 3600
log: ~/.config/oxidized/log
debug: false
threads: 30
timeout: 20
timelimit: 300

source:
  default: csv
  csv:
    file: ~/.config/oxidized/router.db
    delimiter: !ruby/regexp /:/
    map:
      name: 0
      model: 1

output:
  default: git
  git:
    user: Oxidized
    email: [email protected]
    repo: ~/.config/oxidized/backups.git

O router.db é o inventário no formato rancid — uma linha por equipamento com hostname:modelo e o delimiter do exemplo oficial (:). Credenciais por linha entram com o mapeamento completo da documentação (hostname:model:username:password e vars_map), e os grupos por zona entram no map ou nas credenciais do bloco groups::

# hostname:modelo (formato rancid; o delimiter do exemplo oficial é :)
sw-core:routeros
rt-filial:routeros
sw-cctv:procurve

A referência completa lista os campos de mapeamento e os grupos — os grupos valem ouro quando as credenciais diferem por zona (loja, filiais, CCTV). Para quem já mantém o inventário noutro sítio, o source pode ser SQLite, MySQL ou um URL HTTP — é aqui que a integração com o LibreNMS se encaixa mais abaixo.

Git: histórico e diff por alteração

O output git é o recomendado: cada versão da configuração é um commit no repositório (backups.git no exemplo), e os comandos do Git passam a ser a ferramenta de auditoria:

git -C ~/.config/oxidized/backups.git log --oneline  # últimas alterações
git -C ~/.config/oxidized/backups.git diff HEAD~1 HEAD # o que mudou

Como bónus, o README documenta um exemplo de syslog UDP+ficheiro para IOS/JunOS que apanha os eventos de mudança de config e regista o autor da alteração no commit — um git blame em qualquer linha revela o utilizador e a data. O fluxo está documentado para IOS/JunOS: para modelos como o MikroTik, o histórico Git mostra quando as linhas mudaram, não quem as mexeu.

A interface web de consulta rápida

A gem oxidized-web abre a porta 8888 e expõe a lista de equipamentos com o estado da última recolha, a configuração completa de cada um, e as versões guardadas com diff entre elas. A REST API do mesmo serviço é o que o LibreNMS e scripts externos consomem: /nodes (inventário), /node/fetch/[NODE] (config), /node/version (versões) — e /node/next/[NODE] coloca um equipamento à cabeça da fila (a recolha é imediata com next_adds_job: true; sem ele, corre quando um worker fica livre).

A sintaxe actual usa o bloco extensions.oxidized-web: load é false por omissão (tem de ser true) e listen é 127.0.0.1 por omissão — a API fica em localhost, o que se quer quando quem consulta é só o LibreNMS na mesma máquina. A sintaxe antiga rest: está obsoleta (emite aviso e será removida), a extensão pede oxidized-web ≥ 0.16.0 e, se rest: estiver na configuração, o bloco novo é ignorado:

extensions:
  oxidized-web:
    load: true
    listen: 127.0.0.1
    port: 8888
vars: {}

A integração no LibreNMS

Com o LibreNMS no mesmo host (ou acessível por HTTP), dois passos ligam os dois. Primeiro, apontas o LibreNMS ao serviço e ligar a apresentação da config na página de cada device:

lnms config:set oxidized.enabled true
lnms config:set oxidized.url http://127.0.0.1:8888
lnms config:set oxidized.features.versioning true
lnms config:set oxidized.group_support true

Segundo, opcional mas recomendado: usar o próprio LibreNMS como inventário do Oxidized — o source HTTP do Oxidized lê a lista de devices directamente da API do LibreNMS (/api/v0/oxidized, com token de API), e a opção oxidized.reload_nodes true faz o LibreNMS reenviar a lista sempre que entra um device novo. Neste modo, o router.db deixa de ser mantido à mão. O LibreNMS não fornece credenciais — o inventário chega com hostname e grupo; o group_support faz o grupo do device no LibreNMS escolher o grupo do Oxidized, onde as credenciais ficam no bloco groups:.

source:
  default: http
  http:
    url: https://librenms.empresa.pt/api/v0/oxidized
    map:
      name: hostname
      model: os
      group: group
    headers:
      X-Auth-Token: '0TOKEN_LONGO_DA_API'

Numa instalação com SELinux (RHEL/Alma/Rocky), a integração falha em silêncio sem setsebool -P httpd_can_network_connect 1. Com tudo activo, a página de cada switch no LibreNMS passa a ter o separador Config com a última configuração e o histórico — o diff num clique, sem consola.

Intervalo, eventos e hooks

O interval: 3600 define a recolha de hora em hora, com o serviço a ajustar automaticamente as threads para caber no prazo. O evento valioso é o contrário da recolha por agendamento: com o syslog do router ligado ao Oxidized, a mudança de config dispara a recolha. O fluxo documentado no README é para IOS/JunOS; no LibreNMS, o script syslog-notify-oxidized.php é quem chama /node/next no evento de syslog. Duas condições: next_adds_job: true na configuração (senão o nó só entra à cabeça da fila) e um listener a fazer a chamada. Com isso, a config muda às 14h32 e o backup entra de seguida, com commit e diff. Os hooks (exec no evento pós-store, GitHub repo, Slack Diff, XMPP) cobrem a notificação da alteração:

# hooks: notificar o canal Slack da equipa a cada alteração de config
# requer: gem install slack-ruby-client
hooks:
  slack:
    type: slackdiff
    events: [post_store]
    token: SLACK_BOT_TOKEN
    channel: "C0123456789"

O hook usa um token de bot de Slack e o ID do canal (não um webhook e não o nome #redes) — os valores vêm do Hooks.md. Opcional: diff: false com message: para publicar um link ao commit em vez do snippet.

Onde guardar o repositório

O repositório Git com as configurações é um backup crítico — contém o estado de todo o parque de rede. Entra no ciclo de backup do servidor (o guia de backups do site cobre o resto), e a cópia de backups.git para um segundo destino (NAS, cloud) fecha o ciclo: o servidor perde-se, as configs sobrevivem num git clone.

Erros comuns

  • Unknown model — o modelo no router.db não corresponde ao nome certo (ex. mikrotik em vez de routeros). A página Supported OS Types do projecto lista os nomes válidos.
  • Device a falhar sempre — o timeout (20 s por omissão) limita cada operação e o timelimit (300 s, por omissão) o total do job; para equipamentos lentos, aumenta-os na configuração, e um debug: true temporário mostra a fase exacta da falha.
  • Login failed — credenciais variam por zona: blocos groups: na config com username/password por grupo.
  • LibreNMS sem separador Config — com SELinux activo, httpd_can_network_connect é o primeiro suspeito (a falha é silenciosa).
  • oxidized-web sem API — a extensão não arranca por omissão: falta load: true no bloco extensions.oxidized-web, ou a gem não está no ambiente do utilizador do serviço (verifica com gem list oxidized-web nesse utilizador).

Veredicto

A partir de meia dúzia de equipamentos, o backup de rede deixa de ser luxo — é o que separa um restauro de dez minutos de um fim de semana a refazer configurações. O Oxidized é leve (gem Ruby, SQLite e Git — dimensiona consoante o tamanho do parque), e o Git dá auditoria sem custo, e a ligação ao LibreNMS põe a configuração no painel que a equipa já abre. O projecto, embora activo e amplamente usado em produção, tem o maintainer principal a pedir reforço — é um factor a saber, e o argumento extra para o backups.git ter cópia fora do servidor.

Artigos Relacionados

Fontes Oficiais