WebSEAL LUA http transformation rules with external data
With IBM Security Verify Access 10.0.4 came the possibility to use LUA scripting for HTTP Transformation rules, in addition to the existing XML HTTP Transformation rules.
This is a huge improvement!
Creating transformations using LUA scripts is a lot more intuitive than the XML transformation rules, and there’s also new possiblities coming with the LUA transformation rules.
You can now also modify the body of the http response, and it’s possible to connect to external sources during the execution of a transformation rule.
And there’s a test program available, so you can test your lua scripts before deploying them to WebSEAL (or IAG, for that matter) !
Documentation
The ISVA WebSEAL documentation itself is limited, but there’s more examples at these locations:
- IBM-Security’s Github : https://github.com/IBM-Security/isam-support/tree/master/config-example/webseal/http-transformations
- IAG documentation : https://docs.verify.ibm.com/gateway/docs/tasks-http-transformations.
Lua transformation rule using http.requests
So let’s try this out.
This transformation rule demonstrates the concept of storing configuration for a LUA transformation rule in an external (or internal) webserver. This has offers the possibility to give control to a 3rd party to modify the rules on when the transformation has to execute (in this example : for which url patterns).
A bonus is that there’s no need to restart WebSeal when the configuration changes (like you would have to do when changing the actual transformation rule).
External configuration
So I’ve put a json file on a webserver ( a json array of dictionaries ).
[
{"url": "index.html"},
{"url": "testerdetest"}
]
The redis-lua
module is also available, so you could also store configuration in a Redis server in a similar vein.
The example below doesn’t actually do anything visibly, so you need to use the trace setting pdweb.http.transformation
(minimum at 5), to look at the output in WebSEAL.
Transformation rule
You can edit the configuri
parameter to point to your own local webserver. In any case, the file is available from this website too.
Save the script in the current directory as load_from_http.lua
(for the test)
local request = require "http.request"
local cjson = require "cjson"
local configuri = "https://www.gwbasics.be/requestmatch.json"
local headers, stream = assert(request.new_from_uri(configuri):go())
if (headers == nil) or (not headers) then
Control.returnErrorPage( "1. Could not load configuration data from: "..configuri )
return
end
local body = assert(stream:get_body_as_string())
if headers:get ":status" ~= "200" then
Control.returnErrorPage( "2. http error during loading of: "..configuri )
return
end
if (body == nil) or (not body) then
Control.returnErrorPage( "3. http error during loading of: "..configuri )
return
end
Control.trace(5, string.format(body))
local thejsonbody = cjson.decode(body)
local req = HTTPRequest.getURL()
for i,v in ipairs(thejsonbody) do
Control.trace(5, "Access url table element : "..v["url"])
if string.find(req, v["url"]) then
HTTPRequest.setHeader("X-LUA-Processed", "Tom Bosmans")
Control.trace(5, "Found match "..req)
end
end
The lua script reads the configuration data and then matches the current url of the request in WebSEAL with the url
value.
If there’s a match, a header X-LUA-Processed
is added to be sent to the backend (useless, I know, but this merely shows the concept).
Update: lua-http does not specifically support TLS, and ssl enabled connections will only work when you have a certificate signed by a global public CA.
Test the rule
Prepare context.yaml
You can generate a context.yaml using dumpContext
(in lua script), or by creating one yourself.
You can start from a generated context:
podman run --rm --entrypoint iag_lua_transformation_test icr.io/ibmappgateway/ibm-application-gateway:22.07 -t
This is the output, and you can edit it as you like.
http:
request:
method: <method>
url: <url>
version: <version>
protocol: <protocol>
body: <body>
headers:
- name: <hdr-name>
value: <hdr-value>
cookies:
- name: <cookie-name>
value: <cookie-value>
response:
version: <version>
body: <body>
status:
code: <code>
message: <message>
headers:
- name: <hdr-name>
value: <hdr-value>
cookies:
- name: <cookie-name>
value: <cookie-value>
session:
id: <id>
username: <username>
credential:
attributes:
- name: <attribute-name>
value: <attribute-value>
This is the context.yaml
I’m using for this test :
http:
request:
method: "GET"
url: "/junction1/index.html"
headers:
- name: "User-Agent"
value: "curl"
- name: "Host"
value: "www.ibm.com"
credential:
attribute:
- name: "AZN_CRED_PRINCIPAL_NAME"
value: "testuser"
Save it in the current directory, together with the .lua file.
Run the test
You should now have 2 files in the current directory:
- load_from_http.lua ( the actual lua script )
- context.yaml (the webseal context)
podman run --rm -v ${PWD}:/lua_test:z --entrypoint iag_lua_transformation_test \
icr.io/ibmappgateway/ibm-application-gateway:22.07 -c /lua_test/context.yaml -s /lua_test/load_from_http.lua
Note: in podman, the :z
option in the volume mount seems necessary. The error message is not particularly helpful, but if you get the message below, it’s likely the volume mount files are not accessible by the container.
DPWWA3301E A failure occurred while parsing the YAML file: /var/iag/staging/context.yaml (bad file)
The reason is that the files actually should have system_u:object_r:container_file_t:s0
as selinux context. If you run the above podman command once with the :z option, the selinux context is updated and :z is no longer necessary.
If all goes well, you get this output:
Lua trace (level: 5): [
{"url": "index.html"},
{"url": "testerdetest"}
]
Lua trace (level: 5): Access url table element : index.html
Request header: set (X-LUA-Processed,Tom Bosmans)
Lua trace (level: 5): Found match /gwbasics/index.html
Lua trace (level: 5): Access url table element : testerdetest
Interactive
If you still run into problems, you can also run the iag_lua_transformation_test
command interactively:
podman run --rm -it --entrypoint bash icr.io/ibmappgateway/ibm-application-gateway:22.07
[ivmgr@76f3f6ef8a69 /]$ cd /var/iag/staging/
[ivmgr@76f3f6ef8a69 staging]$ ls
[ivmgr@76f3f6ef8a69 staging]$ vi rule.lua
[ivmgr@76f3f6ef8a69 staging]$ vi context.yaml
[ivmgr@76f3f6ef8a69 staging]$ iag_lua_transformation_test -c context.yaml -s rule.lua
WebSeal configuration
webseald.conf
Once you’ve tested the rule, you can deploy it in WebSEAL.
Edit the webseald.conf, and add a request-match
entry.
loadfromhttp = load_from_http.lua
[http-transformations:loadfromhttp]
request-match = request:* /isam/*
NOTE: Make sure to limit the scope here (limit when the lua transformation rule is executed). There is going to be a relatively big hit on performance, since the http call is executed every time the transformation rule is called.
Error page
By adding a page named errors/c/38b9a4b2.html
to the management root of the instance, you create a custom error page for these transformations.
You can use the macro %FAILREASON%
to show the error messages you generate in the lua script using Control.returnErrorPage
.
<html>
<head>
<title>ERROR during transformation</title>
</head>
<body>
<h1>Error during execution of a transformation rule</h1>
%FAILREASON%
</body>
</html>
It’s easy to verify this, for instance by using a non-existing hostname for the configuri
variable.