| -- Licensed to the Apache Software Foundation (ASF) under one | |
| -- or more contributor license agreements. See the NOTICE file | |
| -- distributed with this work for additional information | |
| -- regarding copyright ownership. The ASF licenses this file | |
| -- to you under the Apache License, Version 2.0 (the | |
| -- "License"); you may not use this file except in compliance | |
| -- with the License. You may obtain a copy of the License at | |
| -- | |
| -- http://www.apache.org/licenses/LICENSE-2.0 | |
| -- | |
| -- Unless required by applicable law or agreed to in writing, software | |
| -- distributed under the License is distributed on an "AS IS" BASIS, | |
| -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| -- See the License for the specific language governing permissions and | |
| -- limitations under the License. | |
| ts.add_package_path('/usr/local/var/lua/?.lua') | |
| local msc = require("msc") | |
| local msc_config = require("msc_config") | |
| local ffi = require("ffi") | |
| local C = require("C") | |
| local mst = msc.msc_init() | |
| msc.msc_set_connector_info(mst, "ModSecurity-ats") | |
| -- Initialization. Load the modsecurity configuration passed to this lua module | |
| function __init__(argtb) | |
| if (#argtb) < 1 then | |
| ts.error("No ModSecurity Conf is given") | |
| return -1 | |
| end | |
| msc_config.rulesfile = argtb[1] | |
| ts.debug("ModSecurity Conf file is " .. msc_config.rulesfile) | |
| msc_config.rules = msc.msc_create_rules_set() | |
| local error = ffi.new("const char *[1]") | |
| local result = msc.msc_rules_add_file(msc_config.rules, msc_config.rulesfile, error) | |
| if(result < 0) then | |
| ts.error("Problems loading the rules: ".. ffi.string(error[0])) | |
| local ptr = ffi.cast("void *", error[0]) | |
| C.free(ptr) | |
| msc.msc_rules_cleanup(msc_config.rules) | |
| msc_config.rules = nil | |
| return -1 | |
| end | |
| end | |
| -- Reload modsecurity configuration. Trigger by "traffic_ctl config reload" | |
| function __reload__() | |
| ts.debug("Reloading ModSecurity Conf: " .. msc_config.rulesfile) | |
| newrules = msc.msc_create_rules_set() | |
| local error = ffi.new("const char *[1]") | |
| local result = msc.msc_rules_add_file(newrules, msc_config.rulesfile, error) | |
| if(result < 0) then | |
| ts.error("Problems loading the rules during reload: ".. ffi.string(error[0])) | |
| local ptr = ffi.cast("void *", error[0]) | |
| C.free(ptr) | |
| msc.msc_rules_cleanup(newrules) | |
| newrules = nil | |
| else | |
| -- TODO: we are not doing clean up on the old rules and thus leaking resources here | |
| msc_config.rules = newrules | |
| end | |
| end | |
| -- Entry point function run for each incoming request | |
| function do_global_read_request() | |
| if(msc_config.rules == nil) then | |
| ts.debug("No rules loaded. Thus there is no processing done") | |
| return 0 | |
| end | |
| local txn = msc.msc_new_transaction(mst, msc_config.rules ,nil) | |
| -- processing for the connection information | |
| local client_ip, client_port, client_ip_family = ts.client_request.client_addr.get_addr() | |
| local incoming_port = ts.client_request.client_addr.get_incoming_port() | |
| msc.msc_process_connection(txn, client_ip, client_port, "127.0.0.1", incoming_port) | |
| -- processing for the uri information | |
| local uri = ts.client_request.get_uri() | |
| local query_params = ts.client_request.get_uri_args() or '' | |
| if (query_params ~= '') then | |
| uri = uri .. '?' .. query_params | |
| end | |
| msc.msc_process_uri(txn, uri, ts.client_request.get_method(), ts.client_request.get_version()) | |
| -- processing for the request headers | |
| local hdrs = ts.client_request.get_headers() | |
| for k, v in pairs(hdrs) do | |
| msc.msc_add_request_header(txn, k, v) | |
| end | |
| msc.msc_process_request_headers(txn) | |
| msc.msc_process_request_body(txn) | |
| ts.debug("done with processing request") | |
| -- detect if intervention is needed | |
| local iv = ffi.new("ModSecurityIntervention") | |
| iv.status = 200 | |
| iv.log = nil | |
| iv.url = nil | |
| iv.disruptive = 0 | |
| local iv_res = msc.msc_intervention(txn, iv) | |
| ts.debug("done with intervention ".. iv_res .. ' with status ' .. iv.status ) | |
| if(iv.log ~= nil) then | |
| ts.debug("Intervention log: " .. ffi.string(iv.log)) | |
| C.free(iv.log) | |
| end | |
| -- if found an intervention url, trigger handler when sending response to client | |
| if(iv.url ~= nil) then | |
| ts.ctx['url'] = ffi.string(iv.url) | |
| ts.hook(TS_LUA_HOOK_SEND_RESPONSE_HDR, send_response) | |
| C.free(iv.url) | |
| end | |
| -- intervention is needed if status is not 200 | |
| if (iv.status ~= 200) then | |
| ts.http.set_resp(iv.status) | |
| msc.msc_process_logging(txn) | |
| msc.msc_transaction_cleanup(txn) | |
| ts.debug("done with setting custom response") | |
| return 0 | |
| end | |
| -- storing modsecurity object in context | |
| ts.ctx["mst"] = txn | |
| ts.debug("done with setting context") | |
| -- trigger handler to run when response is received | |
| ts.hook(TS_LUA_HOOK_READ_RESPONSE_HDR, read_response) | |
| return 0 | |
| end | |
| -- function run when response is received from origin | |
| function read_response() | |
| -- retrieving modsecurity object | |
| local txn = ts.ctx["mst"] | |
| if(txn == nil) then | |
| ts.error("no transaction object") | |
| return 0 | |
| end | |
| -- processing for the response headers | |
| local hdrs = ts.server_response.get_headers() | |
| for k, v in pairs(hdrs) do | |
| msc.msc_add_response_header(txn, k, v) | |
| end | |
| msc.msc_process_response_headers(txn, ts.server_response.get_status(), "HTTP/"..ts.server_response.get_version()) | |
| msc.msc_process_response_body(txn) | |
| ts.debug("done with processing response") | |
| -- determine if intervention is needed | |
| local iv = ffi.new("ModSecurityIntervention") | |
| iv.status = 200 | |
| iv.log = nil | |
| iv.url = nil | |
| iv.disruptive = 0 | |
| local iv_res = msc.msc_intervention(txn, iv) | |
| ts.debug("done with intervention ".. iv_res .. ' with status ' .. iv.status ) | |
| if(iv.log ~= nil) then | |
| ts.debug("Intervention log: " .. ffi.string(iv.log)) | |
| C.free(iv.log) | |
| end | |
| -- if found an intervention url, trigger handler when sending response to client | |
| ts.ctx['url'] = '' | |
| if(iv.url ~= nil) then | |
| ts.ctx['url'] = ffi.string(iv.url) | |
| C.free(iv.url) | |
| end | |
| -- intervention needed when status is not 200 | |
| ts.ctx['status'] = nil | |
| if (iv.status ~= 200) then | |
| ts.ctx['status'] = iv.status | |
| end | |
| -- response needs to be modified? | |
| if(ts.ctx['url'] ~= '' or ts.ctx['status'] ~= nil) then | |
| ts.hook(TS_LUA_HOOK_SEND_RESPONSE_HDR, send_response) | |
| end | |
| -- we need to return -1 for lua to reset response body with an error body | |
| if(ts.ctx['status'] ~= nil) then | |
| ts.ctx["mst"] = nil | |
| msc.msc_process_logging(txn) | |
| msc.msc_transaction_cleanup(txn) | |
| ts.debug("done with cleaning up context and return error response") | |
| return -1 | |
| end | |
| -- cleaning up | |
| ts.ctx["mst"] = nil | |
| msc.msc_process_logging(txn) | |
| msc.msc_transaction_cleanup(txn) | |
| ts.debug("done with cleaning up context") | |
| return 0 | |
| end | |
| -- function run when sending response to client | |
| function send_response() | |
| -- retrieve intervention url and add it as "Location" header on response to client | |
| local location = ts.ctx['url'] | |
| if (location ~= '') then | |
| ts.debug('location: ' .. location) | |
| ts.client_response.header['Location'] = location | |
| end | |
| -- retrieve status and reset the response with it | |
| local status = ts.ctx['status'] | |
| if (status ~= nil) then | |
| ts.client_response.set_error_resp(status, 'Contents Reset by ModSecurity\n') | |
| end | |
| end |