Percio Castelo.
EN Início
Wazuh Docker: resolvendo erros de permissão e TLS no generate-indexer-certs.yml
← Voltar para blog

Wazuh Docker: resolvendo erros de permissão e TLS no generate-indexer-certs.yml

Atualizado em 19/09/2026 · Publicado em 14/06/2026 · 5 min · Cibersegurança · 602 visualizações

Montar a stack do Wazuh via Docker em produção (seja em arquitetura single-node ou multi-node) exige blindagem de comunicação por padrão. O tráfego entre o Manager, o Indexer e o Dashboard roda obrigatoriamente sob mTLS (Mutual TLS).

Para poupar o trabalho de gerar essas chaves de forma manual, o repositório oficial do Wazuh fornece um compose utilitário chamado generate-indexer-certs.yml. Ele roda um container temporário (generator) que lê as instruções do arquivo instances.yml e cospe todos os certificados necessários na pasta ./config/certs/ do host.

No entanto, no primeiro boot da stack principal, é muito comum ver o container wazuh.indexer entrar em crash loop eterno ou rejeitar as chaves TLS. Se você está lidando com isso agora, o problema costuma residir em dois pontos específicos detalhados a seguir.


Pré-requisitos#

Antes de executar os comandos de geração, certifique-se de possuir:

  • Docker Engine 20.10+ e Docker Compose v2 instalados no host.
  • Repositório oficial clonado localmente: git clone https://github.com/wazuh/wazuh-docker.git. Em versões recentes do repositório (4.7+), o generate-indexer-certs.yml fica em single-node/ ou multi-node/ — ajuste o path: docker compose -f single-node/generate-indexer-certs.yml run --rm generator.
  • Espaço em disco suficiente no diretório ./config.
  • Acesso root ou privilégios de sudo para manipulação de arquivos do sistema.

1. Falha de permissão de acesso (accessdeniedexception)#

O container utilitário do gerador roda como root e escreve as pastas e chaves no host. Porém, o binário do Wazuh Indexer roda dentro do container sob o usuário de sistema wazuh, que possui o UID/GID 1000.

Se você não acertar os donos dos arquivos no disco do host, o indexador quebra logo no boot porque não consegue ler a chave privada (.key):

wazuh-indexer | java.nio.file.AccessDeniedException: /usr/share/wazuh-indexer/config/certs/wazuh.indexer.key

Como resolver via chown no host#

Ajuste a propriedade e as permissões de leitura das chaves no host imediatamente após a geração:

# Corrige o proprietário para o UID 1000 do Wazuh
sudo chown -R 1000:1000 ./config/certs

# Restringe as permissões das chaves privadas e certificados
sudo chmod 600 ./config/certs/*.key
sudo chmod 644 ./config/certs/*.pem

Alternativa: mapeamento de usuário (user mapping) no compose#

Se você deseja evitar o ajuste manual de chown no host, declare o mapeamento do usuário diretamente no arquivo docker-compose.yml para coincidir com os certificados gerados:

services:
  wazuh.indexer:
    image: wazuh/wazuh-indexer:4.x
    user: "1000:1000"  # Força o container a rodar com UID/GID correspondentes
    volumes:
      - ./config/certs:/usr/share/wazuh-indexer/config/certs:ro

Dessa forma, o Docker garante que o processo do container tenha permissão de leitura sobre o bind mount sem necessidade de root manual no host.


2. Hostname mismatch no handshake do TLS#

A negociação TLS valida de forma estrita o campo SAN (Subject Alternative Name) do certificado. Se o nome do serviço ou o hostname declarado no seu docker-compose.yml (ex: wazuh.indexer) for diferente do nome configurado no arquivo de provisionamento, a comunicação falha.

O erro exibido varia conforme o componente que tenta iniciar a conexão:

  • Wazuh Dashboard (Node.js — OpenSearch Dashboards): ERR_TLS_CERT_ALTNAME_INVALID no log do container / HTTP 503 no endpoint do painel
  • Wazuh Indexer (Java): java.security.cert.CertificateException: No Subject Alternative Names present
  • Wazuh Manager (C): ERROR: SSL server certificate verification failed

Como resolver#

Abra o arquivo config/certs/instances.yml e garanta que o bloco name e os valores de dns batam exatamente com o nome dos serviços descritos no seu arquivo docker-compose.yml:

# config/certs/instances.yml
instances:
  - name: wazuh.indexer
    ip:
      - 127.0.0.1
    dns:
      - wazuh.indexer
      - localhost
  - name: wazuh.manager
    dns:
      - wazuh.manager
  - name: wazuh.dashboard
    dns:
      - wazuh.dashboard

Segurança: backup obrigatório antes de limpar certificados#

Se precisar alterar essas configurações, nunca limpe o diretório antigo de forma direta sem backup. Se outros componentes ativos dependerem destas assinaturas, eles perderão comunicação. Salve um backup com timestamp e garanta que o backup foi gerado com sucesso antes de efetuar qualquer deleção física:

# Cria backup compactado e limpa o diretório apenas em caso de sucesso
BACKUP_DATE=$(date +%Y%m%d-%H%M%S)
sudo tar czf "./config/certs-backup-${BACKUP_DATE}.tar.gz" ./config/certs/ && \
sudo rm -rf ./config/certs/* || { echo "❌ Falha no backup! Abortando exclusão."; exit 1; }

echo "✅ Backup gerado com sucesso e pasta limpa para regeneração."

3. Segurança adicional: SELinux e AppArmor#

Em distribuições como RHEL, Rocky Linux, CentOS ou Ubuntu com AppArmor ativo, o mecanismo de Controle de Acesso Obrigatório (MAC) do kernel pode bloquear o acesso do container aos certificados montados, mesmo que as permissões de arquivos (chmod/chown) estejam corretas no host.

Verificando e ajustando contexto do SELinux#

Se o comando getenforce retornar Enforcing, você precisará rotular o diretório de certificados.

# Opção 1: Ajuste temporário/volátil (rápido para testes)
sudo chcon -R -t container_file_t ./config/certs/

# Opção 2: Ajuste persistente (recomendado em produção)
# 1. Registrar o contexto padrão para o path absoluto do diretório de certificados
sudo semanage fcontext -a -t container_file_t "/path/absoluto/do/projeto/config/certs(/.*)?"
# 2. Aplicar o contexto registrado
sudo restorecon -R -v ./config/certs

4. Script de instalação e setup automatizado#

Utilize o script abaixo para realizar o setup inicial dos certificados de forma limpa, segura e com validação de conectividade. Em versões recentes do repositório (4.7+), o generate-indexer-certs.yml fica em single-node/ ou multi-node/ — ajuste o path: docker compose -f single-node/generate-indexer-certs.yml run --rm generator. Você pode inspecionar o código completo abaixo sem sair da página ou acessar a versão mantida no Gist:

wazuh-certs-setup.sh — Setup e provisionamento de certificados Wazuh
GistVer código
#!/usr/bin/env bash
# wazuh-certs-setup.sh - Setup e provisionamento de certificados Wazuh
set -euo pipefail

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }

# Validação de execução como root
if [ "$(id -u)" -ne 0 ]; then
  log "❌ Execute como root (sudo su -) ou use: sudo bash $0"; exit 1
fi

# 1. Validação de Dependências
for cmd in docker openssl; do
  command -v "$cmd" >/dev/null 2>&1 || { log "❌ Dependência ausente: $cmd"; exit 1; }
done

# 2. Backup Preventivo com validação de sucesso
if [ -d "./config/certs" ] && [ "$(ls -A ./config/certs)" ]; then
  BACKUP_PATH="./config/certs-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
  log "💾 Gerando backup de chaves antigas em $BACKUP_PATH..."
  tar -czf "$BACKUP_PATH" ./config/certs/ && rm -rf ./config/certs/* || { log "❌ Falha crítica no backup de certificados! Abortando limpeza."; exit 1; }
fi

mkdir -p ./config/certs

# 3. Geração de Certificados (em repositórios 4.7+, o yml fica em single-node/ ou multi-node/)
log "🔑 Iniciando container de geração de certificados Wazuh..."
COMPOSE_FILE="generate-indexer-certs.yml"
[ ! -f "$COMPOSE_FILE" ] && [ -f "single-node/generate-indexer-certs.yml" ] && COMPOSE_FILE="single-node/generate-indexer-certs.yml"
docker compose -f "$COMPOSE_FILE" run --rm generator

# 4. Correção de Permissões
log "🔧 Ajustando proprietário e permissões (UID 1000)..."
chown -R 1000:1000 ./config/certs
chmod 600 ./config/certs/*.key
chmod 644 ./config/certs/*.pem

# 5. Aplicação de políticas SELinux se ativo (persistente se semanage disponível)
if command -v getenforce >/dev/null && [ "$(getenforce)" = "Enforcing" ]; then
  log "🛡️ Aplicando contexto SELinux (container_file_t)..."
  if command -v semanage >/dev/null; then
    ABS_PATH=$(realpath ./config/certs)
    semanage fcontext -a -t container_file_t "$ABS_PATH(/.*)?" 2>/dev/null && restorecon -R ./config/certs || chcon -R -t container_file_t ./config/certs
  else
    chcon -R -t container_file_t ./config/certs
  fi
fi

log "✅ Processo concluído com sucesso!"

5. Script de diagnóstico e triagem de erros#

Se encontrar erros no handshake TLS ou problemas de inicialização, execute este script de diagnóstico no host para analisar o status das permissões, logs de erros e Subject Alternative Names (SAN) dos certificados gerados. Você pode inspecionar o código completo abaixo sem sair da página ou acessar a versão mantida no Gist:

diagnose-wazuh-certs.sh — Diagnóstico de certificados do Wazuh
GistVer código
#!/usr/bin/env bash
# diagnose-wazuh-certs.sh - Script de diagnóstico de certificados do Wazuh

set -eo pipefail

echo "=== DIAGNÓSTICO DO WAZUH DOCKER ==="
echo "Data: $(date)"
echo ""

# 1. Verificar Status dos Containers
echo "📦 Status dos Containers da Stack:"
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -i wazuh || echo "Nenhum container Wazuh encontrado."
echo ""

# 2. Verificar Permissões Físicas dos Certificados
echo "👤 Dono e Permissões dos Arquivos de Chave:"
if [ -d "./config/certs" ]; then
  for file in ./config/certs/*; do
    if [ -f "$file" ]; then
      stat -c "  %n -> Owner: %U:%G | Perms: %a" "$file"
    fi
  done
else
  echo "❌ Diretório ./config/certs não encontrado no host."
fi
echo ""

# 3. Validar Subject Alternative Name (SAN) no Certificado do Indexer
echo "🔍 Auditoria do Subject Alternative Name (SAN):"
INDEXER_CERT="./config/certs/wazuh.indexer.pem"
if [ -f "$INDEXER_CERT" ]; then
  openssl x509 -in "$INDEXER_CERT" -text -noout | grep -A2 "Subject Alternative Name" || echo "⚠️ SAN não encontrado no certificado."
else
  echo "❌ Certificado wazuh.indexer.pem não encontrado."
fi
echo ""

# 4. Extrair Logs de Erros de Conexão ou Inicialização (Nota: em multi-node o nome do container pode variar, ex: wazuh-indexer-1)
echo "📋 Últimos erros encontrados nos logs do Indexer:"
INDEXER_CONTAINER=$(docker ps --format '{{.Names}}' | grep -i -E "wazuh.*indexer" | head -n 1 || echo "wazuh.indexer")
echo "Lendo logs do container: $INDEXER_CONTAINER"
docker logs --tail 50 "$INDEXER_CONTAINER" 2>&1 | grep -i -E "error|exception|denied|mismatch|failed" || echo "Nenhum log de erro crítico encontrado nas últimas 50 linhas."
echo ""

# 5. Rollback de Emergência / Reset do Gerador
echo "⚠️  NOTA DE RECUPERAÇÃO:"
echo "Se precisar recomeçar do zero e limpar estados órfãos, execute:"
echo "  docker compose -f generate-indexer-certs.yml down -v"
echo "  rm -rf ./config/certs/*"
echo "==================================="

Fluxo de provisionamento e inicialização (troubleshooting)#

Abaixo está o mapeamento visual do pipeline de geração, permissões, contexto do SELinux e validação do boot da stack do Wazuh:

instances.yml configurado
        │
        ▼
docker compose -f generate-indexer-certs.yml run --rm generator
        │
        ▼
Certificados gravados no host (root)
        │
        ▼
chown -R 1000:1000 ./config/certs
        │
        ▼
chmod 600 *.key  ·  chmod 644 *.pem
        │
        ▼
   SELinux ativo? ──Sim──▶ semanage fcontext + restorecon
        │                          │
       Não                         │
        └──────────▼───────────────┘
        Validar SAN no certificado (openssl x509)
        │
        ▼
docker compose up -d

Conclusão#

Neste guia cobri as duas falhas que mais derrubam o boot do indexer — permissão de leitura das chaves (UID 1000) e validação estrita de SAN no handshake mTLS — e o caminho de blindagem com SELinux persistente. Com o setup automatizado e o diagnóstico pronto, o crash loop vira um problema resolvido em minutos.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

602 leituras
Creative Commons Atribuição-NãoComercial

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários