Arquitetura interna, modos de operação, replicação DR Veeam-style, instant recovery via NBD, conversão cross-hypervisor (vSphere/Hyper-V → PVE), e modelo de segurança com fingerprint pinning TLS.
Documento técnico complementar à página do plugin PodHeitor Proxmox.
1. Problema: Bacula stock não enxerga Proxmox VE
Bacula Community em sua forma stock não tem awareness algum de hipervisores. Backup de VMs Proxmox sem plugin é tipicamente uma das três opções, todas ruins:
- Backup do filesystem do host — captura arquivos
.qcow2/.rawem estado inconsistente, sem quiesce, sem CBT. Restauração frequentemente boota em corrupção. vzdump+ dump em diretório, depois Bacula — duplica o storage (1× dataset PVE + 1× dumpvzdump), e cada incremental retransmite o dump inteiro porquevzdumpnão emite delta nativamente.- Bacula Enterprise PVE plugin — existe, mas custa preço enterprise e não suporta replicação DR cross-site nem conversão cross-hypervisor.
O PodHeitor Proxmox Plugin endereça as três lacunas em um único binário: backup VM-aware com CBT, replicação DR Veeam-style entre nós PVE, e restore cross-hypervisor (vSphere/Hyper-V → PVE).
2. Modelo arquitetural
O plugin segue o padrão PodHeitor de cdylib + backend standalone, comunicando-se via PTCOMM (length-tagged framing em stdin/stdout). A motivação é tripla:
- Crash isolation. Um panic no engine de NBD ou QMP mata o backend, não o
bacula-fd. O cdylib observa EOF na pipe, reporta o job como falhado, e o FD continua atendendo outros jobs. - Liberdade de paralelismo. O backend pode abrir conexões PVE REST + NBD + QMP em paralelo sem violar o contrato Bacula de “uma thread por
bpContext“. - License firewall. Desde v2.0.0, nenhum source AGPLv3 do Bacula é vinculado estaticamente.
2.1 Topologia de processos
bacula-fd → podheitor-proxmox-fd.so (cdylib ~600 LoC) → podheitor-proxmox-backend (Rust ~4500 LoC)
├─ PVE REST API (HTTPS/TLS pinned)
├─ NBD Client (disk I/O)
└─ QMP Client (snapshot + dirty bitmap)
O backend hospeda cinco engines: BackupEngine, RestoreEngine, ReplicationSender, ReplicationReceiver e InstantRecoveryEngine. Selecionado pelo parâmetro mode= do plugin string.
3. Modos de operação
| Mode | Função | Engine |
|---|---|---|
backup |
Backup VM-aware (Full/Inc/Diff) com CBT via QEMU dirty bitmaps | BackupEngine |
seed |
Sync inicial completa, auto-provisiona VM réplica no DR | ReplicationSender |
incremental (DR) |
Replicação CBT incremental — dirty-bitmap deltas only | ReplicationSender |
receiver |
Daemon receiver no DR target — escuta TCP dr_port (9190) |
ReplicationReceiver |
failover-exec |
Boot da réplica no DR (planned failover) | ReplicationSender |
failback-pre |
Retorno da réplica para standby | ReplicationSender |
4. CBT via QEMU dirty bitmaps
Em vez de re-enviar 100 GB toda noite, o plugin instala um dirty bitmap persistente no QEMU do PVE node via QMP block-dirty-bitmap-add. Cada incremental:
- Tira snapshot consistente (com
quiesce=yesvia QEMU Guest Agent quando disponível). - Lê apenas blocos marcados dirty desde o último backup, via NBD
BLOCK_STATUS. - Stream-a esses blocos pela PTCOMM com offset preservado.
- Reseta o bitmap após confirmar que o backup terminou OK.
VM de 100 GB com 2 GB modificados → apenas 2 GB transferidos. Sem CBT (Bacula stock + filesystem), seriam 100 GB cada noite.
5. Replicação DR Veeam-style (v1.1.0)
O plugin implementa um pipeline DR cross-node PVE-1 → PVE-2 com semântica próxima a Veeam Replication, mas dirigido inteiramente pelo Bacula Director (FileSet/Job).
5.1 Fases
- Seed (
mode=seed): sync inicial full. Auto-provisiona a VM réplica no DR target (cores, RAM, NICs, SCSI controllers, storage spec). - Incremental contínuo (
mode=incremental): apenas deltas dirty-bitmap. - Restore-points: snapshots auto-rotacionados na réplica (default 7 pontos).
- Verify (
verify_sample_blocks=N): hash FNV-1a-64 de N blocos amostrais, comparado source ↔ DR. Mismatch = job fail (não silenciosamente OK). - Planned failover (
mode=failover-exec): uma execução boot-a a réplica no DR. - Planned failback (
mode=failback-pre): retorna réplica a standby.
5.2 Autenticação do canal DR
| Método | Parâmetro | Quando usar |
|---|---|---|
| Token PSK (HMAC) | dr_auth_token |
Default; setup rápido entre dois sites controlados |
| TLS Mutual Auth | dr_auth_cert + dr_auth_key |
Compliance / multi-tenant; rustls + PEM |
| Ambos | todos os 3 | Defense-in-depth |
5.3 Daemon receiver standalone
O DR target não precisa rodar bacula-fd. O package instala o template systemd [email protected]; o receiver fica escutando em dr_port (default 9190) e aceita streams autenticados, escrevendo discos via NBD para o storage dr_storage do PVE local. Isso reduz superfície de ataque no DR e simplifica air-gap parcial.
6. Instant Recovery via NBD overlay
Recuperação tradicional de uma VM de 500 GB pode levar horas para escrever bytes em disco antes que o serviço volte. O InstantRecoveryEngine contorna isso:
- Boot-a a VM no PVE apontando para um disco virtual servido por NBD pelo backend, lendo direto do stream Bacula restore.
- Overlay writes (
ir_overlay_storage) capturam mudanças do guest em storage local rápido. - Em background, com
ir_auto_migrate=yes, o disco é migrado para storage final (ir_target_storage) sem downtime.
RTO típico cai de horas para minutos. Parâmetros chave: ir_nbd_bind, ir_nbd_port, ir_overlay_storage, ir_target_storage, ir_timeout (default 3600s).
7. Conversão cross-hypervisor
O plugin lê backups produzidos pelos plugins-irmãos PodHeitor vSphere e PodHeitor Hyper-V e restaura diretamente em PVE — sem reconversão manual:
| Origem | Formato disco origem | Conversão |
|---|---|---|
| VMware vSphere | VMDK | VMDK → qcow2/raw via biblioteca interna (não shell-out) |
| Hyper-V | VHDX | VHDX → qcow2/raw via biblioteca interna |
Validado em laboratório: Job 805 (Hyper-V → PVE) e Job 865 (VMware → PVE) restauraram VMs com boot bem-sucedido no PVE de destino.
8. Modelo de segurança
8.1 TLS Fingerprint Pinning (PVE API)
Bacula stock tipicamente confia na trust store do sistema; PVE certs são autossignados por padrão. O plugin enforça SHA-256 fingerprint pinning explícito via pve_fingerprint=AA:BB:CC:..., com pve_insecure=no como default. Para obter o fingerprint:
openssl s_client -connect pve-host:8006 </dev/null 2>/dev/null
| openssl x509 -noout -fingerprint -sha256
| sed 's/SHA256 Fingerprint=//'
Mismatch (cert rotacionado, MITM, host trocado) → job aborta com ERROR: TLS fingerprint mismatch. Expected AA:BB:... got CC:DD:....
8.2 Credenciais
- Senhas e tokens passados via FileSet plugin string (não armazenados em disco pelo plugin).
bacula-dir.confdeve ter perms600, ownerbacula.- Para produção: integrar com vault externo (HashiCorp Vault, AWS Secrets Manager) e gerar plugin string via templating do Director.
9. Validação em laboratório
| Métrica | Resultado |
|---|---|
| Bacula Jobs executados (sequencial) | 1.290+ JobIDs |
| Backup/Restore Full/Inc/Diff (same-host + cross-host) | OK |
| Cross-hypervisor Hyper-V → PVE (Job 805) | OK |
| Cross-hypervisor VMware → PVE (Job 865) | OK |
| Replication seed 100 GB | 107 GB em 93,7 min, 19 MB/s sustained |
| Replication incrementais | 15 / 15 back-to-back, média 14,5 s |
| Integrity verify | 150 sample blocks, 0 mismatches |
| mTLS DR channel | Cert v3 com IP SAN — handshake + integrity OK |
| Planned failover + failback | One-command, exit 0 |
| Bacula-driven JobId 3448 | Termination=OK |
Ambiente: Director Oracle Linux 9.6 + Bacula 15.0.3; PVE Site A Debian 12 + PVE 8.4.18; PVE Site B Debian 13 + PVE 9.x.
10. Anti-patterns documentados
- Não desabilite
pve_fingerprintem produção. Setarpve_insecure=yesaceita qualquer cert apresentado pelo PVE host — incluindo certs MITM. Use somente em lab. - Não rode
quiesce=yessem QEMU Guest Agent instalado e ativo na VM. Sem agent, o plugin cai para crash-consistent automaticamente, mas o operador deve saber que isso aconteceu (verifique log do job). - Não rode receiver e sender no mesmo host PVE. Eles abrem o mesmo
dr_porte o segundo binda em fail. - Não confunda
backup_type=incremental(Bacula level) commode=incremental(DR mode). O primeiro é nível de backup do FileSet; o segundo é o modo do engine de replicação. São ortogonais.
11. License posture
Desde v2.0.0, o plugin distribui sob LicenseRef-PodHeitor-Proprietary. Nenhum source AGPLv3 do Bacula é vinculado estaticamente ao .so. O cdylib é construído sobre o crate puro PodHeitor plugin-proxmox no workspace PodHeitor Rust cdylib, com bindings extern "C" independentes via crate bacula-fd-abi.
Pronto para avaliar?
Trial gratuito de 30 dias para frotas Proxmox VE em produção. Garantimos no mínimo 50% de desconto vs Bacula Enterprise, Veeam ou Commvault, com mais funcionalidades inclusas (replicação DR, instant recovery, conversão cross-hypervisor).
Heitor Faria — Fundador, PodHeitor International
✉ [email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 Página do plugin PodHeitor Proxmox
Disponível em:
Português
English (Inglês)
Español (Espanhol)