Introducing iRules LX
iRules is a powerful scripting language that allows you to control network traffic in real time that can route, redirect, modify, drop, log or do just about anything else with network traffic passing through a BIG-IP proxy. iRules enables network programmability to consolidate functions across applications and services.
iRules LX: The Next Evolution of Network Programmability
iRules LX is the next stage of evolution for network programmability that brings Node.js language support to the BIG-IP with version 12.1. Node.js allows JavaScript developers access to over 250,000 npm packages that makes code easier to write and maintain. Development teams can access and work on code with the new iRules LX Workspace environment and the new plug-in available for the Eclipse IDE and can be used for continuous integration builds.
Extend with Node.js Support
iRules Language eXtensions (LX) enables node.js capabilities on the BIG-IP platform. By using iRules LX (Node.js) extensions you can program iRules LX using javascript to control network traffic in real time.
There are only three simple Tcl commands that you need to extend iRules to Node.js
- ILX::init – create Tcl handle, bind extension
- ILX::call – send/receive data to/from extension method
- ILX::notify – send data one-way to method, no response
There is only one Node.js command to know:
- ILX.addMethod – create method, for use by above
Share and Reuse Code
iRules LX makes it easy for JavaScript developers to share and reuse Node.js code, and it makes it easy to update the code. These bits of reusable code are called packages, or sometimes modules. This makes it possible for you to compose complex business logic in node.js without sophisticated developers who know Tcl in-depth. There are over 250,000 npm packages that makes code easier to write and maintain.
iRules LX Workspaces
LX Workspaces provides a portable development package environment with NPM-like directory structure that acts as the source for creation and modification of plugins and node.js extensions. Workspace LX is developer-friendly, with Git-like staging and publishing that simplifies export and import to keep your files in sync for continuous integration and development.
iRules IDE Integration / Eclipse Plugin
Available soon, F5 is delivering a free downloadable plugin to enable our customers to develop iRules & iRules LX scripts via their own Eclipse IDE. The plugin provides modern IDE editing capabilities including syntax highlighting & validation, code completion, and code formatting for both Tcl and Javascript. This plugin will enable authenticated push and pull of iRules code files to and from the BIG-IP platform. This simplifies development workflows by enabling our customers to develop iRules scripts in a familiar development environment.
iRules LX Example: Use Node.js to Make an Off Box MySQL Lookup
This iRules example shows a connection to MySQL database via Node.js. This example uses Tcl iRulesand JavaScript code to make a MySQL call. The iRules prompts the user for a basic auth username and password, then we lookup the username in a MySQL database table and return the user's groups.
MySQL iRule (Tcl) Example: ################################### # mysql_irulelx # 2015-05-17 - Aaron Hooley - First draft example showing iRulesLX querying MySQL database ################################### when RULE_INIT { # Enable logging to /var/log/ltm? # 0=none, 1=error, 2=verbose set static::mysql_debug 2 } when HTTP_REQUEST { if {$static::mysql_debug >= 2}{log local0. "New HTTP request. \[HTTP::username\]=[HTTP::username]"} # If the client does not supply an HTTP basic auth username, prompt for one. # Else send the HTTP username to node.js and respond to client with result if {[HTTP::username] eq ""}{ HTTP::respond 401 content "Username was not included in your request.\nSend an HTTP basic auth username to test" WWW-Authenticate {Basic realm="iRulesLX example server"} if {$static::mysql_debug >= 2}{log local0. "No basic auth username was supplied. Sending 401 to prompt client for username"} } else { # Do some basic validation of the HTTP basic auth username # First fully URI decode it using https://devcentral.f5.com/s/articles?sid=523 set tmp_username [HTTP::username] set username [URI::decode $tmp_username] # Repeat decoding until the decoded version equals the previous value while { $username ne $tmp_username } { set tmp_username $username set username [URI::decode $tmp_username] } # Check that username only contains valid characters using https://devcentral.f5.com/s/articles?sid=726 if {$username ne [set invalid_chars [scan $username {%[-a-zA-Z0-9_]}]]}{ HTTP::respond 401 content "\nA valid username was not included in your request.\nSend an HTTP basic auth username to test\n" WWW-Authenticate {Basic realm="iRulesLX example server"} if {$static::mysql_debug >= 1}{log local0. "Invalid characters in $username. First invalid character was [string range $username [string length $invalid_chars] [string length $invalid_chars]]"} # Exit this iRule event return } # Client supplied a valid username, so initialize the iRulesLX extension set RPC_HANDLE [ILX::init mysql_extension] if {$static::mysql_debug >= 2}{log local0. "\$RPC_HANDLE: $RPC_HANDLE"} # Make the call and save the iRulesLX response # Pass the username and debug level as parameters #set rpc_response [ILX::call $RPC_HANDLE myql_nodejs $username $static::mysql_debug] set rpc_response [ILX::call $RPC_HANDLE myql_nodejs $username] if {$static::mysql_debug >= 2}{log local0. "\$rpc_response: $rpc_response"} # The iRulesLX rule will return -1 if the query succeeded but no matching username was found if {$rpc_response == -1}{ HTTP::respond 401 content "\nYour username was not found in MySQL.\nSend an HTTP basic auth username to test\n" WWW-Authenticate {Basic realm="iRulesLX example server"} if {$static::mysql_debug >= 1}{log local0. "Username was not found in MySQL"} } elseif {$rpc_response eq ""}{ HTTP::respond 401 content "\nDatabase connection failed.\nPlease try again\n" WWW-Authenticate {Basic realm="iRulesLX example server"} if {$static::mysql_debug >= 1}{log local0. "MySQL query failed"} } else { # Send an HTTP 200 response with the groups retrieved from the iRulesLX plugin HTTP::respond 200 content "\nGroup(s) for '$username' are '$rpc_response'\n" if {$static::mysql_debug >= 1}{log local0. "Looked up \$username=$username and matched group(s): $rpc_response"} } } } MySQL iRule LX (Node.js) Example /* ###################################### */ /* index.js counterpart for mysql_irulelx */ /* Log debug to /var/log/ltm? 0=none, 1=errors only, 2=verbose */ var debug = 2; if (debug >= 2) {console.log('Running extension1 index.js');} /* Import the f5-nodejs module. */ var f5 = require('f5-nodejs'); /* Create a new rpc server for listening to TCL iRule calls. */ var ilx = new f5.ILXServer(); /* Start listening for ILX::call and ILX::notify events. */ ilx.listen(); /* Add a method and expect a username parameter and reply with response */ ilx.addMethod('myql_nodejs', function(username, response) { if (debug >= 1) {console.log('my_nodejs' + ' ' + typeof(username.params()) + ' = ' + username.params());} var mysql = require('mysql'); var connection = mysql.createConnection({ host : '10.0.0.110', user : 'bigip', password : 'bigip' }); // Connect to the MySQL server connection.connect(function(err) { if (err) { if (debug >= 1) {console.error('Error connecting to MySQL: ' + err.stack);} return; } if (debug >= 2) {console.log('Connected to MySQL as ID ' + connection.threadId);} }); // Perform the query. Escape the user-input using mysql.escape: https://www.npmjs.com/package/mysql#escaping-query-values connection.query('SELECT * from users_db.users_table where name = ' + mysql.escape(username.params(0)), function(err, rows, fields) { if (err) { // MySQL query failed for some reason, so send a null response back to the Tcl iRule if (debug >= 1) {console.error('Error with query: ' + err.stack);} response.reply(''); return; } else { // Check for no result from MySQL if (rows < 1){ if (debug >= 1) {console.log('No matching records from MySQL');} // Return -1 to the Tcl iRule to show no matching records from MySQL response.reply('-1'); } else { if (debug >= 2) {console.log('First row from MySQL is: ', rows[0]);} //Return the group field from the first row to the Tcl iRule response.reply(rows.pop()); } } }); // Close the MySQL connection connection.end(); });
Getting Started
To get started with iRules LX, download a BIG-IP trial and follow the upcoming tutorials available on the DevCentral community.
- DamP_320463Nimbostratus
Hi Danny,
Thanks for your awesome article.
I would like to ask if ss it possible to use APM logon page username & password instead of HTTP Basic Authentication iRule?
Thanks!
Matteo
- Walter_KacynskiCirrostratus
Sure, just use when ACCESS_POLICY_AGENT_EVENT { and an iRule Agent within the VPE to trigger it.