Aditiva de proposito: nenhum endereco antigo sai. Quem esta usando
<tenant>.athleticmap.influxdigital.com.br continua entrando, e o cert-manager
passa a emitir tambem o certificado de <tenant>.athleticmap.com.
Foi possivel porque o dono criou o curinga hoje: *.athleticmap.com resolve para
187.77.37.184, conferido com um nome ALEATORIO — demo. ou www. responderiam
mesmo sem curinga, e a conferencia passaria por engano.
9 arquivos, 28 trocas, 317 insercoes e 4 remocoes. As 4 sao as linhas de TLS em
fluxo, reescritas com os dois dominios na mesma lista.
Cada regra de Ingress foi DUPLICADA com os mesmos paths, e nao trocada: uma
regra com host: X so atende X.
`platform/` (ArgoCD e Grafana) fica FORA: mexer no Ingress do ArgoCD no meio de
uma migracao de dominio e perder o instrumento de rollback.
Conferido antes de aplicar: YAML valido em todos os Ingress, e ZERO host sem
entrada de TLS.
O que NAO muda aqui: ATM_ISSUER, ATM_KC_URL e as URLs do Garmin. Ninguem e
deslogado nesta fase.
O clone e feito como root e o container principal roda como o usuario app
(uid 999). Sem o chown, a copia de trabalho fica so-leitura para ele, e a etapa
MANIFESTOS_GERADOS falharia ao escrever tenants/<nome> — medido de dentro do pod
logo depois do primeiro deploy: "escrita: NEGADA".
chown, e nao fsGroup: fsGroup ajusta o GRUPO e depende de o diretorio ter
permissao de grupo, o que o git nao garante arquivo a arquivo. Entregar a posse
e explicito e nao depende de umask.
Era a primeira etapa do provisionamento e a unica bloqueada por algo MEU, nao
por credencial do dono: o pod nao enxergava o molde nem o repositorio GitOps, e
as tres propriedades ficavam vazias. Falhar fechado, nomeando a propriedade, era
o comportamento certo — o que faltava era o material chegar.
Um initContainer clona o repositorio em /gitops na subida. Dentro dele,
moldes/consolidado e o molde e tenants/<nome> e onde a etapa escreve: UM volume
serve aos dois, e por isso o molde foi para o repositorio GitOps no commit
anterior.
Tres decisoes que valem registro:
- CLONE ANONIMO. O Gitea aceita leitura sem credencial neste repositorio, e
leitura e tudo o que a etapa 1 precisa. Empurrar e a etapa ARGO_SINCRONIZADO,
que depende de credencial e continua bloqueada — esta mudanca nao a destrava e
nao tenta;
- emptyDir, e nao PVC. A copia e descartavel por desenho: se o pod morrer no
meio de um provisionamento, o que ficou pela metade some junto. Melhor do que
um clone velho e divergente sobrevivendo entre reinicios;
- a imagem e a do act_runner, que JA esta no no e tem git 2.45.2. Nao ha
registry aqui, entao construir uma imagem so para ter git custaria mais do que
reusar esta.
O endereco e o interno (gitea-http.gitea.svc:3000), o mesmo do ApplicationSet.
Depende do 19-netpol-gitea.yaml do commit anterior.
A etapa MANIFESTOS_GERADOS renderiza o molde para tenants/<nome> numa copia de
trabalho, e o pod obtem essa copia clonando o Gitea na subida. Clonar e leitura.
Escrever continua fora de alcance, de proposito: empurrar no ramo padrao E o
deploy, e depende de uma credencial que ainda nao existe.
O Gitea aceita clone ANONIMO deste repositorio (conferido com git ls-remote sem
credencial), entao a etapa 1 funciona antes de a credencial de escrita existir.
A MESMA armadilha de rede, pela TERCEIRA vez. De dentro do pod, antes desta
politica: HTTP 000, exit 7. O ipBlock 10.43.0.0/16 da deny-cross-tenant cobre o
ClusterIP do Gitea e nao adianta — o k3s avalia DEPOIS do DNAT, e ali o destino
ja e o IP do POD.
Aconteceu com a API do Kubernetes em 20/09, com o sealed-secrets hoje mais cedo,
e agora aqui. E regra deste cluster, nao coincidencia.
Estreita: so o servico-captacao, so o POD do Gitea por rotulo (namespaceSelector
sozinho abriria a 3000 do namespace inteiro, onde tambem vivem o Valkey e o
runner de CI), so a 3000, so saida.
Os quatro tenants foram corrigidos mais cedo hoje; o plataforma nao. O defeito
so apareceu agora, e por um motivo que vale registrar: ele e o unico SEM
catch-all /api. Nos outros, uma rota sem linha propria era engolida pelo
backend legado; aqui ela cai no frontend — e e ali que o bug estava esperando.
Medido no log do proprio pod:
servico-bff could not be resolved (3: Host not found)
request: "POST /api/plataforma/provisionamentos"
O resolver do nginx nao aplica os search do /etc/resolv.conf, entao nome curto
nao resolve. As cinco variaveis estavam vazias, e toda ausente cai em
ATM_API_URL, que cai no nome curto `servico-bff`.
Aqui nao ha runtime consolidado: os destinos apontam para o BFF, que ja era o
padrao — a diferenca e so o FQDN.
Achado ao conferir o PLATAFORMA_ADMIN_TOKEN recem-ligado: a consulta de estado
devolvia 502 em vez de 401, e o 502 era isto.
Vai DEPOIS do commit que selou os Secrets, e nao junto: secretKeyRef de Secret
ausente impede o pod de subir. Os dois ja materializaram no cluster.
PLATAFORMA_WEBHOOK_TOKEN destrava a etapa que recebia o aviso de assinatura e
recusava tudo por falta de configuracao. Falta CADASTRAR o mesmo valor no painel
do Asaas, na conta da Athletic Map — quem for cadastrar le uma vez com:
kubectl -n plataforma-prod get secret plataforma-webhook-token \
-o jsonpath='{.data.token}' | base64 -d
PLATAFORMA_ADMIN_TOKEN destrava a consulta de estado do provisionamento, que e
interina e vive no cabecalho x-atm-admin-token.
Ao conferir que os Secrets tinham materializado, o comando imprimiu o campo
`.data` inteiro — ou seja, parte do valor em base64 dos dois tokens foi parar na
conversa. Curto, e o bastante para nao dar por confiavel.
Regerados. Custo zero: nada os referenciava ainda (nenhum Deployment, e o
webhook nao estava cadastrado no Asaas), entao nao houve janela de uso.
A licao, que ja valia para senha e vale igual para Secret: conferir existencia
com `-o name`, e nunca com `-o custom-columns=...:.data` nem `-o yaml`. O que se
quer saber e SE existe, nao O QUE e.
PLATAFORMA_ADMIN_TOKEN e PLATAFORMA_WEBHOOK_TOKEN. Os dois sao gerados aqui, ao
contrario da api-key do Asaas, que vem do painel do provedor.
O valor nasceu, foi selado e morreu dentro de um unico shell no servidor: nao
foi ecoado, nao foi para arquivo, nao entrou no historico e nao apareceu em ps.
O que se commita e o cifrado, que e publico por natureza.
Vao SOZINHOS neste commit, sem a referencia no Deployment: secretKeyRef de
Secret ausente impede o pod de subir. Primeiro o Secret materializa, depois o
env aponta para ele.
O webhook-token precisa ser CADASTRADO no painel do Asaas com o mesmo valor. Ele
pode ser lido uma vez, por quem for cadastrar:
kubectl -n plataforma-prod get secret plataforma-webhook-token \
-o jsonpath='{.data.token}' | base64 -d
O Ingress do plataforma nao tem catch-all, por desenho: rota publica nova
precisa ser acrescentada a mao, ou devolve 404. Duas estavam faltando, as duas
achadas por scripts/conferir_rotas_publicas.py.
/api/public/conta (P5.8) — a tela de espera depois do pagamento. Quem acabou de
pagar AINDA NAO TEM CONTA, logo nao tem token. O controlador existia
(EstadoDaContaController) e a cadeia ja liberava (permitAll), mas o Ingress
devolvia 404: a tela que existe para tranquilizar quem pagou nao respondia.
/api/public/webhooks/assinatura (P5.1) — e outra coisa do webhooks/asaas que ja
estava la: aquele avisa que uma COBRANCA de cliente foi paga e vai para o
financeiro do tenant; este avisa que alguem assinou a Athletic Map, e dispara o
provisionamento de um tenant NOVO. Contas diferentes no Asaas, servicos
diferentes aqui. Sem esta linha, alguem assinaria, pagaria, e nenhum tenant
seria criado — sem erro em lugar nenhum do nosso lado.
O ipBlock da deny-cross-tenant nao vale para destino em outro namespace (o k3s
avalia depois do DNAT), entao a variavel sozinha daria FalhaTemporaria. Vai
junto a netpol estreita: so o servico-captacao, so o pod do controlador, so a
porta 8080, so saida.
O pod resolvia api.asaas.com e a conexao TCP morria em 5ms. O deny-cross-tenant
fecha egress e nao tinha regra para IP publico. O except das faixas privadas
preserva o isolamento entre tenants, que e o proposito da politica.
O realm do molde nao tinha os papeis que o SecurityConfig dos servicos verifica.
Acrescentados ao ConfigMap de import e a copia em realm/. Nao afeta o realm que
ja existe — o Keycloak so importa realm inexistente.