Fixando seus IPs
Toda credencial carrega uma lista de endereços dos quais suas requisições podem vir. Uma requisição é aceita só se a assinatura for válida e ela chegar de um desses endereços.
accept = valid_signature(credential) AND source_ip ∈ allowed_ips
A lista é obrigatória: não emitimos credencial sem restrição de origem.
Por que a lista existe, se a requisição já é assinada
A maioria dos incidentes não envolve assinatura quebrada. Envolve uma chave privada que saiu de onde deveria ficar: um repositório commitado, um arquivo compartilhado, a máquina de um desenvolvedor, uma ferramenta de terceiro. Uma vez fora, a chave produz assinatura válida de qualquer lugar, e o tráfego fica idêntico ao da sua integração real.
A lista tira isso. Uma chave copiada para fora do seu ambiente para de funcionar, porque quem a tem não reproduz junto a sua origem de rede. Ela não substitui a assinatura: a assinatura prova quem produziu a requisição, a lista limita de onde ela pode vir.
É uma segunda camada, não um motivo para afrouxar o cuidado com a chave. Mantenha a privada em HSM ou cofre, use uma credencial por ambiente e por workload, e lembre que tudo que roda dentro do seu perímetro permitido herda essa confiança.
As regras
| Regra | Valor |
|---|---|
| Entradas por credencial | 1 a 50 |
| Formato aceito | Endereços IPv4 públicos individuais (203.0.113.10) |
| Faixas (CIDR) | Não aceitas |
| IPv6 | Ainda não suportado |
| Duplicados | Colapsados |
| Ordem | Canônica. A ordem que você envia não muda a credencial |
| Escopo | Por credencial e por ambiente; sandbox e produção são independentes |
Faixa é recusada de propósito: com CIDR livre, 0.0.0.0/0 seria uma entrada válida e uma lista
obrigatória não restringiria nada. Registre cada endereço de saída explicitamente. O teto de 50
cobre com folga várias regiões, caminhos de failover e workloads separados.
Contra o que a lista é comparada
O endereço do peer que chega até nós, ou seja, de onde suas requisições saem. Não o seu load balancer, não o seu hostname público, não o navegador de onde você está configurando. Endereço de entrada e de saída são coisas diferentes e quase nunca são o mesmo valor.
Esta seção é sobre o tráfego de você para nós. Os endereços que usamos ao chamar os seus webhooks são a direção oposta e se configuram no seu próprio firewall.
Descubra seu endereço de saída real
Rode isto de dentro do workload que vai chamar a API, pelo mesmo caminho de rede que ele usa em produção, e não do seu laptop:
curl --fail --silent https://ifconfig.me
Repita em cada instância, task ou função que nos chama. Um endereço que muda entre execuções não é fixo, mesmo que as duas primeiras execuções concordem.
Mudando a lista depois
Editar os IPs permitidos substitui a credencial por uma nova versão assinada, que mantém a mesma
chave e o mesmo client_id, então sua aplicação não muda. A versão anterior deixa de ser aceita naquele
momento, inclusive os tokens já emitidos por ela, então uma chamada em curso pode falhar uma vez e
se recuperar no token seguinte.
Registre o endereço novo antes de mandar tráfego por ele, e remova o antigo só depois de confirmar o caminho novo. Entradas sobrepostas não custam nada e evitam uma indisponibilidade que você mesmo causou.
Uma requisição recusada
Uma requisição corretamente assinada, vinda de um endereço fora da lista, é recusada com 403 e
IP_NOT_ALLOWED, antes de chegar ao recurso.
A lista só é avaliada depois que a assinatura confere. Uma requisição com assinatura inválida é recusada como falha de autenticação, e nunca revela se o endereço dela seria permitido.
IP de saída fixo por provedor
O padrão é o mesmo em todo provedor: coloque o workload numa sub-rede privada, sem endereço público próprio, e roteie a saída dele por um componente NAT gerenciado que tenha um endereço público reservado. Esse endereço é o que você registra.
| Provedor | Componente | Endereço a reservar |
|---|---|---|
| AWS | NAT Gateway | Elastic IP |
| Azure | NAT Gateway | IP público estático (SKU Standard) |
| Google Cloud | Cloud NAT + Cloud Router | IP externo regional reservado |
| Oracle Cloud | NAT Gateway | IP público reservado |
Runtimes serverless e de container gerenciado saem por pools do provedor por padrão, então o endereço de saída deles não é estável. A maioria pode ser anexada à sua VPC ou VNet, e aí passa a seguir o caminho de NAT acima.
- AWS
- Azure
- Google Cloud
- Oracle Cloud
# Pré-requisitos: uma VPC com sub-rede pública roteada para um Internet Gateway,
# e o workload numa sub-rede privada, sem IP público próprio.
export AWS_REGION=<region>
# 1. reserve the address — note AllocationId and PublicIp
aws ec2 allocate-address --domain vpc
# 2. create the NAT Gateway in the PUBLIC subnet, using that address
aws ec2 create-nat-gateway \
--subnet-id <public-subnet-id> \
--allocation-id <allocation-id>
aws ec2 wait nat-gateway-available --nat-gateway-ids <nat-gateway-id>
# 3. send the private subnet's outbound traffic through it
# (use replace-route if a 0.0.0.0/0 route already exists)
aws ec2 create-route \
--route-table-id <private-route-table-id> \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id <nat-gateway-id>
# 4. confirm the reserved address...
aws ec2 describe-addresses --allocation-ids <allocation-id> \
--query 'Addresses[0].PublicIp' --output text
# 5. ...and that the workload actually egresses through it
curl --fail --silent https://ifconfig.me
# NAT Gateway requires a Standard SKU public IP.
# 1. reserve the address
az network public-ip create \
--resource-group <resource-group> \
--name lbpay-egress \
--location <location> \
--sku Standard \
--allocation-method Static
# 2. create the NAT Gateway with it
az network nat gateway create \
--resource-group <resource-group> \
--name lbpay-nat \
--location <location> \
--public-ip-addresses lbpay-egress
# 3. attach it to the subnet where the workload runs
az network vnet subnet update \
--resource-group <resource-group> \
--vnet-name <vnet> \
--name <subnet> \
--nat-gateway lbpay-nat
# 4. confirm the reserved address...
az network public-ip show --resource-group <resource-group> \
--name lbpay-egress --query ipAddress --output tsv
# 5. ...and that the workload actually egresses through it
curl --fail --silent https://ifconfig.me
# Instances must have no external IP, otherwise they bypass Cloud NAT
# and egress through their own address.
gcloud config set project <project-id>
# 1. reserve a regional external address
gcloud compute addresses create lbpay-egress --region=<region>
# 2. reuse an existing Cloud Router in this network and region, or create one
gcloud compute routers create lbpay-router \
--network=<vpc-network> --region=<region>
# 3. pin Cloud NAT to the reserved address.
# Use <subnet>:ALL to also cover secondary ranges — GKE pods need this.
gcloud compute routers nats create lbpay-nat \
--router=lbpay-router --region=<region> \
--nat-custom-subnet-ip-ranges=<subnet> \
--nat-external-ip-pool=lbpay-egress
# 4. confirm the reserved address...
gcloud compute addresses describe lbpay-egress \
--region=<region> --format='value(address)'
# 5. ...and that the workload actually egresses through it
curl --fail --silent https://ifconfig.me
Cloud NAT is regional: repeat for every region you run in, and register each address.
# 1. reserve a public IP so the address survives a gateway recreation
oci network public-ip create \
--compartment-id <compartment-ocid> \
--lifetime RESERVED \
--display-name lbpay-egress
# 2. create the NAT Gateway with it
oci network nat-gateway create \
--compartment-id <compartment-ocid> \
--vcn-id <vcn-ocid> \
--display-name lbpay-nat \
--public-ip-id <reserved-public-ip-ocid>
# 3. read the CURRENT route rules first — the update replaces the whole
# set, so anything you omit is deleted
oci network route-table get --rt-id <route-table-ocid> \
--query 'data."route-rules"'
# 4. resend the existing rules plus the new one
oci network route-table update --rt-id <route-table-ocid> \
--route-rules '[<existing-rules>,
{"cidrBlock":"0.0.0.0/0",
"networkEntityId":"<nat-gateway-ocid>"}]'
# 5. confirm the gateway address...
oci network nat-gateway get --nat-gateway-id <nat-gateway-ocid> \
--query 'data."nat-ip"'
# 6. ...and that the workload actually egresses through it
curl --fail --silent https://ifconfig.me
Ler o endereço no provedor só prova que ele está reservado. Só o curl de dentro do workload prova
que o seu tráfego realmente sai por ele. Os dois têm que devolver o mesmo IPv4.
Quando você não tem endereço fixo
Plataformas sem anexação a VPC, runtimes de terceiros cuja rede você não controla, ou links com endereçamento dinâmico: canalize só o tráfego destinado à nossa API por um único ponto fixo que seja seu: uma instância pequena com endereço público reservado, numa rede que você controla.
your workload (rotating address)
│
▼
egress proxy (fixed address — the one you register)
│
▼
LB Pay API
Quatro requisitos:
- Continue assinando na sua aplicação. O proxy encaminha tráfego; ele nunca deve guardar sua chave privada nem reassinar nada.
- Não termine TLS sem um motivo específico. Encaminhar a conexão cifrada mantém o proxy fora do escopo do conteúdo das requisições, e um proxy que altera a requisição quebra a assinatura.
- Restrinja quem pode usá-lo. Do nosso lado o proxy é uma origem confiável, então ele deve aceitar conexões só dos seus workloads e encaminhar só para a nossa API.
- Faça-o redundante. Um proxy só é ponto único de falha para toda integração atrás dele. Duas instâncias, cada uma com seu endereço reservado, ambas registradas.
# TCP passthrough — forwards the encrypted stream untouched.
stream {
resolver 1.1.1.1;
server {
listen 443;
ssl_preread on;
proxy_pass api.sdb.lbpay.com.br:443;
}
}
Nem todo runtime respeita HTTPS_PROXY automaticamente. Alguns exigem o proxy configurado
explicitamente no cliente HTTP. Confirme com a conferência de saída, rodada pelo mesmo cliente que a
sua integração usa.
Diagnóstico
Quase toda falha tem a mesma causa raiz: o endereço de onde você acha que sai não é o endereço que nós vemos.
| Sintoma | Causa provável | O que fazer |
|---|---|---|
IP_NOT_ALLOWED logo após registrar | Você registrou um endereço de entrada: load balancer, CDN, hostname ou o seu próprio navegador | Rode a conferência de saída de dentro do workload e compare |
IP_NOT_ALLOWED só em algumas requisições | O workload sai por um pool rotativo do provedor | Roteie a saída por um NAT gateway na sua VPC, ou por um proxy dedicado |
| A aplicação funciona, o job em lote falha | O segundo workload roda em outra sub-rede ou conta, com saída própria | Registre o endereço de saída de cada chamador na mesma credencial |
| Falhas só durante failover regional | A região de failover sai por um endereço que nunca foi registrado | Registre os endereços de failover antes, não durante o incidente |
| Erros de assinatura depois de introduzir um proxy | O proxy termina TLS e altera a requisição | Encaminhe a conexão intacta; nunca reassine no proxy |
| O endereço mudou sozinho | Ele era efêmero, não reservado | Reserve o endereço explicitamente e prenda o componente NAT a ele |
Antes de ir para produção
- Mapeie todo sistema que nos chama, inclusive jobs em lote, ferramentas internas e terceiros agindo em seu nome.
- Descubra o endereço de saída real de cada um, de dentro do workload.
- Dê um caminho de saída fixo a tudo que não tiver um.
- Registre todos os endereços resultantes, failover incluído.
- Faça no sandbox primeiro, confirme que a integração continua funcionando, depois repita em produção.