KB-INFRA-005: GLPI 11 subcaminho root_doc="" quebra rotas Symfony (form builder)
Salvar/criar formulario no form builder nativo falhava com 404 do Traefik porque root_doc vazio faz o JS chamar /GenericAjaxCrud na raiz, fora do allowlist. Documenta causa-raiz, correcao (allowlist Traefik+nginx) e fix de raiz. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a0c30bfc65
commit
5e5a467f07
2 changed files with 131 additions and 1 deletions
23
index.json
23
index.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"version": "1.0.0",
|
||||
"last_updated": "2026-07-13",
|
||||
"last_updated": "2026-07-15",
|
||||
"records": [
|
||||
{
|
||||
"id": "KB-INFRA-001",
|
||||
|
|
@ -69,6 +69,27 @@
|
|||
"path": "records/infrastructure/KB-INFRA-004-forgejo-onmind-dev-origin.md",
|
||||
"summary": "id: KB-INFRA-004"
|
||||
},
|
||||
{
|
||||
"id": "KB-INFRA-005",
|
||||
"title": "GLPI 11 em subcaminho com root_doc vazio quebra rotas Symfony (form builder nao salva)",
|
||||
"domain": "infrastructure",
|
||||
"tags": [
|
||||
"glpi",
|
||||
"glpi11",
|
||||
"docker",
|
||||
"swarm",
|
||||
"traefik",
|
||||
"nginx",
|
||||
"reverse-proxy",
|
||||
"root_doc",
|
||||
"symfony-routes",
|
||||
"allowlist"
|
||||
],
|
||||
"status": "active",
|
||||
"severity": "critical",
|
||||
"path": "records/infrastructure/KB-INFRA-005-glpi11-subpath-root_doc-symfony-routes-allowlist.md",
|
||||
"summary": "id: KB-INFRA-005"
|
||||
},
|
||||
{
|
||||
"id": "KB-PLUGIN-001",
|
||||
"title": "Instalacao de plugin GLPI requer callbacks install/uninstall",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
---
|
||||
id: KB-INFRA-005
|
||||
title: GLPI 11 em subcaminho com root_doc vazio quebra rotas Symfony (form builder nao salva)
|
||||
domain: infrastructure
|
||||
tags:
|
||||
- glpi
|
||||
- glpi11
|
||||
- docker
|
||||
- swarm
|
||||
- traefik
|
||||
- nginx
|
||||
- reverse-proxy
|
||||
- root_doc
|
||||
- symfony-routes
|
||||
- allowlist
|
||||
status: active
|
||||
severity: critical
|
||||
created_at: 2026-07-15
|
||||
updated_at: 2026-07-15
|
||||
applies_to:
|
||||
- GLPI 11.0.x atras de reverse proxy em subcaminho (ex.: /glpi)
|
||||
- Stack devportal (Traefik + nginx glpi-proxy) em /opt/mindtek-dev
|
||||
- Qualquer deploy que sirva o GLPI sob um path prefix removendo o prefixo antes do PHP
|
||||
related_records:
|
||||
- KB-INFRA-001
|
||||
- KB-INFRA-002
|
||||
---
|
||||
|
||||
# Contexto
|
||||
O GLPI 11 e exposto em subcaminho (`https://host/glpi`), mas o proxy remove o prefixo
|
||||
antes de entregar ao PHP. O construtor de formularios nativo passou a nao salvar nem
|
||||
criar nenhum formulario.
|
||||
|
||||
# Sintoma observavel
|
||||
- Salvar/criar QUALQUER formulario no form builder falha silenciosamente.
|
||||
- Nada nos logs do servidor (`php-errors.log`, `sql-errors.log` vazios) — indicio de erro que nem chega no PHP.
|
||||
- No console do browser: AJAX de `common_ajax_controller.js` (`#handleFormSubmit`) com
|
||||
`status: 404` e `responseText: "404 page not found\n"`.
|
||||
- O texto `404 page not found` (minusculo, plain text) e o 404 padrao do **Go/Traefik**,
|
||||
NAO a pagina 404 (HTML) do GLPI. Ou seja: o request nao foi roteado, morreu no proxy.
|
||||
|
||||
# Causa raiz
|
||||
Cadeia: browser -> Traefik -> nginx (glpi-proxy) -> GLPI.
|
||||
|
||||
1. O nginx faz `proxy_pass http://glpi/` (com barra final) na `location /glpi/`, o que
|
||||
**remove** o prefixo `/glpi`. O GLPI recebe o request na raiz.
|
||||
2. `Config::loadLegacyConfiguration()` computa, no contexto web,
|
||||
`root_doc = Request::createFromGlobals()->getBasePath()` (`src/Config.php` ~1398).
|
||||
Sem o prefixo no request, `getBasePath()` retorna `""` -> **`root_doc = ""`**.
|
||||
3. Com `root_doc=""`, o JS do GLPI monta as URLs de AJAX na RAIZ do dominio:
|
||||
`${CFG_GLPI.root_doc}/GenericAjaxCrud` = `/GenericAjaxCrud` (sem `/glpi`).
|
||||
4. O GLPI NAO honra `X-Forwarded-Prefix` (nao ha `Request::setTrustedProxies()` no codigo),
|
||||
entao mesmo o nginx enviando o header, `root_doc` continua vazio.
|
||||
5. O acesso ao GLPI e liberado por **allowlist de paths de topo** em DOIS lugares que
|
||||
precisam casar (ver KB-INFRA-002): a regra Traefik `dev-glpi-root.rule` (labels do
|
||||
servico `glpi-proxy`) e a regex `location ~ ^/(...)` em `nginx.conf`. Rotas legadas
|
||||
(`/front`, `/ajax`, ...) estavam no allowlist e funcionavam; as rotas **novas de topo
|
||||
do GLPI 11 (Symfony)** — em especial `GenericAjaxCrud`, usada pelo save do form builder
|
||||
— NAO estavam -> 404 do Traefik.
|
||||
|
||||
Por isso o form builder (controlador Symfony generico) quebrava, enquanto o resto do GLPI
|
||||
(AJAX legado sob `/ajax`, `/front`) funcionava.
|
||||
|
||||
# Correcao aplicada
|
||||
Adicionar as rotas de topo do GLPI 11 ao allowlist nos DOIS lugares.
|
||||
|
||||
Enumerar as rotas de topo:
|
||||
```bash
|
||||
grep -rhoE "#\[Route\(\s*[\"']/[A-Za-z0-9_]+" src/Glpi/Controller/ \
|
||||
| sed -E "s/.*['\"]\/([A-Za-z0-9_]+).*/\1/" | sort -u
|
||||
# -> Central Form GenericAjaxCrud Helpdesk Install Inventory front progress
|
||||
```
|
||||
|
||||
1. `nginx.conf` — regex do `location`:
|
||||
```
|
||||
location ~ ^/(index\.php|front|ajax|...|build|Central|Form|GenericAjaxCrud|Helpdesk|Inventory|progress)(/|$) {
|
||||
```
|
||||
2. `stack.yml` — label `traefik.http.routers.dev-glpi-root.rule`, acrescentar:
|
||||
```
|
||||
|| PathPrefix(`/Central`) || PathPrefix(`/Form`) || PathPrefix(`/GenericAjaxCrud`)
|
||||
|| PathPrefix(`/Helpdesk`) || PathPrefix(`/Inventory`) || PathPrefix(`/progress`)
|
||||
```
|
||||
3. Aplicar: `sudo docker stack deploy -c stack.yml devportal` (atualiza labels Traefik) +
|
||||
`docker exec <glpi-proxy> nginx -s reload` (recarrega o bind-mount do nginx.conf).
|
||||
|
||||
`Install` deixado de fora por seguranca (nao expor o instalador na raiz).
|
||||
|
||||
# Validacao padrao
|
||||
```bash
|
||||
# Antes: 404 "404 page not found" (Traefik). Depois: 403/302 com HTML do GLPI (chegou no GLPI).
|
||||
curl -sk -o /dev/null -w "%{http_code}\n" -X POST https://host/GenericAjaxCrud # -> 403
|
||||
curl -sk -o /dev/null -w "%{http_code}\n" https://host/Form/Render/1 # -> 302
|
||||
curl -sk https://host/rota-inexistente | head -c 40 # -> "404 page not found" (controle: Traefik OK)
|
||||
```
|
||||
No browser: recarregar (Ctrl+Shift+R) e salvar um formulario -> deve funcionar.
|
||||
|
||||
# Regra para agentes
|
||||
- 404 com corpo `404 page not found` (plain text) = proxy/Traefik nao roteou; 404 HTML = GLPI roteou mas nao achou a rota. Distinga SEMPRE pelo corpo antes de culpar a app ou um plugin.
|
||||
- GLPI 11 em subcaminho depende de allowlist manual das rotas de topo (Symfony) em Traefik + nginx. A cada upgrade do GLPI, reenumerar as rotas de `src/Glpi/Controller` e ressincronizar as duas allowlists.
|
||||
- Isto NAO e problema de plugin. Ao ver "form nao salva" + logs de servidor limpos, checar o console do browser e o roteamento do proxy primeiro.
|
||||
|
||||
# Fix de raiz (recomendado a longo prazo)
|
||||
Eliminar o subcaminho: servir o GLPI num subdominio proprio na raiz (ex.: `glpi.host`),
|
||||
onde `root_doc=""` passa a ser correto e nao ha allowlist a manter. Alternativa: wire de
|
||||
trusted proxies para honrar `X-Forwarded-Prefix` e ter `root_doc=/glpi` consistente.
|
||||
|
||||
# Classificacao
|
||||
- Tipo: Armadilha de deploy do GLPI 11 atras de reverse proxy em subcaminho.
|
||||
- Reutilizacao: obrigatoria em qualquer ambiente que sirva GLPI 11 sob path prefix.
|
||||
Loading…
Reference in a new issue