mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
fix(sys-front-inj-all): move body_filter Lua to *_by_lua_file to fix prometheus vhost long-bracket parse error
OpenResty's inline *_by_lua_block parser is the nginx config parser; it
can't reliably handle Lua long brackets ([=[...]=]) embedded in large
rendered snippets. The prometheus role's javascript.js.j2 (logout-icon
DOM-mutation script) collapses to a ~3.4 KB one-liner wrapped in
head_snippets[#... + 1] = [=[<huge_JS>]=], which trips the parser with
"missing the closing long bracket "]=]", the inlined Lua code may be too
long" — the wording is OpenResty's own hint to switch to the file form.
Rendering the body_filter as an external per-app Lua file routes through
Lua's native parser instead, eliminating the limit.
- plugins/lookup/nginx.py: expose <conf_dir>/lua/ as
directories.configuration.lua + add to directories.ensure so
sys-svc-webserver-core auto-creates it
- roles/sys-front-inj-all/templates/body_filter.lua.j2: new — pure Lua
body (formerly inside body_filter_by_lua_block { ... }) plus the
push_snippets Jinja macro
- roles/sys-front-inj-all/templates/location.lua.j2: replace inline
body_filter_by_lua_block with body_filter_by_lua_file pointing at the
per-app .lua
- roles/sys-front-inj-all/tasks/main.yml: render the per-app Lua file
after the feature include_roles (those set the *_code_one_liner vars
the sub.j2 templates depend on); notify restart openresty
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
1 parent
3d05803dc7
commit
55c1bceeb1
4 files changed
+87
-78
No files matched your search
@@ -117,6 +117,7 @@ class LookupModule(LookupBase):
|
||||
servers_https_dir = _ensure_trailing_slash(_join(servers_dir, "https"))
|
||||
maps_dir = _ensure_trailing_slash(_join(conf_dir, "maps"))
|
||||
streams_dir = _ensure_trailing_slash(_join(conf_dir, "streams"))
|
||||
lua_dir = _ensure_trailing_slash(_join(conf_dir, "lua"))
|
||||
|
||||
data_html_dir = _ensure_trailing_slash(_join(www_dir, "public_html"))
|
||||
data_files_dir = _ensure_trailing_slash(_join(www_dir, "public_files"))
|
||||
@@ -136,6 +137,7 @@ class LookupModule(LookupBase):
|
||||
_dir_spec(servers_https_dir, "0755"),
|
||||
_dir_spec(maps_dir, "0755"),
|
||||
_dir_spec(streams_dir, "0755"),
|
||||
_dir_spec(lua_dir, "0755"),
|
||||
_dir_spec(www_dir, "0755"),
|
||||
_dir_spec(data_html_dir, "0755"),
|
||||
_dir_spec(data_files_dir, "0755"),
|
||||
@@ -156,6 +158,7 @@ class LookupModule(LookupBase):
|
||||
"servers": servers_dir,
|
||||
"maps": maps_dir,
|
||||
"streams": streams_dir,
|
||||
"lua": lua_dir,
|
||||
"http_includes": [
|
||||
global_dir,
|
||||
maps_dir,
|
||||
|
||||
@@ -45,3 +45,10 @@
|
||||
include_role:
|
||||
name: sys-front-inj-javascript
|
||||
when: inj_enabled.javascript
|
||||
|
||||
- name: "Render body_filter Lua for '{{ application_id }}'"
|
||||
ansible.builtin.template:
|
||||
src: body_filter.lua.j2
|
||||
dest: "{{ lookup('nginx', 'directories.configuration.lua') }}{{ application_id }}.lua"
|
||||
mode: "0644"
|
||||
notify: restart openresty
|
||||
@@ -0,0 +1,76 @@
|
||||
{# Jinja macro: expands feature snippets into Lua array pushes at render time #}
|
||||
{% macro push_snippets(list_name, features) -%}
|
||||
{% set kind = list_name | regex_replace('_snippets$','') %}
|
||||
{% for f in features if inj_enabled.get(f) -%}
|
||||
{{ list_name }}[#{{ list_name }} + 1] = [=[
|
||||
{%- include 'roles/sys-front-inj-' ~ f ~ '/templates/' ~ kind ~ '_sub.j2' -%}
|
||||
]=]
|
||||
{% endfor -%}
|
||||
{%- endmacro %}
|
||||
|
||||
-- Only process HTML responses
|
||||
if not ngx.ctx.is_html then
|
||||
return
|
||||
end
|
||||
|
||||
-- Buffer all chunks until EOF
|
||||
ngx.ctx.buf = ngx.ctx.buf or {}
|
||||
local chunk, eof = ngx.arg[1], ngx.arg[2]
|
||||
|
||||
if chunk ~= "" then
|
||||
table.insert(ngx.ctx.buf, chunk)
|
||||
end
|
||||
|
||||
if not eof then
|
||||
-- Swallow intermediate chunks; emit once at EOF
|
||||
ngx.arg[1] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Concatenate the full HTML
|
||||
local whole = table.concat(ngx.ctx.buf)
|
||||
ngx.ctx.buf = nil
|
||||
|
||||
-- Remove inline CSP <meta http-equiv="Content-Security-Policy"> (case-insensitive)
|
||||
local meta_re = [[<meta[^>]+http-equiv=["']Content-Security-Policy["'][^>]*>\s*]]
|
||||
whole = ngx.re.gsub(whole, meta_re, "", "ijo")
|
||||
|
||||
-- Build head snippets (rendered by Jinja at template time)
|
||||
local head_snippets = {}
|
||||
{{ push_snippets('head_snippets', inj_head_features) }}
|
||||
local head_payload = table.concat(head_snippets, "\n") .. "</head>"
|
||||
|
||||
-- Inject before </head> (first occurrence)
|
||||
local function repl_head(_) return head_payload end
|
||||
local new, n, err = ngx.re.sub(whole, [[</head\s*>]], repl_head, "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.WARN, "No </head> found; trying <body> fallback: ", err or "nil")
|
||||
-- Fallback: inject right AFTER the opening <body ...> tag
|
||||
local body_open_re = [[<body\b[^>]*>]]
|
||||
new, n, err = ngx.re.sub(whole, body_open_re, "$0\n" .. table.concat(head_snippets, "\n"), "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.ERR, "Head-fallback failed: ", err or "nil")
|
||||
end
|
||||
end
|
||||
|
||||
-- Build body snippets (rendered by Jinja at template time)
|
||||
local body_snippets = {}
|
||||
{{ push_snippets('body_snippets', inj_body_features) }}
|
||||
local body_payload = table.concat(body_snippets, "\n") .. "</body>"
|
||||
|
||||
-- Inject before </body> (first occurrence), or append if missing
|
||||
local function repl_body(_) return body_payload end
|
||||
new, n, err = ngx.re.sub(whole, [[</body\s*>]], repl_body, "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.WARN, "No </body> found; appending body snippets at end: ", err or "nil")
|
||||
whole = whole .. table.concat(body_snippets, "\n")
|
||||
end
|
||||
|
||||
-- Emit the modified HTML
|
||||
ngx.arg[1] = whole or ""
|
||||
@@ -1,80 +1,3 @@
|
||||
{# Jinja macro: expands feature snippets into Lua array pushes at render time #}
|
||||
{% macro push_snippets(list_name, features) -%}
|
||||
{% set kind = list_name | regex_replace('_snippets$','') %}
|
||||
{% for f in features if inj_enabled.get(f) -%}
|
||||
{{ list_name }}[#{{ list_name }} + 1] = [=[
|
||||
{%- include 'roles/sys-front-inj-' ~ f ~ '/templates/' ~ kind ~ '_sub.j2' -%}
|
||||
]=]
|
||||
{% endfor -%}
|
||||
{%- endmacro %}
|
||||
|
||||
lua_need_request_body on;
|
||||
|
||||
body_filter_by_lua_block {
|
||||
-- Only process HTML responses
|
||||
if not ngx.ctx.is_html then
|
||||
return
|
||||
end
|
||||
|
||||
-- Buffer all chunks until EOF
|
||||
ngx.ctx.buf = ngx.ctx.buf or {}
|
||||
local chunk, eof = ngx.arg[1], ngx.arg[2]
|
||||
|
||||
if chunk ~= "" then
|
||||
table.insert(ngx.ctx.buf, chunk)
|
||||
end
|
||||
|
||||
if not eof then
|
||||
-- Swallow intermediate chunks; emit once at EOF
|
||||
ngx.arg[1] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Concatenate the full HTML
|
||||
local whole = table.concat(ngx.ctx.buf)
|
||||
ngx.ctx.buf = nil
|
||||
|
||||
-- Remove inline CSP <meta http-equiv="Content-Security-Policy"> (case-insensitive)
|
||||
local meta_re = [[<meta[^>]+http-equiv=["']Content-Security-Policy["'][^>]*>\s*]]
|
||||
whole = ngx.re.gsub(whole, meta_re, "", "ijo")
|
||||
|
||||
-- Build head snippets (rendered by Jinja at template time)
|
||||
local head_snippets = {}
|
||||
{{ push_snippets('head_snippets', inj_head_features) }}
|
||||
local head_payload = table.concat(head_snippets, "\n") .. "</head>"
|
||||
|
||||
-- Inject before </head> (first occurrence)
|
||||
local function repl_head(_) return head_payload end
|
||||
local new, n, err = ngx.re.sub(whole, [[</head\s*>]], repl_head, "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.WARN, "No </head> found; trying <body> fallback: ", err or "nil")
|
||||
-- Fallback: inject right AFTER the opening <body ...> tag
|
||||
local body_open_re = [[<body\b[^>]*>]]
|
||||
new, n, err = ngx.re.sub(whole, body_open_re, "$0\n" .. table.concat(head_snippets, "\n"), "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.ERR, "Head-fallback failed: ", err or "nil")
|
||||
end
|
||||
end
|
||||
|
||||
-- Build body snippets (rendered by Jinja at template time)
|
||||
local body_snippets = {}
|
||||
{{ push_snippets('body_snippets', inj_body_features) }}
|
||||
local body_payload = table.concat(body_snippets, "\n") .. "</body>"
|
||||
|
||||
-- Inject before </body> (first occurrence), or append if missing
|
||||
local function repl_body(_) return body_payload end
|
||||
new, n, err = ngx.re.sub(whole, [[</body\s*>]], repl_body, "ijo")
|
||||
if new then
|
||||
whole = new
|
||||
else
|
||||
ngx.log(ngx.WARN, "No </body> found; appending body snippets at end: ", err or "nil")
|
||||
whole = whole .. table.concat(body_snippets, "\n")
|
||||
end
|
||||
|
||||
-- Emit the modified HTML
|
||||
ngx.arg[1] = whole or ""
|
||||
}
|
||||
body_filter_by_lua_file {{ lookup('nginx', 'directories.configuration.lua') }}{{ application_id }}.lua;
|
||||
Reference in new issue
Block a user