Lua scripts
HAProxy Enterprise includes the Lua interpreter, which allows you to extend the load balancer's functionality with custom Lua scripts. This guide is a quick introduction.
You may also find the following links helpful:
Introduction: http://www.haproxy.org/download/2.4/doc/lua.txt
Examples on ARP Alert: https://www.arpalert.org/haproxy-api.html
The basics
In the global
section of your configuration file, define the Lua files that you want to load. HAProxy Enterprise reads these files before processing any chroot
lines in the configuration, so they can be placed outside of your chrooted directory if needed.
HAProxy Enterprise has a non-blocking architecture and, because of that, Lua scripts must be written in a way that avoids reading files, making network calls, or performing other actions that might block the main thread of the load balancer process during runtime. The best place to read files and perform other blocking IO calls is within a core.register_init
function. Then store that data in a global variable so that it can be accessed during runtime from a core.register_service
block.
Inside your Lua script you will add blocks that start with core.register_service
. These define functions that the load balancer will execute during runtime whenever one of the following configuration directives fires:
tcp-request content use-service lua.<name used in register>
tcp-response content use-service lua.<name used in register>
http-request use-service lua.<name used in register>
http-response use-service lua.<name used in register>
Hello world
First, let's consider a basic hello world example. This will echo back (i.e. print to the screen) the the URL that the client requested.
-
Create a file named /etc/hapee-2.4/hello_world.lua with the following content:
core.register_service("hello_world_tcp", "tcp", function(applet) applet:send("hello world\n") end) core.register_service("hello_world_http", "http", function(applet) local response = "The path which was requested is: '" .. applet.path .. "'\n" applet:set_status(200) applet:add_header("content-length", string.len(response)) applet:add_header("content-type", "text/plain") applet:start_response() applet:send(response) end)
This defines two services:
a simple hello_world_tcp service that will blindly respond to any TCP connection (so no headers/etc) with hello world
a more complicated HTTP example that will send a response with headers/etc.
-
Add the following lines to your HAProxy Enterprise configuration:
global lua-load
/etc/hapee-2.4/hello_world.luafrontend http_test bind 127.0.0.1:81 mode http tcp-request inspect-delay 1s http-request use-service lua.hello_world_http frontend tcp_test bind 127.0.0.1:82 mode tcp tcp-request inspect-delay 1s tcp-request content use-service lua.hello_world_tcp Restart the load balancer.
Make a request to the load balancer at 127.0.0.1:81 (i.e. view it in a web browser). It will show the URL that you requested.
Common Lua tasks
In this section, we demonstrate common ways to use Lua.
Verify a request with another service
Using Lua, you can have HAProxy Enterprise validate some requests before servicing them.
-
Create a Lua script called verify_request.lua:
core.register_action("verify_request", { "http-req" }, function(txn) -- Verify that the request is authorized -- Obviously stupid in this case without additional information being sent local s = core.tcp() -- Should be pointing to an HAProxy frontend with balancing/health checks/etc. s:connect("127.0.0.1:8080") -- We use HTTP 1.0 because we don't support keepalive or any other advanced features in this script. s:send("GET /verify.php?url=" .. txn.sf:path() .. " HTTP/1.1\r\nHost: veriy.example.com\r\n\r\n") local msg = s:receive("*l") -- Indicates a connection failure if msg == nil then -- This leave txn.request_verified unset for potentially different handling return end msg = tonumber(string.sub(msg, 9, 12)) -- Read code from 'HTTP/1.0 XXX' -- Makes it easy to test by making any file to be denied. if msg == 404 then txn.set_var(txn,"txn.request_verified",true) else txn.set_var(txn,"txn.request_verified",false) end -- Read the response body, though in this example we aren't using it. msg = s:receive("*l") end)
-
Add a
lua-load
directive to theglobal
section of your configuration to load the Lua script:global lua-load
/etc/hapee-2.4/verify_request.lua -
Add the following to the HAProxy Enterprise configuration inside the
frontend
where you wish to validate requests:frontend example http-request lua.verify_request http-request deny if !{ var(txn.request_verified) -m bool }
The Lua code sets a variable named txn.request_verified that the load balancer can read. It then denies the request based on the value. You might also use the variable's value to add a header, reduce a rate limit, or log the request.
Next up
Runtime API