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",
|
"version": "1.0.0",
|
||||||
"last_updated": "2026-07-13",
|
"last_updated": "2026-07-15",
|
||||||
"records": [
|
"records": [
|
||||||
{
|
{
|
||||||
"id": "KB-INFRA-001",
|
"id": "KB-INFRA-001",
|
||||||
|
|
@ -69,6 +69,27 @@
|
||||||
"path": "records/infrastructure/KB-INFRA-004-forgejo-onmind-dev-origin.md",
|
"path": "records/infrastructure/KB-INFRA-004-forgejo-onmind-dev-origin.md",
|
||||||
"summary": "id: KB-INFRA-004"
|
"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",
|
"id": "KB-PLUGIN-001",
|
||||||
"title": "Instalacao de plugin GLPI requer callbacks install/uninstall",
|
"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