diff --git a/plugins/lookup/nginx.py b/plugins/lookup/nginx.py index 51a6e09b5e..ef9f3f35e4 100644 --- a/plugins/lookup/nginx.py +++ b/plugins/lookup/nginx.py @@ -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, diff --git a/roles/sys-front-inj-all/tasks/main.yml b/roles/sys-front-inj-all/tasks/main.yml index 48d24e6f8d..58bbac4053 100644 --- a/roles/sys-front-inj-all/tasks/main.yml +++ b/roles/sys-front-inj-all/tasks/main.yml @@ -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 diff --git a/roles/sys-front-inj-all/templates/body_filter.lua.j2 b/roles/sys-front-inj-all/templates/body_filter.lua.j2 new file mode 100644 index 0000000000..582dcc98c7 --- /dev/null +++ b/roles/sys-front-inj-all/templates/body_filter.lua.j2 @@ -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 (case-insensitive) +local meta_re = [[]+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") .. "" + +-- Inject before (first occurrence) +local function repl_head(_) return head_payload end +local new, n, err = ngx.re.sub(whole, [[]], repl_head, "ijo") +if new then + whole = new +else + ngx.log(ngx.WARN, "No found; trying fallback: ", err or "nil") + -- Fallback: inject right AFTER the opening tag + local body_open_re = [[]*>]] + 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") .. "" + +-- Inject before (first occurrence), or append if missing +local function repl_body(_) return body_payload end +new, n, err = ngx.re.sub(whole, [[]], repl_body, "ijo") +if new then + whole = new +else + ngx.log(ngx.WARN, "No 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 "" diff --git a/roles/sys-front-inj-all/templates/location.lua.j2 b/roles/sys-front-inj-all/templates/location.lua.j2 index 604879d438..b1dd587f3e 100644 --- a/roles/sys-front-inj-all/templates/location.lua.j2 +++ b/roles/sys-front-inj-all/templates/location.lua.j2 @@ -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 (case-insensitive) - local meta_re = [[]+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") .. "" - - -- Inject before (first occurrence) - local function repl_head(_) return head_payload end - local new, n, err = ngx.re.sub(whole, [[]], repl_head, "ijo") - if new then - whole = new - else - ngx.log(ngx.WARN, "No found; trying fallback: ", err or "nil") - -- Fallback: inject right AFTER the opening tag - local body_open_re = [[]*>]] - 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") .. "" - - -- Inject before (first occurrence), or append if missing - local function repl_body(_) return body_payload end - new, n, err = ngx.re.sub(whole, [[]], repl_body, "ijo") - if new then - whole = new - else - ngx.log(ngx.WARN, "No 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;