Skip to content

Implantação de Zabbix Proxy 7.0 (PostgreSQL, TLS PSK) em LXC sob Proxmox

Todos os nomes de host, IPs, senhas e identificadores neste documento são fictícios. Substitua pelos valores reais do ambiente antes de aplicar qualquer comando.


1. Contexto

Este documento descreve a implantação de um Zabbix Proxy 7.0, incluindo banco PostgreSQL, criptografia TLS PSK entre proxy e server e hardening de firewall com nftables.

O proxy sobe em um container LXC no Proxmox, com Debian 13 (Trixie) como sistema operacional. Esse ponto importa porque o confinamento aplicado pelo LXC sob Proxmox pode restringir a criação de mount namespace por alguns serviços systemd, independentemente de o container ser privileged ou unprivileged, detalhado na seção 4.

O proxy roda em modo ativo: ele inicia a conexão com o Zabbix server, nunca o contrário. Isso evita expor qualquer porta de entrada no proxy voltada para a internet, o que é preferível quando o proxy fica atrás de NAT ou numa rede sem IP público dedicado.

Topologia de referência:

  • Zabbix server: mon.exemplo.com.br (203.0.113.10), hospedado em nuvem
  • Zabbix proxy: ZBXPROXY-EXEMPLO-HQ (10.10.9.252), container LXC (Debian 13 Trixie) no Proxmox
  • Escritório administrativo: 198.51.100.20

2. Instalação do Zabbix Proxy

Referência oficial: Zabbix Documentation 7.0 — Install e DB scripts.

2.1. Repositório e pacotes

wget https://repo.zabbix.com/zabbix/7.0/debian-arm64/pool/main/z/zabbix-release/zabbix-release_latest_7.0+debian13_all.deb
dpkg -i zabbix-release_latest_7.0+debian13_all.deb
apt update
apt install zabbix-proxy-pgsql zabbix-sql-scripts

Ajuste o caminho do pacote (debian-arm64 neste exemplo) para a arquitetura real do container. Instale também o servidor PostgreSQL, caso ainda não esteja presente no ambiente:

apt install postgresql

2.2. Banco de dados

Conforme a documentação oficial, crie o usuário e o banco no host de banco de dados:

sudo -u postgres createuser --pwprompt zabbix
sudo -u postgres createdb -O zabbix zabbix_proxy

Em containers LXC sem o pacote sudo instalado, substitua por:

su - postgres -c "createuser --pwprompt zabbix"
su - postgres -c "createdb -O zabbix zabbix_proxy"

Importe o schema inicial:

cat /usr/share/zabbix-sql-scripts/postgresql/proxy.sql | sudo -u zabbix psql zabbix_proxy

Duas observações práticas que a documentação oficial não cobre, mas que aparecem com frequência:

  • Se o banco for criado sem especificar encoding, o PostgreSQL herda o padrão do sistema, às vezes SQL_ASCII. O Zabbix rejeita esse encoding na inicialização. Force UTF8 na criação:
sudo -u postgres createdb -O zabbix -E UTF8 -T template0 zabbix_proxy
  • Se o import do schema for feito por um usuário diferente do dono do banco (por exemplo, via postgres em vez de zabbix, comum quando sudo -u zabbix falha por o usuário de sistema zabbix não ter shell de login), as tabelas ficam com o dono errado e o proxy falha na inicialização com permission denied for table users. Corrija concedendo privilégios explicitamente:
su - postgres -c "psql -d zabbix_proxy -c 'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO zabbix;'"
su - postgres -c "psql -d zabbix_proxy -c 'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO zabbix;'"

2.3. Configuração principal

/etc/zabbix/zabbix_proxy.conf:

Server=mon.exemplo.com.br
Hostname=ZBXPROXY-EXEMPLO-HQ
ProxyMode=0
DBName=zabbix_proxy
DBUser=zabbix
DBPassword=<senha_do_banco>

ProxyMode=0 define o modo ativo. Hostname precisa ser idêntico, caractere por caractere, ao Proxy name cadastrado no server em Administration > Proxies. Qualquer divergência gera o erro recorrente proxy "X" not found no log.

2.4. Iniciar o serviço

systemctl restart zabbix-proxy
systemctl enable zabbix-proxy

3. Criptografia TLS PSK

Duas relações de confiança distintas existem nesse desenho, cada uma com sua própria chave. Não reaproveite arquivos entre elas: fazer isso quebra a autenticação de um lado sempre que o outro precisar rotacionar a chave.

Relação Arquivo de chave Configurado em
Proxy ↔ Server /etc/zabbix/proxy_psk.key zabbix_proxy.conf
Agente local ↔ Proxy /etc/zabbix/agent_psk.key zabbix_agent2.conf

3.1. Gerar a chave proxy-server

openssl rand -hex 32 > /etc/zabbix/proxy_psk.key
chown zabbix:zabbix /etc/zabbix/proxy_psk.key
chmod 400 /etc/zabbix/proxy_psk.key

Adicione ao zabbix_proxy.conf:

TLSConnect=psk
TLSAccept=psk
TLSPSKIdentity=ZBXPROXY-EXEMPLO-HQ
TLSPSKFile=/etc/zabbix/proxy_psk.key

3.2. Cadastro no server

Em Administration > Proxies, crie o proxy com:

  • Proxy name: ZBXPROXY-EXEMPLO-HQ
  • Proxy mode: Active
  • Address for active agents: hostname ou IP do próprio proxy (campo obrigatório mesmo em modo ativo, usado para descoberta de agentes ativos)
  • Aba Encryption: marque Connections from proxy como PSK, informe a mesma identity e cole o valor da chave gerada

O campo Connections to proxy fica desabilitado por design em modo ativo, já que o server nunca inicia conexão com o proxy nesse cenário.

3.3. Validação

systemctl restart zabbix-proxy
tail -f /var/log/zabbix/zabbix_proxy.log

A linha received configuration data from server confirma que o handshake TLS e a sincronização funcionaram. Erros comuns e suas causas:

  • connection of type "unencrypted" is not allowed — normalmente indica que TLSPSKFile está apontando para o arquivo de chave errado (mistura entre a chave do proxy e a do agente local)
  • cannot find requested PSK identity — a identity configurada localmente não bate com a cadastrada no server, ou o proxy ainda não sincronizou a config atualizada

4. Firewall (nftables)

O nftables.service padrão do Debian pode falhar dentro de um container LXC sob Proxmox mesmo com o container configurado como privileged. O sintoma é Failed to set up mount namespacing: Permission denied, código de saída 226/NAMESPACE, causado pelas diretivas ProtectSystem e ProtectHome do unit, que exigem criação de mount namespace. O confinamento aplicado pelo próprio LXC (perfil AppArmor do container) pode bloquear essa operação independentemente de o container ser privileged ou unprivileged.

Uma tentativa comum é neutralizar essas diretivas via override (systemctl edit nftables), mas atenção à sintaxe: valores como ProtectSystem= vazio geram Failed to parse ProtectSystem=, ignoring: Invalid argument no log, e o systemd ignora o override, mantendo a diretiva original ativa. Se esse erro aparecer no journalctl -u nftables, o override não está surtindo efeito.

A correção que funciona de forma consistente não é ajustar o unit original (o bloqueio pode vir do confinamento do LXC, não só do systemd), e sim usar um serviço próprio, sem sandboxing:

cat > /etc/systemd/system/nftables-load.service << 'EOF'
[Unit]
Description=Load nftables ruleset (LXC-safe)
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl disable --now nftables 2>/dev/null
systemctl enable --now nftables-load

4.1. Ruleset de referência

#!/usr/sbin/nft -f
# ZBXPROXY-EXEMPLO-HQ - Zabbix Proxy 7
# Modo ativo, sem porta de entrada exposta ao Zabbix server

flush ruleset

table inet filter {

    set trusted_ips {
        type ipv4_addr
        flags interval
        elements = {
            203.0.113.10,     # Zabbix server (nuvem)
            198.51.100.20,    # Escritório administrativo
            10.99.0.0/24      # Rede corporativa (VPN)
        }
    }

    set client_vlans {
        type ipv4_addr
        flags interval
        elements = {
            10.10.9.0/24,     # VLAN de monitoramento
            10.10.20.0/24,    # VLAN de servidores internos
            10.10.21.0/28,    # VLAN de management (hypervisor)
            10.10.100.0/24    # VLAN de management (switches/APs)
        }
    }

    chain input {
        type filter hook input priority 0; policy drop;

        iif "lo" accept
        ct state established,related accept
        ct state invalid drop

        ip protocol icmp icmp type { echo-request, echo-reply, destination-unreachable, time-exceeded } accept

        ip saddr @trusted_ips tcp dport 22222 accept

        # Itens ativos / heartbeat de agentes vindos das sub-redes monitoradas
        ip saddr @client_vlans tcp dport 10051 accept

        log prefix "nft-input-drop: " limit rate 5/minute
        drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy drop;

        oif "lo" accept
        ct state established,related accept

        udp dport 53 accept
        tcp dport 53 accept
        udp dport 123 accept

        ip daddr 203.0.113.10 tcp dport 10051 accept

        ip daddr @client_vlans accept

        log prefix "nft-output-drop: " limit rate 5/minute
        drop
    }
}

Pontos de atenção nesse ruleset:

  • O INPUT libera SSH apenas na porta não padrão 22222, já migrada do padrão 22. Manter a porta padrão fora do firewall reduz o volume de tentativas automatizadas de brute-force contra o host.
  • A regra de client_vlans na porta 10051 do INPUT existe porque agentes configurados com item ativo (ServerActive=) iniciam a conexão em direção ao proxy. Sem essa regra, a coleta ativa falha com timeout mesmo que o SNMP e os checks passivos funcionem normalmente.
  • O OUTPUT é restritivo por padrão (policy drop), liberando apenas DNS, NTP, o Zabbix server e as sub-redes monitoradas. Isso bloqueia inclusive apt update e testes de conectividade genéricos (ex: ping para 1.1.1.1), o que é o comportamento esperado. Para rodar updates de sistema, libere temporariamente:
nft insert rule inet filter output position 0 tcp dport { 80, 443 } accept
apt update && apt upgrade -y
nft -f /etc/nftables.conf   # remove a regra temporária

Aplicação e teste:

cp nftables-ruleset.conf /etc/nftables.conf
nft -f /etc/nftables.conf
systemctl restart nftables-load
nft list ruleset

Teste sempre em uma sessão SSH separada antes de encerrar a sessão atual, para não perder acesso ao host em caso de erro na regra.


5. Instalação do Zabbix Agent 2

Referência oficial: Zabbix Documentation 7.0 — Install.

Válido tanto para hosts monitorados comuns quanto para o próprio servidor do proxy (seção 6).

5.1. Repositório e pacote

Se o host já tiver o repositório Zabbix configurado (por exemplo, por já ter o proxy instalado), pule direto para a instalação do agente. Caso contrário:

wget https://repo.zabbix.com/zabbix/7.0/debian-arm64/pool/main/z/zabbix-release/zabbix-release_latest_7.0+debian13_all.deb
dpkg -i zabbix-release_latest_7.0+debian13_all.deb
apt update
apt install zabbix-agent2

5.2. Plugins (opcional)

Necessário apenas se o host monitorar serviços específicos como bancos de dados:

apt install zabbix-agent2-plugin-mongodb zabbix-agent2-plugin-mssql zabbix-agent2-plugin-postgresql

5.3. Iniciar o serviço

systemctl restart zabbix-agent2
systemctl enable zabbix-agent2

A configuração de Server=, ServerActive= e TLS PSK é detalhada na seção 3 (para a chave agente-proxy) e na seção 6 (para o caso específico do host que também executa o proxy).


6. Host que acumula proxy e agente monitorado

É comum que o mesmo servidor rode o processo zabbix_proxy e também precise ser monitorado como host comum (CPU, disco, memória do próprio proxy). Isso exige atenção a dois pontos:

Saúde do processo proxy: cadastre um host no server vinculado ao template "Zabbix proxy health", com Monitored by = Proxy (o próprio proxy monitorando a si mesmo, via loopback). Não existe interface de rede nesse host, já que os itens são do tipo "Zabbix internal", calculados localmente por quem avalia o host.

Saúde do sistema operacional: para isso, instale o zabbix-agent2 normalmente no mesmo servidor (seção 5), configurando Server=127.0.0.1 e ServerActive=127.0.0.1. Como a checagem é feita localmente, o tráfego passa pela interface de loopback, que já está liberada por padrão em qualquer ruleset (iif "lo" accept).

Um erro comum nesse cenário é o agente rejeitar conexões vindas do IP real da máquina (não apenas 127.0.0.1), gerando Connection reset by peer. Se o teste for feito a partir do IP de rede em vez do loopback, ajuste Server= para incluir ambos:

Server=127.0.0.1,10.10.9.252

7. Checklist de hardening (implantação)

  • [ ] TLS PSK configurado entre proxy e server, com chave exclusiva (não reaproveitada de outro uso)
  • [ ] TLS PSK configurado entre agentes e proxy, preferencialmente com chave exclusiva por host
  • [ ] Firewall com INPUT restrito a origens conhecidas (administração) e sub-redes monitoradas na porta de itens ativos
  • [ ] Firewall com OUTPUT restrito ao Zabbix server, DNS, NTP e as sub-redes monitoradas
  • [ ] PostgreSQL aceitando apenas conexões locais (listen_addresses = 'localhost')
  • [ ] Permissões de arquivo restritas em chaves PSK (chmod 400) e no config principal (chmod 640)
  • [ ] Serviço rodando com usuário dedicado (zabbix), não root
  • [ ] Atualizações de segurança do sistema operacional agendadas ou aplicadas periodicamente

Para o checklist de integrações (SNMP e Grafana), ver o documento "Integrações do Zabbix Proxy".