Resumo: se os pacotes Syslog do FortiGate chegam ao container, mas o arquivo messages não é criado, o problema pode estar no perfil do AppArmor aplicado ao rsyslogd. Neste guia, você acompanha o diagnóstico por camadas e aplica uma exceção com o menor privilégio necessário.

Ao implantar o Cloud Discovery do Microsoft Defender for Cloud Apps, um dos principais objetivos é identificar aplicações em nuvem utilizadas dentro da organização, inclusive serviços que não foram formalmente aprovados pela TI — o conhecido Shadow IT.

Para isso, o Microsoft Defender pode receber os logs de navegação gerados por firewalls e proxies. Em uma arquitetura com FortiGate, um modelo comum é encaminhar os eventos de tráfego para um Log Collector executado em um container Docker no Linux. O coletor recebe os eventos por Syslog, processa os arquivos e os envia ao portal do Microsoft Defender.

Em teoria, o fluxo é simples:

FortiGate → Syslog UDP/514 → Docker → rsyslog → arquivo messages → processamento → Microsoft Defender

Na prática, uma política de segurança do próprio Linux pode interromper silenciosamente esse processo.

Este artigo apresenta um caso real em que:

  • o FortiGate estava enviando os logs;
  • os pacotes chegavam ao container;
  • a porta UDP/514 estava aberta;
  • o coletor conseguia se comunicar com o portal da Microsoft;
  • mas nenhum evento era processado ou enviado.

A causa não estava no firewall, na rede, no Docker ou no Microsoft Defender. O bloqueio era provocado por um perfil do AppArmor aplicado ao processo rsyslogd.

Nota de atualização (agosto de 2026): a documentação atual da Microsoft mostra --security-opt apparmor:unconfined no exemplo de implantação do coletor. Se o seu comando já utiliza essa opção, confirme o perfil efetivamente aplicado ao processo antes de alterar o AppArmor. O caso descrito neste artigo trata de um ambiente em que o rsyslogd aparecia como rsyslogd (enforce).

Entendendo a arquitetura do Log Collector

O Log Collector do Defender for Cloud Apps é executado como um container. No caso do Syslog, o fluxo interno ocorre, de forma simplificada, da seguinte maneira:

  1. O firewall envia um evento Syslog para o endereço IP e a porta configurados.
  2. O Docker encaminha o pacote para a porta correspondente dentro do container.
  3. O rsyslogd recebe o evento.
  4. Um ruleset direciona o evento para um arquivo chamado messages.
  5. O componente de processamento do coletor interpreta e compacta o arquivo.
  6. O arquivo é enviado ao Microsoft Defender for Cloud Apps pela porta TCP/443.

A própria documentação do rsyslog apresenta a associação entre uma entrada UDP, um ruleset e uma ação omfile, responsável pela gravação dos eventos em arquivo. Rsyslog — configuração de um servidor remoto

No Log Collector, o arquivo de entrada fica normalmente neste caminho:

/var/adallom/syslog/<porta-da-fonte>/messages

Para uma fonte configurada na UDP/514:

/var/adallom/syslog/514/messages

Esse detalhe é importante porque receber o pacote na interface de rede não significa que o processo conseguiu gravá-lo em disco.

O sintoma inicial

O primeiro sinal do problema apareceu no comando de status do coletor:

sudo docker exec <NOME_DO_COLETOR> \
  collector_status -p

O resultado indicava:

General information:
    Files uploaded: 0
    Status: OK

Data sources:
    Last log received: Not yet received
    Number of logs received: 0

Em determinado momento, depois do reinício, o status também passou temporariamente para:

Status: Error

Embora o coletor conseguisse se conectar ao portal, nenhum arquivo havia sido enviado e nenhuma fonte apresentava eventos recebidos.

Um erro comum nesse momento seria partir diretamente para alterações no FortiGate, recriar o container ou modificar a fonte no portal. Antes disso, porém, era necessário descobrir exatamente em qual etapa do fluxo o evento estava sendo interrompido.

Status “OK” não comprova que os logs estão sendo processados

O status do coletor precisa ser interpretado com cuidado.

Um resultado Status: OK indica que os componentes principais estão ativos e que o coletor consegue executar suas verificações internas. Também é possível que ele esteja se comunicando normalmente com o portal.

Isso não prova, isoladamente, que:

  • o FortiGate está enviando eventos;
  • os pacotes chegam ao container;
  • o rsyslogd consegue gravá-los;
  • o formato dos eventos é reconhecido;
  • o arquivo atingiu o tamanho necessário para upload;
  • os dados já foram processados pelo Defender.

A análise precisa validar cada camada separadamente.

Etapa 1: validar o container e as portas publicadas

Primeiro, é necessário confirmar se o container está ativo:

sudo docker ps --filter name=<NOME_DO_COLETOR>

A saída deve apresentar o container em execução e a porta Syslog publicada, por exemplo:

0.0.0.0:514->514/udp

Isso confirma que o Docker está expondo a UDP/514 do host para a UDP/514 do container.

Também é importante consultar os logs do container:

sudo docker logs \
  --since 15m \
  --timestamps \
  <NOME_DO_COLETOR>

Nesse ponto, deve-se procurar erros de inicialização, conectividade, armazenamento, permissão ou processamento.

Etapa 2: confirmar que os pacotes chegam ao container

O próximo passo é verificar se o FortiGate realmente está entregando os eventos.

Uma forma precisa de realizar essa validação é obter o PID do container e executar o tcpdump dentro do seu namespace de rede:

container_pid=$(sudo docker inspect \
  --format '{{.State.Pid}}' \
  <NOME_DO_COLETOR>)

Depois:

sudo nsenter -t "$container_pid" -n \
  tcpdump -ni any udp port 514

Se aparecerem pacotes contendo campos típicos do FortiGate, como estes:

devname=
type="traffic"
subtype="forward"
srcip=
dstip=

fica comprovado que:

  • o FortiGate está enviando;
  • o caminho de rede está funcionando;
  • a porta está acessível;
  • o Docker está entregando os pacotes ao container.

A partir desse momento, continuar alterando policies, rotas ou configurações Syslog do FortiGate provavelmente não ajudará. O diagnóstico deve avançar para dentro do container.

Etapa 3: verificar o listener do rsyslog

É necessário confirmar que o rsyslogd está ouvindo na porta definida para a fonte:

sudo nsenter -t "$container_pid" -n \
  ss -lunp | grep ':514'

O processo esperado é o rsyslogd.

Também é possível confirmar sua execução:

sudo docker exec <NOME_DO_COLETOR> \
  ps -eo user,pid,cmd | grep '[r]syslogd'

Nesse caso, tanto o processo Java do coletor quanto o rsyslogd estavam ativos.

Isso eliminou a hipótese de que o serviço estivesse parado. Ainda faltava descobrir se o evento recebido conseguia chegar ao arquivo de destino.

Etapa 4: inspecionar o arquivo messages

A Microsoft orienta verificar diretamente se os eventos Syslog estão sendo gravados no arquivo messages:

sudo docker exec <NOME_DO_COLETOR> \
  ls -la /var/adallom/syslog/514

E, se o arquivo existir:

sudo docker exec <NOME_DO_COLETOR> \
  tail -n 10 /var/adallom/syslog/514/messages

No ambiente analisado, o diretório existia, mas estava vazio:

/var/adallom/syslog/514/

O arquivo abaixo não era criado:

/var/adallom/syslog/514/messages

Essa constatação reduziu significativamente o escopo da investigação. Os eventos chegavam à rede do container, mas não eram persistidos pelo rsyslog.

Um teste interno para eliminar a rede da equação

Para confirmar que a falha era interna, foi gerado um evento diretamente dentro do container:

sudo docker exec <NOME_DO_COLETOR> \
  logger -n 127.0.0.1 -P 514 -d \
  -p local7.notice \
  'TESTE-RSYSLOG-INTERNO'

Depois:

sudo docker exec <NOME_DO_COLETOR> \
  sh -c '
  sleep 2
  test -f /var/adallom/syslog/514/messages &&
    tail -n 5 /var/adallom/syslog/514/messages ||
    echo "Arquivo messages não foi criado"
  '

O arquivo continuou ausente.

Esse teste foi decisivo: mesmo um evento gerado localmente, sem depender do FortiGate, da rede física ou do encaminhamento entre equipamentos, não era gravado.

Portanto, a falha estava entre:

rsyslogd → ruleset → criação do arquivo

A mensagem que revelou a causa

Ao analisar os logs internos, apareceu o erro:

file '/var/adallom/syslog//514/messages':
open error: Permission denied

Em um primeiro momento, uma mensagem Permission denied normalmente direciona a investigação para:

  • proprietário do diretório;
  • grupo;
  • permissões de leitura e escrita;
  • ACL;
  • sistema de arquivos somente leitura;
  • usuário usado pelo processo.

Mas as permissões Unix não explicavam o comportamento.

O rsyslogd estava executando como root, e o diretório também pertencia a root.

Foi então realizado um teste de escrita comum:

sudo docker exec <NOME_DO_COLETOR> \
  touch /var/adallom/syslog/514/teste-escrita-root

O arquivo foi criado com sucesso.

Isso produziu uma evidência importante:

O sistema de arquivos aceitava escrita e o usuário root tinha permissão. O bloqueio era específico para o processo rsyslogd.

Root não ignora todas as políticas de segurança

No Linux, executar como root não significa ter liberdade absoluta.

Mecanismos de controle de acesso obrigatório, como AppArmor e SELinux, podem restringir um processo mesmo quando ele é executado pelo usuário root. Essas políticas avaliam o perfil atribuído ao processo e os recursos que ele está autorizado a acessar.

Para verificar o perfil aplicado ao rsyslogd, foi executado:

sudo docker exec <NOME_DO_COLETOR> \
  sh -c '
  rsyslog_pid=$(pgrep -o rsyslogd)
  cat "/proc/$rsyslog_pid/attr/current"
  '

O resultado foi:

rsyslogd (enforce)

Isso demonstrou que o processo estava confinado por um perfil chamado rsyslogd, em modo de aplicação efetiva.

No modo enforce, o AppArmor não apenas registra operações fora da política: ele efetivamente as bloqueia.

A documentação do Docker explica que o AppArmor pode aplicar perfis aos processos dos containers e recomenda utilizar dmesg e aa-status durante o diagnóstico. Docker — AppArmor security profiles

Confirmando o bloqueio no log do kernel

A confirmação definitiva veio do log de auditoria do kernel:

sudo journalctl -k \
  --since "30 minutes ago" \
  --no-pager |
  grep -iE 'apparmor="DENIED"|profile="rsyslogd"|/var/adallom'

A saída apresentou eventos como:

apparmor="DENIED"
operation="mkdir"
profile="rsyslogd"
name="/var/adallom/syslog/514/"
requested_mask="c"
denied_mask="c"

Essa mensagem contém todas as informações necessárias:

  • apparmor="DENIED": a operação foi negada pelo AppArmor;
  • operation="mkdir": o processo tentou criar um diretório;
  • profile="rsyslogd": o perfil responsável foi o do rsyslog;
  • name="/var/adallom/syslog/514/": o objeto bloqueado era o diretório do coletor;
  • requested_mask="c": o processo solicitou permissão de criação.

Mesmo que o diretório fosse criado manualmente, o processo ainda precisaria de autorização para criar e escrever o arquivo messages.

A correção: uma exceção restrita no perfil

A solução não foi desativar o AppArmor nem colocar o perfil inteiro em modo permissivo. Isso reduziria a segurança do sistema e esconderia o verdadeiro conflito.

A abordagem correta foi criar uma autorização limitada ao diretório utilizado pelo Log Collector.

Primeiro, é necessário localizar o perfil:

sudo grep -Rns \
  'profile.*rsyslogd\|usr/sbin/rsyslogd\|local/usr.sbin.rsyslogd' \
  /etc/apparmor.d 2>/dev/null

Em instalações Ubuntu, o perfil normalmente está relacionado a:

/etc/apparmor.d/usr.sbin.rsyslogd

Antes de criar a exceção, deve-se confirmar se o perfil principal inclui o arquivo de customização local:

sudo grep -n 'local/usr.sbin.rsyslogd' \
  /etc/apparmor.d/usr.sbin.rsyslogd

A entrada esperada é semelhante a:

#include <local/usr.sbin.rsyslogd>

A customização pode então ser adicionada ao arquivo:

/etc/apparmor.d/local/usr.sbin.rsyslogd

Para um ambiente com apenas a UDP/514, uma regra mais restrita pode considerar o diretório pai e a fonte específica:

# Microsoft Defender for Cloud Apps Log Collector
/var/adallom/syslog/ rw,
/var/adallom/syslog/514/ rw,
/var/adallom/syslog/514/** rw,

Se o servidor possuir diferentes fontes em portas distintas e o próprio coletor precisar criar dinamicamente essas estruturas, pode ser necessário autorizar o conteúdo do diretório Syslog:

# Microsoft Defender for Cloud Apps Log Collector
/var/adallom/syslog/ rw,
/var/adallom/syslog/** rw,

A segunda opção é mais abrangente. Portanto, deve ser utilizada apenas quando houver necessidade operacional comprovada.

As regras de arquivo do AppArmor permitem definir permissões sobre caminhos e utilizar padrões para abranger objetos subordinados. Ubuntu Manpages — sintaxe dos perfis AppArmor

Validando e recarregando o perfil

Depois da alteração, é importante validar a sintaxe antes de recarregar o perfil:

sudo apparmor_parser -Q \
  /etc/apparmor.d/usr.sbin.rsyslogd

Se nenhum erro for apresentado:

sudo apparmor_parser -r \
  /etc/apparmor.d/usr.sbin.rsyslogd

O perfil deve permanecer ativo:

sudo aa-status | grep rsyslogd

Não é necessário desativar o AppArmor nem reiniciar todo o servidor.

Depois, o processo rsyslogd do container pode ser reiniciado:

sudo docker exec <NOME_DO_COLETOR> \
  supervisorctl restart rsyslog

Testando a correção

Um novo evento interno deve ser gerado:

sudo docker exec <NOME_DO_COLETOR> \
  logger -n 127.0.0.1 -P 514 -d \
  -p local7.notice \
  'TESTE-RSYSLOG-APPARMOR-CORRIGIDO'

Em seguida:

sudo docker exec <NOME_DO_COLETOR> \
  sh -c '
  sleep 2
  ls -lh /var/adallom/syslog/514/messages
  tail -n 10 /var/adallom/syslog/514/messages
  '

O resultado esperado é encontrar a mensagem:

TESTE-RSYSLOG-APPARMOR-CORRIGIDO

Também é necessário verificar se não foram geradas novas negações:

sudo journalctl -k \
  --since "5 minutes ago" \
  --no-pager |
  grep -iE 'apparmor="DENIED".*profile="rsyslogd"|/var/adallom'

Se não houver novos eventos DENIED e o arquivo messages estiver sendo preenchido, o bloqueio foi resolvido.

Quando os eventos começam a ser enviados ao Defender?

Depois da correção, o arquivo messages passou a receber conteúdo real do FortiGate. Ainda assim, os contadores podiam continuar temporariamente em zero:

Files uploaded: 0
Number of logs received: 0

Isso não significa necessariamente que o problema continua.

No modo Syslog, o Log Collector grava os eventos em disco até o arquivo superar aproximadamente 40 KB. Depois disso, o arquivo é processado e enviado ao Defender for Cloud Apps. Após o upload, uma cópia pode ser consultada no diretório de backup:

/var/adallom/discoverylogsbackup

Esse comportamento é documentado oficialmente pela Microsoft. Microsoft Learn — upload automático para relatórios contínuos e gerenciamento avançado do Log Collector

O crescimento do arquivo pode ser acompanhado com:

sudo docker exec <NOME_DO_COLETOR> \
  ls -lh /var/adallom/syslog/514/messages

E o status:

sudo docker exec <NOME_DO_COLETOR> \
  collector_status -p

Depois do processamento, o resultado esperado é semelhante a:

Files uploaded: 1
Status: OK

Last log received: <data e hora>
Number of logs received: <valor maior que zero>

Um problema paralelo: dimensionamento do servidor

Durante o diagnóstico, também foi identificado que o servidor possuía aproximadamente 22 GB de armazenamento total disponível ao container.

Esse volume estava abaixo do requisito oficial da Microsoft, que atualmente indica:

  • 250 GB de espaço em disco;
  • 2 núcleos de CPU;
  • arquitetura Intel 64 ou AMD 64;
  • 4 GB de memória RAM.

A insuficiência de disco não foi a causa imediata do bloqueio de escrita, pois a negação foi comprovadamente produzida pelo AppArmor. Entretanto, manter o ambiente abaixo dos requisitos pode provocar descarte de logs, falhas futuras de processamento e colocar a implantação fora da configuração suportada pela Microsoft. Microsoft Learn — requisitos do Log Collector no Linux

Um diagnóstico bem executado também deve registrar problemas paralelos, mesmo quando eles não explicam o incidente atual.

O que não deveria ser feito

Durante esse tipo de investigação, algumas “soluções rápidas” podem criar novos riscos:

Aplicar chmod 777

Se o processo já executa como root e um mecanismo de controle obrigatório está negando a operação, ampliar as permissões Unix não resolverá o problema. Além disso, permitirá que outros usuários ou processos escrevam no diretório.

Desativar o AppArmor

Desabilitar o AppArmor pode fazer o erro desaparecer, mas remove uma camada de proteção de todo o sistema. O melhor caminho é autorizar somente os objetos necessários.

Executar o container permanentemente como privilegiado

Aumentar indiscriminadamente as capacidades do container amplia sua superfície de ataque e pode não resolver um perfil específico aplicado ao processo.

Recriar o coletor antes de entender a causa

A recriação pode apagar evidências e repetir exatamente o mesmo problema, especialmente quando a política responsável está carregada no host.

Alterar repetidamente o FortiGate

Depois que o tcpdump confirma os eventos dentro do container, o foco deve sair do firewall e avançar para o processamento interno.

Método recomendado de diagnóstico

A principal lição deste caso é investigar o fluxo por camadas:

  1. Verificar se o container está ativo.
  2. Confirmar as portas publicadas.
  3. Validar a comunicação com o portal.
  4. Capturar os pacotes dentro do namespace do container.
  5. Confirmar que o rsyslogd está ouvindo.
  6. Verificar a existência e o crescimento do arquivo messages.
  7. Gerar um evento local para eliminar dependências externas.
  8. Procurar erros de gravação nos logs.
  9. Comparar a escrita de um processo comum com a escrita do serviço.
  10. Consultar AppArmor, SELinux, ACLs e outras políticas de confinamento.
  11. Aplicar uma exceção baseada no menor privilégio.
  12. Repetir os testes e confirmar a ausência de novas negações.
  13. Aguardar o arquivo atingir o limite de processamento.
  14. Validar o upload e a interpretação dos eventos no portal.

A orientação de troubleshooting da Microsoft segue a mesma lógica geral: verificar a associação da fonte ao coletor, confirmar o protocolo e a porta, validar a chegada dos eventos e garantir a comunicação de saída pela TCP/443. Microsoft Learn — solução de problemas do Cloud Discovery

Conclusão

O aspecto mais interessante deste incidente foi que quase todos os componentes aparentavam estar funcionando:

  • o FortiGate enviava os eventos;
  • os pacotes chegavam ao container;
  • a UDP/514 estava aberta;
  • o rsyslogd estava ativo;
  • o coletor se conectava ao portal;
  • o diretório existia;
  • o usuário root conseguia escrever nele.

Ainda assim, o fluxo estava interrompido.

A causa somente ficou evidente quando a análise deixou de tratar Permission denied como uma simples permissão de arquivo e passou a considerar os controles de acesso obrigatórios do Linux.

O AppArmor cumpria exatamente a sua função: impedia que um processo escrevesse fora dos caminhos previstos pelo seu perfil. O problema estava na incompatibilidade entre a política aplicada ao rsyslogd e o diretório utilizado pelo Log Collector.

A correção adequada não foi remover a proteção, mas ajustar a política com o menor privilégio necessário.

Esse caso reforça uma regra valiosa para troubleshooting de infraestrutura e segurança: acompanhe o dado de ponta a ponta e valide cada transição do fluxo. Quando o pacote chega à interface, mas não aparece na aplicação, a rede pode já ter feito seu trabalho. O problema pode estar na próxima camada — e, algumas vezes, na própria proteção criada para manter o ambiente seguro.