Wiki
GeoIP e controlo de acesso geográfico
Como o QuoteNode usa uma base de dados GeoIP local para controlo de acesso por país e seguimento de propostas.
GeoIP e controlo de acesso geográfico
O QuoteNode inclui um controlo de acesso geográfico opcional e seguimento por país através de uma base de dados GeoIP local por país (DB-IP Country Lite sem chave por predefinição, ou MaxMind GeoLite2).
Dois usos do GeoIP
1. Controlo de acesso à aplicação
Quando ativado, o filtro GeoIP pode restringir o acesso à aplicação a determinados países. Os pedidos de países não permitidos recebem HTTP 403 (Forbidden).
É útil para organizações que:
- operam apenas em jurisdições específicas,
- querem reduzir a exposição a ataques automatizados de certas regiões,
- têm de cumprir requisitos de residência de dados ou de controlo de acesso.
2. Seguimento de interações com a proposta
Quando um cliente abre um link público de proposta, o sistema regista o código do país do cliente (ISO 3166-1 alpha-2) juntamente com o evento de interação. Isto dá contexto geográfico à análise de propostas:
- «A sua proposta foi aberta a partir da Alemanha» — confirma alcance internacional,
- «Várias aberturas de países diferentes» — pode indicar que a proposta foi reencaminhada.
Como funciona
Base de dados
A resolução GeoIP usa uma base de dados local por país no formato MaxMind DB (.mmdb) — não uma API externa. Todas as consultas ocorrem em memória, sem chamadas de rede.
Por predefinição é a edição DB-IP Country Lite sem chave (licença CC-BY-4.0, sem conta). O contentor descarrega-a automaticamente no arranque (e segundo um agendamento diário) se estiver em falta ou desatualizada — independentemente de GEOIP_ENABLED, governado por GEOIP_AUTO_UPDATE (predefinição true). A base pode assim estar pronta enquanto a imposição por país está desligada. Sem descarga manual nem montagem de ficheiro. Para usar antes o MaxMind GeoLite2, define MAXMIND_ACCOUNT_ID / MAXMIND_LICENSE_KEY (uma conta MaxMind gratuita); é o mesmo formato .mmdb. Também podes preencher o ficheiro manualmente com scripts/update-geoip.sh.
Deteção do IP
O IP do cliente é resolvido inteiramente pela aplicação — não é retirado cegamente dos cabeçalhos do pedido. Um X-Forwarded-For, X-Real-IP ou CF-Connecting-IP falsificado a partir de um cliente direto nunca pode falsificar a verificação de país.
- A aplicação toma o par de transporte (o endereço que efetivamente abriu a ligação).
- O par só é considerado de confiança quando é um proxy declarado explicitamente — uma entrada em
SECURITY_TRUSTED_PROXIES(IP/CIDR literais separados por vírgulas e/ou nomes de serviço do Compose). O serviço integradofrontend/caddyé declarado automaticamente, pelo que os stacks padrão de Docker/Coolify funcionam sem configuração. Um endereço privado/loopback/CGNAT não é de confiança apenas por não ser roteável globalmente. - Se o par não for um proxy de confiança, todos os cabeçalhos de reencaminhamento são ignorados e o próprio par é o cliente. A partir de um proxy de confiança, o cliente é o único e autoritativo
X-Real-IP; caso contrário, a cadeiaX-Forwarded-Forpercorrida da direita para a esquerda, ignorando apenas os saltos de confiança (o primeiro endereço não fiável a partir da direita é o cliente, mesmo que seja privado). Apenas são aceites IP literais (sem DNS). O backend nunca lêCF-Connecting-IP— a Cloudflare é tratada na edge (abaixo).
Atrás da Cloudflare (automático). Quando a implantação está atrás da Cloudflare, o par de transporte estrito é um endereço de edge da Cloudflare, não o visitante. A edge Caddy integrada (o Caddy do frontend no Coolify e o Caddy autónomo) normaliza isto automaticamente: um pedido só é tratado como Cloudflare quando o IP que se liga está dentro das gamas oficiais publicadas da Cloudflare e o CF-Connecting-IP é um único IP bem formado; então X-Real-IP é definido para esse visitante real e o CF-Connecting-IP é removido antes do backend. Um valor duplicado, em lista ou malformado falha de forma segura (fail-closed) para o endereço de edge. Não adiciona as gamas da Cloudflare a SECURITY_TRUSTED_PROXIES, e o backend continua a nunca confiar num CF-Connecting-IP fornecido pelo cliente. A lista de gamas é versionada e atualizada por scripts/sync-cloudflare-ips.sh, protegida por uma verificação de desvio semanal.
Atrás de outro CDN. Um CDN que não seja a Cloudflare não é reconhecido automaticamente. Normalize o IP do cliente na sua própria edge da mesma forma — defina X-Real-IP para o visitante real e descarte os cabeçalhos de reencaminhamento não fiáveis — antes de o tráfego chegar ao backend.
O IP do cliente é um filtro grosseiro de melhor esforço, não autenticação. Quem conseguir alcançar a origem diretamente (contornando o CDN/proxy) poderia apresentar um cabeçalho de reencaminhamento à escolha; o reforço recomendado é restringir a origem por firewall para que apenas o seu CDN/proxy a alcance.
Os endereços de cliente não roteáveis globalmente (loopback, privados/RFC 1918, link-local, CGNAT, IPv6 ULA) nunca são geo-bloqueados — não podem ser geolocalizados — pelo que os health checks de localhost e os deployments LAN/same-host continuam a funcionar enquanto o GeoIP está ativo. A lista de permissões de IP e a autenticação continuam a aplicar-se-lhes.
Caminhos isentos
Os seguintes caminhos estão sempre isentos das restrições geográficas para se manterem acessíveis em todo o mundo:
- links públicos de propostas (
/offer/public/*) e branding público (/api/v1/public/branding/*), - ações de links de e-mail: cancelamento de subscrição (
/api/v1/notifications/unsubscribe-token/*) e preferências públicas (/api/v1/notifications/preferences-public/*), - endpoints de infraestrutura/saúde (
/health*,/actuator/health,/actuator/info).
A autenticação de administrador (/api/v1/auth/login, 2FA) e os recursos autenticados continuam sujeitos ao controlo geográfico.
Configuração
| Variável | Por omissão | Descrição |
|---|---|---|
GEOIP_ENABLED |
false |
Apenas interruptor de imposição por país — também o interruptor de emergência. Não afeta o carregamento da base de dados: a BD continua a carregar e a atualizar-se (ver GEOIP_AUTO_UPDATE), por isso pode estar pronta enquanto a imposição está desligada. false desativa de forma fiável toda a imposição por país (ver recuperação abaixo). |
GEOIP_DB_PATH |
/app/data/geoip/GeoLite2-Country.mmdb |
Caminho para o ficheiro de base de dados .mmdb por país (preenchido automaticamente quando o GeoIP está ativo). |
GEOIP_AUTO_UPDATE |
true |
Descarregar a base automaticamente no arranque (DB-IP sem chave por predefinição; MaxMind quando MAXMIND_* estão definidos) quando está em falta ou mais antiga que GEOIP_AUTO_UPDATE_MAX_AGE_DAYS (predefinição da aplicação 35; o modelo de produção define explicitamente 25). Define false para gerir o ficheiro manualmente. |
GEOIP_AUTO_UPDATE_MAX_AGE_DAYS |
35 |
Idade a partir da qual a base é considerada desatualizada e volta a ser descarregada. É a predefinição da aplicação; infra/.env.prod.example substitui-a por um 25 explícito, por isso uma instalação que omita a chave obtém 35. |
GEOIP_UPDATE_SCHEDULER_ENABLED |
true |
Interruptor da verificação periódica de desatualização. Vem ligado porque uma base de países desatualizada é o estado inseguro. Desligá-lo não afeta a atualização no arranque nem as atualizações manuais. |
GEOIP_UPDATE_CRON |
0 15 3 * * * |
Quando a verificação periódica corre, no fuso horário do servidor (UTC salvo se definir TZ). Um agendamento diário é uma verificação diária, não um download diário: o ficheiro só é obtido se faltar ou estiver desatualizado. O modelo suportado é um atualizador por caminho da base. |
SECURITY_GEOIP_ALLOWED_COUNTRIES |
(vazio) | Códigos de país ISO 3166-1 alpha-2 separados por vírgulas (p. ex. PL,DE,FR). Os códigos são normalizados para maiúsculas e validados; os inválidos são rejeitados. Vazio = sem restrição. Quando definido, este valor do operador prevalece sobre a definição do tenant na aplicação. |
Quando nenhuma lista de países está configurada, o filtro GeoIP é passivo — resolve códigos de país para fins de seguimento mas não bloqueia qualquer pedido. Ativar a filtragem por país na interface de administração usa um seletor de países validado (ISO 3166-1 alpha-2) e exige pelo menos um país e uma base de dados carregada.
Atualizar a base de dados: a descarga automática no arranque mantém-na razoavelmente atualizada, mas depois de substituir manualmente o ficheiro
.mmdbpode recarregá-lo sem reiniciar em Administração → Segurança → estado efetivo → Recarregar base de dados (um recarregamento protegido — um candidato corrompido ou em falta mantém a base de dados atualmente carregada). Um reinício do backend também adota o novo ficheiro. O painel de estado efetivo mostra se a base de dados está carregada, com as marcas temporais de compilação/carregamento.
Recuperação após um bloqueio de GeoIP
Se uma lista de países permitidos excluir acidentalmente o seu próprio país, recebe HTTP 403 com a sugestão de recuperação GEO_BLOCKED. Para recuperar o acesso:
- Defina
GEOIP_ENABLED=falseno ambiente do backend. - Reinicie o backend (isto desativa a aplicação de GeoIP antes de qualquer política ser avaliada).
- Corrija ou limpe a lista de países permitidos, depois defina
GEOIP_ENABLED=truee reinicie novamente.
Isto é independente do interruptor de recuperação da lista de permissões de IP — recuperar um nunca desativa o outro de forma silenciosa.
Considerações de privacidade
- A resolução GeoIP é sem estado — não é armazenada permanentemente qualquer correspondência IP-país.
- Para o seguimento de propostas é registado apenas o código do país (p. ex. «PT»), não o endereço IP em si.
- Os endereços IP registados nos eventos web das propostas estão sujeitos ao trabalho configurável de anonimização de IP, que os submete a hash após um número configurável de dias para conformidade com o RGPD.