Note that this tuto is not:
- HAProxy configuration tutorial. The reader must known HAProxy basics
- Lua programation tutorial. The reader must known Lua basics
Useful links
- Some articles about HAProxy and Lua: contains many examples of Lua usage.
- official HAProxy Discourse about Lua: provides community help about Lua. Do not hesitate to ask questions and share your examples.
- Lua 5.3 documentation.
- HAProxy configuration documentation
- HAProxy / Lua API documentation
Before start
In the following examples, I use the function print_r(). This function is usefull for displaying complex Lua structures. It is used for debugging. You can found this function in the page Lua scripts. The samples.lua file starts with an include (require()) of the print_r script.require("print_r")
For testing the following samples, we always use HAProxy in debug mode. The global section of the configuration file is like the following. This is not a production ready configuration !
global
lua-load samples.lua
stats socket /tmp/haproxy.sock mode 644 level admin
tune.ssl.default-dh-param 2048
defaults
timeout client 1m
timeout server 1m
The configuration file is named haproxy.cfg, all the Lua samples which could be copy, need to be pasted in the file samples.lua. The command line for starting sample is like the following. We assume a start from the directory containing the two configuration file (for default path reasons).
haproxy -d -f haproxy.cfg
Most of tests are performed whith Curl.
The documentation links points to the HAProxy version 1.7 documentation.
Note about sample-fetches wrappers and Lua Notation: txn.f is an array of wrapper functions. The object like members of an array doesn't supports the "." which is a reserved character. So all the sample fetches are replaced by a "_".
With Lua, a function is a variable. For convenience, the executed function can be declared inline as anonymous functions or referenced as named function. So the two sample below have the same behavior.
-- Named function
function a(arg)
print("hello")
end
register_callback(a)
-- Inline function
register_callback(function(arg)
print("hello")
end)
I will use the inline notation. Bellow the first sample commented. Note that
all the Lua registered function are mapped in the HAProxy configuration with
a prefix "lua.".
Using HAProxy sample fetches inside Lua
This first example is absolutely useless. It just shows how to create a Lua sample-fetch which return the same content that an embedded sample-fetch. By the way, it shows how to use embedded sample fetches in Lua.First, HAProxy provides some functions and class. Some of these ones are availaible from the start of HAProxy. Where the lua file is read, it is executed, so the Lua file is executed during the start of HAProxy.
New all the functions which will be call by HAProxy (called "hook") needs to be registered. For declaring a Lua sample-fetch we will use the function core.register_fetches()
- core.register_fetches(): Function (doc): Register Lua sample-fetch. register_fetches takes a function as argument. This function will be called according with the HAProxy
- txn: Class TXN (doc): Provides functions available with the transaction.
- txn.f: Class Fetches (doc): Provides an access to all the haproxy sample fetches. There are called sample-fetches wrappers.
-- This register new sample-fetch called "lua.sample1"
core.register_fetches("sample1", function(txn)
-- By default, all varibles are global. specify the local
-- keyword to avoid bug. The following line store the
-- content of the sample-fetch "path" in the variable
-- "path".
local path = txn.f:path()
-- Print out the content of path variable. The data
-- id display on the HAProxy standard output.
print_r(path)
-- Return the value of the path as the sample-fetch
-- result.
return txn.f:path()
end)
We can use this new sample-fetch like the embedded sample-fetch"path":
listen sample1
mode http
bind *:10010
acl use-lua lua.sample1 -m beg /use-lua
acl use-php lua.sample1 -m beg /use-php
http-request redirect location /lua if use-lua
http-request redirect location /php if use-php
http-request redirect location /other
Start HAProxy and test with curl:
$ curl -s -i http://127.0.0.1:10010/use-lua | grep Location
Location: /lua
$ curl -s -i http://127.0.0.1:10010/use-php | grep Location
Location: /php
$ curl -s -i http://127.0.0.1:10010/use-other | grep Location
Location: /other
Simplify complex conditions
In some cases, configurations have complex conditions based on sample-fetches. These kind of conditions are not easy to maintain because there are not easily understandable. We are quickly lost with a lot of ACL. A prod likely example is choosing redirect to https or no according with some conditions. The conditions are prod or preprod, already ssl or no, protected page or no. We want to force https for the payment page, except for the preprod, except if the request is already SSL and except if the DEBUG cookie is set.We want to redirect to http if the request is ssl and if the preprod is required or if not a payment page is required and if the exception cookie is not set.
These condition are a little bit complex with classic ACL. The Lua can help us. The foolowing table resule input name, their definition and the testing method:
Condition name | Description | Test with Curl |
---|---|---|
is_payment_page | The payment page is requested | curl http://127.0.0.1:10020/payment/ |
is_ssl | The request is performed using SSL | curl -k https://127.0.0.1:10021/ |
is_preprod | The preprod is required | curl http://127.0.0.1:10020/ -H 'Host: preprod.test.com' |
is_cookie_exception | A DEBUG cookie is set | curl http://127.0.0.1:10020/ -H 'Cookie: DEBUG=1' |
is_payment | is_ssl | is_preprod | is_debug | action |
---|---|---|---|---|
0 | 0 | 0 | 0 | forward |
0 | 0 | 0 | 1 | forward |
0 | 0 | 1 | 0 | forward |
0 | 0 | 1 | 1 | forward |
0 | 1 | 0 | 0 | HTTP redirect |
0 | 1 | 0 | 1 | HTTP redirect |
0 | 1 | 1 | 0 | HTTP redirect |
0 | 1 | 1 | 1 | HTTP redirect |
1 | 0 | 0 | 0 | HTTPS redirect |
1 | 0 | 0 | 1 | forward |
1 | 0 | 1 | 0 | forward |
1 | 0 | 1 | 1 | forward |
1 | 1 | 0 | 0 | forward |
1 | 1 | 0 | 1 | HTTP redirect |
1 | 1 | 1 | 0 | HTTP redirect |
1 | 1 | 1 | 1 | HTTP redirect |
-- This function returns all condition in an array.
function get_variables(txn)
-- This array will contains conditions
local cond = {}
-- True if the path starts with "/payment/"
cond['is_payment_page'] = string.match(txn.sf:path(), '^/payment/') ~= nil
-- True if the front connection is SSL
cond['is_ssl'] = txn.f:ssl_fc() == 1
-- True if the domain name asked is preprod
cond['is_preprod'] = txn.f:req_fhdr('host') == 'preprod.test.com'
-- True if the cookie 'DEBUG' is set
cond['is_cookie_exception'] = txn.f:req_cook_cnt('DEBUG') >= 1
-- Display extracted conditions
-- print_r(cond)
return cond
end
-- This sample fetch return 1 if we need HTTPS redirect
core.register_fetches("sample2_1", function(txn)
-- Get input conditions
local cond = get_variables(txn)
-- Return result according with conditions value and policy.
if cond['is_ssl'] then return 0 end
if cond['is_cookie_exception'] then return 0 end
if cond['is_preprod'] then return 0 end
if cond['is_payment_page'] then return 1 end
return 0
end)
-- This sample fetch returns 1 if we need HTTP redirect
core.register_fetches("sample2_2", function(txn)
-- Get input conditions
local cond = get_variables(txn)
-- Return result according with conditions value and policy.
if not cond['is_ssl'] then return 0 end
if cond['is_cookie_exception'] then return 1 end
if cond['is_preprod'] then return 1 end
if not cond['is_payment_page'] then return 1 end
return 0
end)
And the HAProxy corresponding code:
listen sample2
mode http
bind *:10020
bind *:10021 ssl crt www.test.com.crt crt preprod.test.com.crt
http-request redirect location /to-https if { lua.sample2_1 1 }
http-request redirect location /to-http if { lua.sample2_2 1 }
http-request redirect location /forward
No comments:
Post a Comment