diff --git a/index.json b/index.json index 098cba2..4f04544 100755 --- a/index.json +++ b/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", diff --git a/records/infrastructure/KB-INFRA-005-glpi11-subpath-root_doc-symfony-routes-allowlist.md b/records/infrastructure/KB-INFRA-005-glpi11-subpath-root_doc-symfony-routes-allowlist.md new file mode 100644 index 0000000..e697ec8 --- /dev/null +++ b/records/infrastructure/KB-INFRA-005-glpi11-subpath-root_doc-symfony-routes-allowlist.md @@ -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 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.