mirror of
https://github.com/openappsec/attachment.git
synced 2025-06-28 16:41:03 +03:00
Add kong plugin (#36)
* add kong plugin to open-appsec * fix url in rockspec file * add the attachment prefix to the paths * check * fix branch --------- Co-authored-by: wiaamm <wiaamm@checkpoint.com>
This commit is contained in:
parent
8f459a139b
commit
882dc4c187
171
attachments/kong/handler.lua
Executable file
171
attachments/kong/handler.lua
Executable file
@ -0,0 +1,171 @@
|
||||
local nano = require "kong.plugins.open-appsec-waf-kong-plugin.nano_ffi"
|
||||
local kong = kong
|
||||
|
||||
local NanoHandler = {}
|
||||
|
||||
NanoHandler.PRIORITY = 3000
|
||||
NanoHandler.VERSION = "1.0.0"
|
||||
|
||||
NanoHandler.sessions = {}
|
||||
NanoHandler.processed_requests = {} -- Track processed requests
|
||||
|
||||
-- **Handles worker initialization**
|
||||
function NanoHandler.init_worker()
|
||||
nano.init_attachment()
|
||||
end
|
||||
|
||||
-- **Handles Request Headers (DecodeHeaders Equivalent)**
|
||||
function NanoHandler.access(conf)
|
||||
local headers = kong.request.get_headers()
|
||||
local session_id = nano.generate_session_id()
|
||||
kong.service.request.set_header("x-session-id", tostring(session_id))
|
||||
|
||||
if NanoHandler.processed_requests[session_id] then
|
||||
kong.ctx.plugin.blocked = true
|
||||
return
|
||||
end
|
||||
|
||||
local session_data = nano.init_session(session_id)
|
||||
if not session_data then
|
||||
kong.log.err("Failed to initialize session")
|
||||
kong.ctx.plugin.blocked = true
|
||||
return kong.response.exit(500, { message = "Session Initialization Failed" })
|
||||
end
|
||||
|
||||
kong.ctx.plugin.session_data = session_data
|
||||
kong.ctx.plugin.session_id = session_id
|
||||
|
||||
local meta_data = nano.handle_start_transaction()
|
||||
local req_headers = nano.handleHeaders(headers)
|
||||
|
||||
local has_content_length = tonumber(ngx.var.http_content_length) and tonumber(ngx.var.http_content_length) > 0
|
||||
local contains_body = has_content_length and 1 or 0
|
||||
|
||||
local verdict, response = nano.send_data(session_id, session_data, meta_data, req_headers, contains_body, nano.HttpChunkType.HTTP_REQUEST_FILTER)
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
kong.ctx.plugin.blocked = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
|
||||
if contains_body == 1 then
|
||||
local body = kong.request.get_raw_body()
|
||||
if body and #body > 0 then
|
||||
verdict, response = nano.send_body(session_id, session_data, body, nano.HttpChunkType.HTTP_REQUEST_BODY)
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
kong.ctx.plugin.blocked = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
end
|
||||
|
||||
local ok, verdict, response = pcall(function()
|
||||
return nano.end_inspection(session_id, session_data, nano.HttpChunkType.HTTP_REQUEST_END)
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
kong.log.err("Error ending request inspection: ", verdict)
|
||||
nano.fini_session(session_data)
|
||||
kong.ctx.plugin.blocked = true
|
||||
return kong.response.exit(500, { message = "Error completing request processing" })
|
||||
end
|
||||
|
||||
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
kong.ctx.plugin.blocked = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
else
|
||||
verdict, response = nano.end_inspection(session_id, session_data, nano.HttpChunkType.HTTP_REQUEST_END)
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
kong.ctx.plugin.blocked = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
end
|
||||
|
||||
NanoHandler.processed_requests[session_id] = true
|
||||
end
|
||||
|
||||
function NanoHandler.header_filter(conf)
|
||||
local ctx = kong.ctx.plugin
|
||||
if ctx.blocked then
|
||||
return
|
||||
end
|
||||
|
||||
local session_id = ctx.session_id
|
||||
local session_data = ctx.session_data
|
||||
|
||||
if not session_id or not session_data then
|
||||
kong.log.err("No session data found in header_filter")
|
||||
return
|
||||
end
|
||||
|
||||
local headers = kong.response.get_headers()
|
||||
local header_data = nano.handleHeaders(headers)
|
||||
local status_code = kong.response.get_status()
|
||||
local content_length = tonumber(headers["content-length"]) or 0
|
||||
|
||||
local verdict, response = nano.send_response_headers(session_id, session_data, header_data, status_code, content_length)
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
kong.ctx.plugin.blocked = true
|
||||
nano.fini_session(session_data)
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
|
||||
ctx.expect_body = not (status_code == 204 or status_code == 304 or (100 <= status_code and status_code < 200) or content_length == 0)
|
||||
end
|
||||
|
||||
function NanoHandler.body_filter(conf)
|
||||
local ctx = kong.ctx.plugin
|
||||
if ctx.blocked then
|
||||
return
|
||||
end
|
||||
|
||||
local session_id = ctx.session_id
|
||||
local session_data = ctx.session_data
|
||||
|
||||
if not session_id or not session_data or ctx.session_finalized then
|
||||
return
|
||||
end
|
||||
|
||||
local body = kong.response.get_raw_body()
|
||||
|
||||
if body then
|
||||
ctx.body_seen = true
|
||||
local verdict, response, modifications = nano.send_body(session_id, session_data, body, nano.HttpChunkType.HTTP_RESPONSE_BODY)
|
||||
|
||||
-- Initialize if not exists
|
||||
ctx.body_buffer_chunk = ctx.body_buffer_chunk or 0
|
||||
|
||||
-- Handle body modifications if any
|
||||
if modifications then
|
||||
body = nano.handle_body_modifications(body, modifications, ctx.body_buffer_chunk)
|
||||
kong.response.set_raw_body(body)
|
||||
end
|
||||
|
||||
ctx.body_buffer_chunk = ctx.body_buffer_chunk + 1
|
||||
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
ctx.session_finalized = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if ctx.body_seen or ctx.expect_body == false then
|
||||
local verdict, response = nano.end_inspection(session_id, session_data, nano.HttpChunkType.HTTP_RESPONSE_END)
|
||||
if verdict == nano.AttachmentVerdict.DROP then
|
||||
nano.fini_session(session_data)
|
||||
ctx.session_finalized = true
|
||||
return nano.handle_custom_response(session_data, response)
|
||||
end
|
||||
|
||||
nano.fini_session(session_data)
|
||||
ctx.session_finalized = true
|
||||
end
|
||||
end
|
||||
|
||||
return NanoHandler
|
562
attachments/kong/lua_attachment_wrapper.c
Executable file
562
attachments/kong/lua_attachment_wrapper.c
Executable file
@ -0,0 +1,562 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "nano_attachment.h"
|
||||
#include "nano_attachment_common.h"
|
||||
|
||||
#define MAX_HEADERS 10000
|
||||
|
||||
// Initialize NanoAttachment for worker
|
||||
static int lua_init_nano_attachment(lua_State *L) {
|
||||
int worker_id = luaL_checkinteger(L, 1);
|
||||
int num_workers = luaL_checkinteger(L, 2);
|
||||
|
||||
NanoAttachment* attachment = InitNanoAttachment(0, worker_id, num_workers, fileno(stdout));
|
||||
if (!attachment) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Failed to initialize NanoAttachment");
|
||||
return 2;
|
||||
}
|
||||
|
||||
lua_pushlightuserdata(L, attachment);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_get_web_response_type(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*)lua_touserdata(L, 1);
|
||||
HttpSessionData* session_data = (HttpSessionData*)lua_touserdata(L, 2);
|
||||
AttachmentVerdictResponse* response = (AttachmentVerdictResponse*)lua_touserdata(L, 3);
|
||||
|
||||
if (!attachment || !session_data || !response) {
|
||||
return luaL_error(L, "invalid args to get_web_response_type");
|
||||
}
|
||||
|
||||
NanoWebResponseType type = GetWebResponseType(attachment, session_data, response);
|
||||
lua_pushinteger(L, type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int lua_get_response_code(lua_State *L) {
|
||||
AttachmentVerdictResponse* response = (AttachmentVerdictResponse*)lua_touserdata(L, 1);
|
||||
if (!response) {
|
||||
return luaL_error(L, "invalid response");
|
||||
}
|
||||
|
||||
int code = GetResponseCode(response);
|
||||
lua_pushinteger(L, code);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_get_block_page(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*)lua_touserdata(L, 1);
|
||||
HttpSessionData* session_data = (HttpSessionData*)lua_touserdata(L, 2);
|
||||
AttachmentVerdictResponse* response = (AttachmentVerdictResponse*)lua_touserdata(L, 3);
|
||||
|
||||
if (!attachment || !session_data || !response) {
|
||||
return luaL_error(L, "invalid args to get_block_page");
|
||||
}
|
||||
|
||||
BlockPageData page = GetBlockPage(attachment, session_data, response);
|
||||
size_t size = page.title_prefix.len + page.title.len +
|
||||
page.body_prefix.len + page.body.len +
|
||||
page.uuid_prefix.len + page.uuid.len + page.uuid_suffix.len;
|
||||
|
||||
char *result = malloc(size + 1);
|
||||
if (!result) {
|
||||
return luaL_error(L, "memory allocation failed");
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
memcpy(result + offset, page.title_prefix.data, page.title_prefix.len); offset += page.title_prefix.len;
|
||||
memcpy(result + offset, page.title.data, page.title.len); offset += page.title.len;
|
||||
memcpy(result + offset, page.body_prefix.data, page.body_prefix.len); offset += page.body_prefix.len;
|
||||
memcpy(result + offset, page.body.data, page.body.len); offset += page.body.len;
|
||||
memcpy(result + offset, page.uuid_prefix.data, page.uuid_prefix.len); offset += page.uuid_prefix.len;
|
||||
memcpy(result + offset, page.uuid.data, page.uuid.len); offset += page.uuid.len;
|
||||
memcpy(result + offset, page.uuid_suffix.data, page.uuid_suffix.len); offset += page.uuid_suffix.len;
|
||||
result[size] = '\0';
|
||||
|
||||
lua_pushlstring(L, result, size);
|
||||
free(result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_get_redirect_page(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*)lua_touserdata(L, 1);
|
||||
HttpSessionData* session_data = (HttpSessionData*)lua_touserdata(L, 2);
|
||||
AttachmentVerdictResponse* response = (AttachmentVerdictResponse*)lua_touserdata(L, 3);
|
||||
|
||||
if (!attachment || !session_data || !response) {
|
||||
return luaL_error(L, "invalid args to get_redirect_page");
|
||||
}
|
||||
|
||||
RedirectPageData data = GetRedirectPage(attachment, session_data, response);
|
||||
lua_pushlstring(L, (const char*)data.redirect_location.data, data.redirect_location.len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Allocate memory for nano_str_t (long-lived, must be freed later)
|
||||
static int lua_createNanoStrAlloc(lua_State *L) {
|
||||
const char* str = luaL_checkstring(L, 1);
|
||||
if (!str) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Invalid string input");
|
||||
return 2;
|
||||
}
|
||||
|
||||
char* c_str = strdup(str);
|
||||
if (!c_str) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Failed to allocate memory for string");
|
||||
return 2;
|
||||
}
|
||||
|
||||
nano_str_t* nanoStr = (nano_str_t*)malloc(sizeof(nano_str_t));
|
||||
if (!nanoStr) {
|
||||
free(c_str); // Clean up already allocated string
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Failed to allocate memory for nano_str_t");
|
||||
return 2;
|
||||
}
|
||||
|
||||
nanoStr->len = strlen(str);
|
||||
nanoStr->data = (unsigned char*)c_str;
|
||||
|
||||
lua_pushlightuserdata(L, nanoStr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Free nano_str_t (Memory cleanup)
|
||||
static int lua_freeNanoStr(lua_State *L) {
|
||||
nano_str_t* nanoStr = (nano_str_t*)lua_touserdata(L, 1);
|
||||
if (nanoStr) {
|
||||
free(nanoStr->data);
|
||||
free(nanoStr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate memory for HttpHeaders
|
||||
// Allocate memory for HttpHeaders
|
||||
static int lua_allocHttpHeaders(lua_State *L) {
|
||||
size_t max_headers = 10000;
|
||||
|
||||
HttpHeaders* headers = (HttpHeaders*)malloc(sizeof(HttpHeaders));
|
||||
if (!headers) {
|
||||
return luaL_error(L, "Memory allocation failed for HttpHeaders");
|
||||
}
|
||||
|
||||
headers->data = (HttpHeaderData*)malloc(max_headers * sizeof(HttpHeaderData));
|
||||
if (!headers->data) {
|
||||
free(headers);
|
||||
return luaL_error(L, "Memory allocation failed for HttpHeaderData");
|
||||
}
|
||||
|
||||
headers->headers_count = 0;
|
||||
|
||||
lua_pushlightuserdata(L, headers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Free HttpHeaders memory
|
||||
static int lua_freeHttpHeaders(lua_State *L) {
|
||||
HttpHeaders* headers = (HttpHeaders*)lua_touserdata(L, 1);
|
||||
if (headers) {
|
||||
free(headers->data);
|
||||
free(headers);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the headers_count in HttpHeaders
|
||||
static int lua_setHeaderCount(lua_State *L) {
|
||||
HttpHeaders* headers = (HttpHeaders*)lua_touserdata(L, 1);
|
||||
int count = luaL_checkinteger(L, 2);
|
||||
|
||||
if (!headers) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
headers->headers_count = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper function to convert Lua string to nano_str_t
|
||||
static void lua_fill_nano_str(lua_State *L, int index, nano_str_t *nano_str) {
|
||||
size_t len;
|
||||
const char *str = luaL_checklstring(L, index, &len);
|
||||
|
||||
if (!str) {
|
||||
nano_str->data = NULL;
|
||||
nano_str->len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate memory + 1 for null termination
|
||||
nano_str->data = (char *)malloc(len + 1);
|
||||
|
||||
if (!nano_str->data) { // Check if allocation failed
|
||||
nano_str->len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(nano_str->data, str, len); // Copy exact `len` bytes
|
||||
nano_str->data[len] = '\0'; // Manually null-terminate
|
||||
nano_str->len = len;
|
||||
}
|
||||
|
||||
static int lua_free_http_metadata(lua_State *L) {
|
||||
HttpMetaData *metadata = *(HttpMetaData **)lua_touserdata(L, 1);
|
||||
if (!metadata) return 0;
|
||||
|
||||
free(metadata->http_protocol.data);
|
||||
free(metadata->method_name.data);
|
||||
free(metadata->host.data);
|
||||
free(metadata->listening_ip.data);
|
||||
free(metadata->uri.data);
|
||||
free(metadata->client_ip.data);
|
||||
free(metadata->parsed_host.data);
|
||||
free(metadata->parsed_uri.data);
|
||||
free(metadata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set a header element in HttpHeaderData
|
||||
static int lua_setHeaderElement(lua_State *L) {
|
||||
HttpHeaders *headers = (HttpHeaders *)lua_touserdata(L, 1);
|
||||
int index = luaL_checkinteger(L, 2);
|
||||
|
||||
if (!headers || index >= MAX_HEADERS) {
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Safely allocate and set header key/value
|
||||
lua_fill_nano_str(L, 3, &headers->data[index].key);
|
||||
lua_fill_nano_str(L, 4, &headers->data[index].value);
|
||||
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize session
|
||||
static int lua_init_session(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
SessionID session_id = luaL_checkinteger(L, 2);
|
||||
|
||||
if (!attachment) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Invalid nano_attachment");
|
||||
return 2;
|
||||
}
|
||||
|
||||
HttpSessionData* session_data = InitSessionData(attachment, session_id);
|
||||
if (!session_data) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Failed to initialize session data");
|
||||
return 2;
|
||||
}
|
||||
|
||||
lua_pushlightuserdata(L, session_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Finalize session
|
||||
static int lua_fini_session(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
HttpSessionData* session_data = lua_touserdata(L, 2);
|
||||
|
||||
if (!attachment || !session_data) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "Error: Invalid attachment or session_data");
|
||||
return 2;
|
||||
}
|
||||
|
||||
FiniSessionData(attachment, session_data);
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if session is finalized
|
||||
static int lua_is_session_finalized(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
HttpSessionData* session_data = (HttpSessionData*) lua_touserdata(L, 2);
|
||||
|
||||
if (!attachment || !session_data) {
|
||||
lua_pushboolean(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = IsSessionFinalized(attachment, session_data);
|
||||
lua_pushboolean(L, result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Function to extract request metadata and create HttpMetaData struct
|
||||
static int lua_create_http_metadata(lua_State *L) {
|
||||
HttpMetaData *metadata = (HttpMetaData *)malloc(sizeof(HttpMetaData));
|
||||
if (!metadata) {
|
||||
return luaL_error(L, "Memory allocation failed");
|
||||
}
|
||||
|
||||
lua_fill_nano_str(L, 1, &metadata->http_protocol);
|
||||
lua_fill_nano_str(L, 2, &metadata->method_name);
|
||||
lua_fill_nano_str(L, 3, &metadata->host);
|
||||
lua_fill_nano_str(L, 4, &metadata->listening_ip);
|
||||
metadata->listening_port = (uint16_t)luaL_checkinteger(L, 5);
|
||||
lua_fill_nano_str(L, 6, &metadata->uri);
|
||||
lua_fill_nano_str(L, 7, &metadata->client_ip);
|
||||
metadata->client_port = (uint16_t)luaL_checkinteger(L, 8);
|
||||
lua_fill_nano_str(L, 9, &metadata->parsed_host);
|
||||
lua_fill_nano_str(L, 10, &metadata->parsed_uri);
|
||||
|
||||
// Store pointer in Lua
|
||||
lua_pushlightuserdata(L, metadata);
|
||||
return 1; // Return userdata
|
||||
}
|
||||
|
||||
// Send data to NanoAttachment
|
||||
static int lua_send_data(lua_State *L) {
|
||||
// Retrieve function arguments from Lua
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
SessionID session_id = luaL_checkinteger(L, 2);
|
||||
HttpSessionData *session_data = (HttpSessionData*) lua_touserdata(L, 3);
|
||||
HttpChunkType chunk_type = luaL_checkinteger(L, 4);
|
||||
HttpMetaData* meta_data = (HttpMetaData*) lua_touserdata(L, 5);
|
||||
HttpHeaders* req_headers = (HttpHeaders*) lua_touserdata(L, 6);
|
||||
int contains_body = luaL_checkinteger(L, 7); // Convert Lua boolean to C int
|
||||
//int contains_body = 0;
|
||||
|
||||
// Validate inputs
|
||||
if (!attachment || !session_data || !meta_data || !req_headers) {
|
||||
lua_pushstring(L, "Error: received NULL data in lua_send_data");
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
// Allocate memory for HttpRequestFilterData
|
||||
HttpRequestFilterData *filter_data = (HttpRequestFilterData *)malloc(sizeof(HttpRequestFilterData));
|
||||
if (!filter_data) {
|
||||
return luaL_error(L, "Memory allocation failed for HttpRequestFilterData");
|
||||
}
|
||||
|
||||
// Populate HttpRequestFilterData struct
|
||||
filter_data->meta_data = meta_data;
|
||||
filter_data->req_headers = req_headers;
|
||||
filter_data->contains_body = contains_body;
|
||||
|
||||
// Create attachment data
|
||||
AttachmentData attachment_data;
|
||||
attachment_data.session_id = session_id;
|
||||
attachment_data.session_data = session_data;
|
||||
attachment_data.chunk_type = chunk_type;
|
||||
attachment_data.data = (void*)filter_data;
|
||||
|
||||
AttachmentVerdictResponse* res_ptr = malloc(sizeof(AttachmentVerdictResponse));
|
||||
*res_ptr = SendDataNanoAttachment(attachment, &attachment_data);
|
||||
|
||||
// Free allocated memory
|
||||
free(filter_data);
|
||||
|
||||
lua_pushinteger(L, res_ptr->verdict);
|
||||
lua_pushlightuserdata(L, res_ptr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int lua_send_body(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
SessionID session_id = luaL_checkinteger(L, 2);
|
||||
HttpSessionData *session_data = (HttpSessionData*) lua_touserdata(L, 3);
|
||||
size_t body_len;
|
||||
const char *body_chunk = luaL_checklstring(L, 4, &body_len);
|
||||
HttpChunkType chunk_type = luaL_checkinteger(L, 5);
|
||||
|
||||
if (!attachment || !session_data || !body_chunk) {
|
||||
lua_pushstring(L, "Error: Invalid attachment or session_data");
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
// For small bodies, send as a single chunk
|
||||
if (body_len <= 8 * 1024) {
|
||||
HttpBody http_chunks;
|
||||
http_chunks.bodies_count = 1;
|
||||
|
||||
nano_str_t chunk;
|
||||
chunk.data = (unsigned char*)body_chunk;
|
||||
chunk.len = body_len;
|
||||
http_chunks.data = &chunk;
|
||||
|
||||
AttachmentData attachment_data;
|
||||
attachment_data.session_id = session_id;
|
||||
attachment_data.session_data = session_data;
|
||||
attachment_data.chunk_type = chunk_type;
|
||||
attachment_data.data = &http_chunks;
|
||||
|
||||
AttachmentVerdictResponse* res_ptr = malloc(sizeof(AttachmentVerdictResponse));
|
||||
*res_ptr = SendDataNanoAttachment(attachment, &attachment_data);
|
||||
|
||||
// Push verdict
|
||||
lua_pushinteger(L, res_ptr->verdict);
|
||||
lua_pushlightuserdata(L, res_ptr);
|
||||
|
||||
// Push modifications if they exist
|
||||
if (res_ptr->modifications) {
|
||||
lua_pushlightuserdata(L, res_ptr->modifications);
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Calculate number of chunks (8KB each, like Envoy)
|
||||
const size_t CHUNK_SIZE = 8 * 1024;
|
||||
size_t num_chunks = ((body_len - 1) / CHUNK_SIZE) + 1;
|
||||
|
||||
// Limit number of chunks like Envoy
|
||||
if (num_chunks > 10000) {
|
||||
num_chunks = 10000;
|
||||
}
|
||||
|
||||
// Create HttpBody structure
|
||||
HttpBody http_chunks;
|
||||
http_chunks.bodies_count = num_chunks;
|
||||
|
||||
// Allocate memory for chunks array
|
||||
http_chunks.data = (nano_str_t*)malloc(num_chunks * sizeof(nano_str_t));
|
||||
if (!http_chunks.data) {
|
||||
lua_pushstring(L, "Error: Failed to allocate memory for chunks");
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
// Prepare chunks using pointer arithmetic like Envoy
|
||||
for (size_t i = 0; i < num_chunks; i++) {
|
||||
nano_str_t* chunk_ptr = (nano_str_t*)((char*)http_chunks.data + (i * sizeof(nano_str_t)));
|
||||
size_t chunk_start = i * CHUNK_SIZE;
|
||||
size_t chunk_len = (i == num_chunks - 1) ? (body_len - chunk_start) : CHUNK_SIZE;
|
||||
|
||||
chunk_ptr->data = (unsigned char*)(body_chunk + chunk_start);
|
||||
chunk_ptr->len = chunk_len;
|
||||
}
|
||||
|
||||
// Prepare attachment data
|
||||
AttachmentData attachment_data;
|
||||
attachment_data.session_id = session_id;
|
||||
attachment_data.session_data = session_data;
|
||||
attachment_data.chunk_type = chunk_type;
|
||||
attachment_data.data = &http_chunks;
|
||||
|
||||
// Send all chunks at once
|
||||
AttachmentVerdictResponse* res_ptr = malloc(sizeof(AttachmentVerdictResponse));
|
||||
*res_ptr = SendDataNanoAttachment(attachment, &attachment_data);
|
||||
|
||||
// Free allocated memory
|
||||
free(http_chunks.data);
|
||||
|
||||
// Push verdict
|
||||
lua_pushinteger(L, res_ptr->verdict);
|
||||
lua_pushlightuserdata(L, res_ptr);
|
||||
|
||||
// Push modifications if they exist
|
||||
if (res_ptr->modifications) {
|
||||
lua_pushlightuserdata(L, res_ptr->modifications);
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int lua_end_inspection(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
SessionID session_id = luaL_checkinteger(L, 2);
|
||||
HttpSessionData* session_data = (HttpSessionData*) lua_touserdata(L, 3);
|
||||
HttpChunkType chunk_type = luaL_checkinteger(L, 4);
|
||||
|
||||
if (!attachment || !session_data) {
|
||||
lua_pushstring(L, "Error: Invalid attachment or session_data");
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
AttachmentData attachment_data;
|
||||
attachment_data.session_id = session_id;
|
||||
attachment_data.session_data = session_data;
|
||||
attachment_data.chunk_type = chunk_type;
|
||||
attachment_data.data = NULL;
|
||||
|
||||
// Send NULL to indicate end of data
|
||||
AttachmentVerdictResponse* res_ptr = malloc(sizeof(AttachmentVerdictResponse));
|
||||
*res_ptr = SendDataNanoAttachment(attachment, &attachment_data);
|
||||
|
||||
lua_pushinteger(L, res_ptr->verdict);
|
||||
lua_pushlightuserdata(L, res_ptr);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Send response headers to NanoAttachment
|
||||
static int lua_send_response_headers(lua_State *L) {
|
||||
NanoAttachment* attachment = (NanoAttachment*) lua_touserdata(L, 1);
|
||||
SessionID session_id = luaL_checkinteger(L, 2);
|
||||
HttpSessionData *session_data = (HttpSessionData*) lua_touserdata(L, 3);
|
||||
HttpHeaders *headers = (HttpHeaders*) lua_touserdata(L, 4);
|
||||
int status_code = luaL_checkinteger(L, 5);
|
||||
uint64_t content_length = luaL_checkinteger(L, 6);
|
||||
|
||||
if (!attachment || !session_data || !headers) {
|
||||
lua_pushstring(L, "Error: Invalid attachment, session_data, or headers");
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
// Create ResHttpHeaders structure exactly as in filter.go
|
||||
ResHttpHeaders res_headers;
|
||||
res_headers.headers = headers;
|
||||
res_headers.response_code = status_code;
|
||||
res_headers.content_length = content_length;
|
||||
|
||||
AttachmentData attachment_data;
|
||||
attachment_data.session_id = session_id;
|
||||
attachment_data.session_data = session_data;
|
||||
attachment_data.chunk_type = HTTP_RESPONSE_HEADER;
|
||||
attachment_data.data = &res_headers;
|
||||
|
||||
AttachmentVerdictResponse* res_ptr = malloc(sizeof(AttachmentVerdictResponse));
|
||||
*res_ptr = SendDataNanoAttachment(attachment, &attachment_data);
|
||||
lua_pushinteger(L, res_ptr->verdict);
|
||||
lua_pushlightuserdata(L, res_ptr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Register functions in Lua
|
||||
static const struct luaL_Reg nano_attachment_lib[] = {
|
||||
{"init_nano_attachment", lua_init_nano_attachment},
|
||||
{"get_web_response_type", lua_get_web_response_type},
|
||||
{"get_response_code", lua_get_response_code},
|
||||
{"get_block_page", lua_get_block_page},
|
||||
{"get_redirect_page", lua_get_redirect_page},
|
||||
{"createNanoStrAlloc", lua_createNanoStrAlloc},
|
||||
{"freeNanoStr", lua_freeNanoStr},
|
||||
{"setHeaderElement", lua_setHeaderElement},
|
||||
{"send_data", lua_send_data},
|
||||
{"send_response_headers", lua_send_response_headers},
|
||||
{"fini_session", lua_fini_session},
|
||||
{"is_session_finalized", lua_is_session_finalized},
|
||||
{"init_session", lua_init_session},
|
||||
{"allocHttpHeaders", lua_allocHttpHeaders},
|
||||
{"freeHttpHeaders", lua_freeHttpHeaders},
|
||||
{"setHeaderCount", lua_setHeaderCount},
|
||||
{"create_http_metadata", lua_create_http_metadata},
|
||||
{"send_body", lua_send_body},
|
||||
{"end_inspection", lua_end_inspection},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
// Load library
|
||||
int luaopen_lua_attachment_wrapper(lua_State *L) {
|
||||
luaL_newlib(L, nano_attachment_lib);
|
||||
return 1;
|
||||
}
|
426
attachments/kong/nano_ffi.lua
Executable file
426
attachments/kong/nano_ffi.lua
Executable file
@ -0,0 +1,426 @@
|
||||
package.cpath = "/usr/local/kong/lib/?.so;" .. package.cpath
|
||||
local nano_attachment = require "lua_attachment_wrapper"
|
||||
local kong = kong
|
||||
local nano = {}
|
||||
|
||||
nano.session_counter = 0
|
||||
nano.attachments = {} -- Store attachments per worker
|
||||
nano.num_workers = ngx.worker.count() or 1 -- Detect number of workers
|
||||
nano.allocated_strings = {}
|
||||
nano.allocate_headers = {}
|
||||
nano.AttachmentVerdict = {
|
||||
INSPECT = 0,
|
||||
ACCEPT = 1,
|
||||
DROP = 2, -- Matches `ATTACHMENT_VERDICT_DROP`
|
||||
INJECT = 3
|
||||
}
|
||||
nano.HttpChunkType = {
|
||||
HTTP_REQUEST_FILTER = 0,
|
||||
HTTP_REQUEST_METADATA = 1,
|
||||
HTTP_REQUEST_HEADER = 2,
|
||||
HTTP_REQUEST_BODY = 3,
|
||||
HTTP_REQUEST_END = 4,
|
||||
HTTP_RESPONSE_HEADER = 5,
|
||||
HTTP_RESPONSE_BODY = 6,
|
||||
HTTP_RESPONSE_END = 7,
|
||||
HOLD_DATA = 8
|
||||
}
|
||||
|
||||
nano.WebResponseType = {
|
||||
CUSTOM_WEB_RESPONSE = 0,
|
||||
RESPONSE_CODE_ONLY = 1,
|
||||
REDIRECT_WEB_RESPONSE = 2,
|
||||
NO_WEB_RESPONSE = 3,
|
||||
}
|
||||
|
||||
local ffi = require "ffi"
|
||||
|
||||
ffi.cdef[[
|
||||
typedef enum HttpModificationType
|
||||
{
|
||||
APPEND,
|
||||
INJECT,
|
||||
REPLACE
|
||||
} HttpModificationType;
|
||||
|
||||
typedef enum NanoWebResponseType
|
||||
{
|
||||
CUSTOM_WEB_RESPONSE,
|
||||
RESPONSE_CODE_ONLY,
|
||||
REDIRECT_WEB_RESPONSE,
|
||||
NO_WEB_RESPONSE
|
||||
} NanoWebResponseType;
|
||||
|
||||
typedef struct __attribute__((__packed__)) HttpInjectData {
|
||||
int64_t injection_pos;
|
||||
HttpModificationType mod_type;
|
||||
uint16_t injection_size;
|
||||
uint8_t is_header;
|
||||
uint8_t orig_buff_index;
|
||||
char data[0];
|
||||
} HttpInjectData;
|
||||
|
||||
typedef struct NanoHttpModificationList {
|
||||
struct NanoHttpModificationList *next; ///< Next node.
|
||||
HttpInjectData modification; ///< Modification data.
|
||||
char *modification_buffer;
|
||||
} NanoHttpModificationList;
|
||||
]]
|
||||
|
||||
-- Assuming you already defined the C struct somewhere:
|
||||
-- ffi.cdef[[
|
||||
-- typedef struct NanoHttpModificationList { ... } NanoHttpModificationList;
|
||||
-- ]]
|
||||
|
||||
local NanoHttpModificationListPtr = ffi.typeof("NanoHttpModificationList*")
|
||||
|
||||
function nano.generate_session_id()
|
||||
nano.session_counter = nano.session_counter + 1
|
||||
local worker_id = ngx.worker.id()
|
||||
-- Compose session_id as "<worker_id><counter>", e.g. "20001"
|
||||
return tonumber(string.format("%d%05d", worker_id, nano.session_counter))
|
||||
end
|
||||
|
||||
function nano.handle_custom_response(session_data, response)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
local response_type = nano_attachment.get_web_response_type(attachment, session_data, response)
|
||||
|
||||
if response_type == nano.WebResponseType.RESPONSE_CODE_ONLY then
|
||||
local code = nano_attachment.get_response_code(response)
|
||||
return kong.response.exit(code, "")
|
||||
end
|
||||
|
||||
if response_type == nano.WebResponseType.REDIRECT_WEB_RESPONSE then
|
||||
local location = nano_attachment.get_redirect_page(attachment, session_data, response)
|
||||
return kong.response.exit(307, "", { ["Location"] = location })
|
||||
end
|
||||
|
||||
local block_page = nano_attachment.get_block_page(attachment, session_data, response)
|
||||
if not block_page then
|
||||
kong.log.err("Failed to retrieve custom block page for session ", session_data)
|
||||
return kong.response.exit(500, { message = "Internal Server Error" })
|
||||
end
|
||||
local code = nano_attachment.get_response_code(response) -- Get the intended status code
|
||||
return kong.response.exit(code, block_page, { ["Content-Type"] = "text/html" })
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Allocates memory (must be freed later)
|
||||
function nano.create_nano_str_alloc(str)
|
||||
if not str then return nil end
|
||||
|
||||
local nano_str = nano_attachment.createNanoStrAlloc(str)
|
||||
table.insert(nano.allocated_strings, nano_str) -- Track allocation
|
||||
return nano_str
|
||||
end
|
||||
|
||||
-- Free nano_str_t to prevent memory leaks
|
||||
function nano.free_nano_str(nano_str)
|
||||
if nano_str then
|
||||
nano_attachment.freeNanoStr(nano_str)
|
||||
end
|
||||
end
|
||||
|
||||
-- Free all allocated nano_str_t to prevent memory leaks
|
||||
function nano.free_all_nano_str()
|
||||
for _, nano_str in ipairs(nano.allocated_strings) do
|
||||
nano_attachment.freeNanoStr(nano_str) -- Free memory in C
|
||||
end
|
||||
|
||||
nano.allocated_strings = {} -- Reset the list
|
||||
end
|
||||
|
||||
function nano.free_http_headers(header_data)
|
||||
for _, nano_header in ipairs(nano.allocate_headers) do
|
||||
nano_attachment.freeNanoStr(nano_header) -- Free memory in C
|
||||
end
|
||||
|
||||
nano.allocate_headers = {} -- Reset the list
|
||||
end
|
||||
|
||||
-- Initialize worker attachment
|
||||
function nano.init_attachment()
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment, err
|
||||
local retries = 3
|
||||
|
||||
for attempt = 1, retries do
|
||||
attachment, err = nano_attachment.init_nano_attachment(worker_id, nano.num_workers)
|
||||
if attachment then break end
|
||||
kong.log.err("Worker ", worker_id, " failed to initialize attachment (attempt ", attempt, "): ", err)
|
||||
ngx.sleep(2)
|
||||
end
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Worker ", worker_id, " failed to initialize attachment after ", retries, " attempts.")
|
||||
else
|
||||
nano.attachments[worker_id] = attachment
|
||||
kong.log.info("Worker ", worker_id, " successfully initialized nano_attachment.")
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize a session for a given request
|
||||
function nano.init_session(session_id)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Cannot initialize session: Attachment not found for worker ", worker_id)
|
||||
return nil
|
||||
end
|
||||
|
||||
local session_data, err = nano_attachment.init_session(attachment, session_id)
|
||||
if not session_data then
|
||||
kong.log.err("Failed to initialize session for session_id ", session_id, ": ", err)
|
||||
return nil
|
||||
end
|
||||
|
||||
return session_data
|
||||
end
|
||||
|
||||
-- Check if session is finalized
|
||||
function nano.is_session_finalized(session_data)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment or not session_data then
|
||||
kong.log.err("Cannot check session finalization: Invalid attachment or session_data")
|
||||
return false
|
||||
end
|
||||
|
||||
return nano_attachment.is_session_finalized(attachment, session_data)
|
||||
end
|
||||
|
||||
-- Extract metadata for request
|
||||
function nano.handle_start_transaction()
|
||||
local stream_info = kong.request
|
||||
|
||||
local full_host = stream_info.get_host()
|
||||
local host = full_host:match("([^:]+)")
|
||||
|
||||
local method = stream_info.get_method()
|
||||
local uri = stream_info.get_path()
|
||||
local scheme = stream_info.get_scheme()
|
||||
local client_ip = kong.client.get_ip()
|
||||
local client_port = kong.client.get_port()
|
||||
|
||||
local listening_address = kong.request.get_host()
|
||||
local listening_ip, listening_port = listening_address:match("([^:]+):?(%d*)")
|
||||
|
||||
-- Call the C function with extracted metadata
|
||||
local metadata = nano_attachment.create_http_metadata(
|
||||
scheme, method, host, listening_ip, tonumber(listening_port) or 0,
|
||||
uri, client_ip, tonumber(client_port) or 0, "", ""
|
||||
)
|
||||
|
||||
collectgarbage("stop")
|
||||
|
||||
return metadata
|
||||
end
|
||||
|
||||
-- Handle request headers and convert them to nano_str_t
|
||||
function nano.handleHeaders(headers)
|
||||
local envoy_headers_prefix = "x-envoy"
|
||||
|
||||
-- Allocate memory for headers in C
|
||||
local header_data = nano_attachment.allocHttpHeaders()
|
||||
table.insert(nano.allocate_headers, header_data) -- Track allocation
|
||||
local index = 0
|
||||
|
||||
for key, value in pairs(headers) do
|
||||
if index > 10000 then break end
|
||||
|
||||
-- Filter out unwanted headers
|
||||
if key:find("^" .. envoy_headers_prefix) or key == "x-request-id" or
|
||||
key == ":method" or key == ":path" or key == ":scheme" or
|
||||
key == "x-forwarded-proto" then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- Convert ":authority" to "Host"
|
||||
if key == ":authority" then key = "Host" end
|
||||
|
||||
-- Store header data in C memory
|
||||
nano_attachment.setHeaderElement(header_data, index, key, value)
|
||||
index = index + 1
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- Store the count
|
||||
nano_attachment.setHeaderCount(header_data, index)
|
||||
|
||||
return header_data
|
||||
end
|
||||
|
||||
-- Send data to NanoAttachment
|
||||
function nano.send_data(session_id, session_data, meta_data, header_data, contains_body, chunk_type)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Attachment not initialized for worker ", worker_id, ". Dropping request.")
|
||||
return nano.AttachmentVerdict.INSPECT
|
||||
end
|
||||
|
||||
contains_body = tonumber(contains_body) or 0 -- Ensure it's a number
|
||||
contains_body = (contains_body > 0) and 1 or 0 -- Force strict 0 or 1
|
||||
|
||||
local verdict, response = nano_attachment.send_data(attachment, session_id, session_data, chunk_type, meta_data, header_data, contains_body)
|
||||
return verdict, response
|
||||
end
|
||||
|
||||
function nano.send_body(session_id, session_data, body_chunk, chunk_type)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Attachment not initialized for worker ", worker_id, ". Dropping request.")
|
||||
return nano.AttachmentVerdict.INSPECT
|
||||
end
|
||||
|
||||
-- Send the body chunk directly as a string
|
||||
local verdict, response, modifications = nano_attachment.send_body(attachment, session_id, session_data, body_chunk, chunk_type)
|
||||
return verdict, response, modifications
|
||||
end
|
||||
|
||||
-- Function to inject content into a string at a specific position
|
||||
function nano.inject_at_position(buffer, injection, pos)
|
||||
if pos < 0 or pos > #buffer then
|
||||
kong.log.err("Invalid injection position: ", pos, ", buffer length: ", #buffer)
|
||||
return buffer
|
||||
end
|
||||
return buffer:sub(1, pos) .. injection .. buffer:sub(pos + 1)
|
||||
end
|
||||
|
||||
-- Function to handle body modifications
|
||||
function nano.handle_body_modifications(body, modifications, body_buffer_chunk)
|
||||
if modifications == nil then
|
||||
return body
|
||||
end
|
||||
-- cast the userdata to a pointer
|
||||
local curr_modification = ffi.cast(NanoHttpModificationListPtr, modifications)
|
||||
|
||||
while curr_modification ~= nil do
|
||||
if tonumber(curr_modification.modification.orig_buff_index) == body_buffer_chunk then
|
||||
local injection_pos = tonumber(curr_modification.modification.injection_pos)
|
||||
local modification_str = ffi.string(curr_modification.modification_buffer)
|
||||
|
||||
kong.log.debug("Injecting modification at pos ", injection_pos, " body buffer chunk ", body_buffer_chunk)
|
||||
|
||||
body = nano.inject_at_position(body, modification_str, injection_pos)
|
||||
end
|
||||
|
||||
curr_modification = curr_modification.next
|
||||
end
|
||||
|
||||
return body
|
||||
end
|
||||
|
||||
-- Add a new function for handling response bodies
|
||||
function nano.send_response_body(session_id, session_data, body_chunk)
|
||||
local verdict, response, modifications = nano.send_body(session_id, session_data, body_chunk, nano.HttpChunkType.HTTP_RESPONSE_BODY)
|
||||
return verdict, response, modifications
|
||||
end
|
||||
|
||||
function nano.end_inspection(session_id, session_data, chunk_type)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Attachment not initialized for worker ", worker_id, ". Dropping request.")
|
||||
return nano.AttachmentVerdict.INSPECT
|
||||
end
|
||||
|
||||
local verdict, response = nano_attachment.end_inspection(attachment, session_id, session_data, chunk_type)
|
||||
return verdict, response
|
||||
end
|
||||
|
||||
-- Finalize session cleanup
|
||||
function nano.fini_session(session_data)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment or not session_data then
|
||||
kong.log.err("Cannot finalize session: Invalid attachment or session_data")
|
||||
return false
|
||||
end
|
||||
|
||||
nano_attachment.fini_session(attachment, session_data)
|
||||
kong.log.info("Successfully finalized session ", session_data, " for worker ", worker_id)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Send response headers for inspection
|
||||
function nano.send_response_headers(session_id, session_data, headers, status_code, content_length)
|
||||
local worker_id = ngx.worker.id()
|
||||
local attachment = nano.attachments[worker_id]
|
||||
|
||||
if not attachment then
|
||||
kong.log.err("Attachment not initialized for worker ", worker_id, ". Dropping request.")
|
||||
return nano.AttachmentVerdict.INSPECT
|
||||
end
|
||||
|
||||
local verdict, response = nano_attachment.send_response_headers(
|
||||
attachment,
|
||||
session_id,
|
||||
session_data,
|
||||
headers,
|
||||
status_code,
|
||||
content_length
|
||||
)
|
||||
return verdict, response
|
||||
end
|
||||
|
||||
-- Function to handle header modifications
|
||||
function nano.handle_header_modifications(headers, modifications)
|
||||
if not modifications then
|
||||
return headers
|
||||
end
|
||||
|
||||
local curr_modification = modifications
|
||||
local modified_headers = headers
|
||||
|
||||
while curr_modification do
|
||||
local mod = curr_modification.modification
|
||||
if mod.is_header then
|
||||
local type = mod.mod_type
|
||||
local key = curr_modification.modification_buffer
|
||||
local value = curr_modification.next and curr_modification.next.modification_buffer or nil
|
||||
|
||||
if type == 0 then -- APPEND
|
||||
kong.log.debug("Appending header: ", key, " : ", value)
|
||||
modified_headers[key] = value
|
||||
elseif type == 1 then -- INJECT
|
||||
local header_index = mod.orig_buff_index
|
||||
local header_name = nil
|
||||
local header_value = nil
|
||||
local i = 0
|
||||
for k, v in pairs(modified_headers) do
|
||||
if i == header_index then
|
||||
header_name = k
|
||||
header_value = v
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
if header_name then
|
||||
kong.log.debug("Injecting into header: ", header_name)
|
||||
modified_headers[header_name] = nano.inject_at_position(header_value, value, mod.injection_pos)
|
||||
end
|
||||
elseif type == 2 then -- REPLACE
|
||||
kong.log.debug("Replacing header: ", key)
|
||||
modified_headers[key] = value
|
||||
end
|
||||
end
|
||||
curr_modification = curr_modification.next
|
||||
end
|
||||
|
||||
return modified_headers
|
||||
end
|
||||
|
||||
return nano
|
57
attachments/kong/open-appsec-waf-kong-plugin-1.0.0-1.rockspec
Executable file
57
attachments/kong/open-appsec-waf-kong-plugin-1.0.0-1.rockspec
Executable file
@ -0,0 +1,57 @@
|
||||
package = "open-appsec-waf-kong-plugin"
|
||||
version = "1.0.0-1"
|
||||
|
||||
source = {
|
||||
url = "git://github.com/openappsec/attachment.git",
|
||||
tag = "main"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "Kong plugin for scanning headers",
|
||||
detailed = [[
|
||||
A Kong plugin that scans HTTP request headers using Nano Attachment.
|
||||
]],
|
||||
homepage = "https://github.com/openappsec/attachment",
|
||||
license = "Apache"
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"lua >= 2.1"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
|
||||
modules = {
|
||||
["kong.plugins.open-appsec-waf-kong-plugin.handler"] = "attachments/kong/handler.lua",
|
||||
["kong.plugins.open-appsec-waf-kong-plugin.nano_ffi"] = "attachments/kong/nano_ffi.lua",
|
||||
["kong.plugins.open-appsec-waf-kong-plugin.schema"] = "attachments/kong/schema.lua",
|
||||
["lua_attachment_wrapper"] = {
|
||||
sources = {
|
||||
"attachments/kong/lua_attachment_wrapper.c",
|
||||
"attachments/nano_attachment/nano_attachment.c",
|
||||
"attachments/nano_attachment/nano_attachment_io.c",
|
||||
"attachments/nano_attachment/nano_attachment_metric.c",
|
||||
"attachments/nano_attachment/nano_attachment_sender.c",
|
||||
"attachments/nano_attachment/nano_attachment_sender_thread.c",
|
||||
"attachments/nano_attachment/nano_attachment_thread.c",
|
||||
"attachments/nano_attachment/nano_compression.c",
|
||||
"attachments/nano_attachment/nano_configuration.c",
|
||||
"attachments/nano_attachment/nano_initializer.c",
|
||||
"attachments/nano_attachment/nano_utils.c",
|
||||
"attachments/nano_attachment/nano_attachment_util/nano_attachment_util.cc",
|
||||
"core/attachments/http_configuration/http_configuration.cc",
|
||||
"core/compression/compression_utils.cc",
|
||||
"core/shmem_ipc_2/shared_ring_queue.c",
|
||||
"core/shmem_ipc_2/shmem_ipc.c"
|
||||
},
|
||||
incdirs = {
|
||||
"core/include/attachments/",
|
||||
"attachments/nano_attachment/"
|
||||
},
|
||||
defines = { "_GNU_SOURCE", "ZLIB_CONST" },
|
||||
libraries = { "pthread", "z", "rt", "stdc++" },
|
||||
ldflags = { "-static-libstdc++", "-static-libgcc" }
|
||||
}
|
||||
}
|
||||
}
|
16
attachments/kong/schema.lua
Executable file
16
attachments/kong/schema.lua
Executable file
@ -0,0 +1,16 @@
|
||||
local typedefs = require "kong.db.schema.typedefs"
|
||||
|
||||
return {
|
||||
name = "open-appsec-waf-kong-plugin",
|
||||
fields = {
|
||||
{ consumer = typedefs.no_consumer }, -- required for Konnect compatibility
|
||||
{ protocols = typedefs.protocols_http }, -- required so Konnect knows when to allow this plugin
|
||||
{ config = {
|
||||
type = "record",
|
||||
fields = {
|
||||
{ debug = { type = "boolean", default = false } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user