One of the iRules LX projects I’ve been working on is the ability to dynamically open firewall rules based upon a user’s APM Access Session. In this example the user is external trying to access internal server resources but is blocked by AFM’s default firewall policies. The user then logs into an APM policy and upon successful login AFM allows that external user access to internal resources. See the video below for a demo:
iRules do not have direct access to control plane settings. So prior to TMOS 12.1 this wasn’t easily doable without some serious iRule foo around sideband connections. However, with the introduction of iRules LX we can leverage Node.js to help us interact with the control plane via API calls.
So how does it work?
You still leverate iRules in iRules LX but a new set of commands allow you to pass data from TMM (data plane) to Node.js (control plane) and process that data via javascript – more information in the Getting Started With iRules LX series on DevCentral.
So what does this look like?
Your iRules do not change much except for the addition of the ILX commands:
- ILX::init – invokes the specified node methods
- ILX::call – establishes a communication path from an iRules to the node process
- ILX::notify – sends a message to the specified node method but does not wait for a response
As an example I’ve included the iRule used in the AFM/APM dynamic firewall rule project below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# AFM/ASM dynamic address list iRulesLX RPC. | |
# | |
# add the address to the address list | |
when ACCESS_POLICY_COMPLETED { | |
set ilx_handle [ILX::init "dynamic_address_list_plugin" "dynamic_address_list"] | |
# set IP address | |
set ip_address [ACCESS::session data get session.user.clientip] | |
if {[info exists ip_address]} { | |
# create a random secret | |
if {[catch {set res [ILX::call $ilx_handle "addAddress" $ip_address]} result]} { | |
log local0.error "Client – [IP::client_addr], ILX failure: $result" | |
return | |
} | |
} else { | |
log local0.error "Can not add address list, client IP not defined" | |
return | |
} | |
} | |
# remove the address from the address list | |
when ACCESS_SESSION_CLOSED { | |
set ilx_handle [ILX::init "dynamic_address_list_plugin" "dynamic_address_list"] | |
# set IP address | |
set ip_address [ACCESS::session data get session.user.clientip] | |
if {[info exists ip_address]} { | |
# create a random secret | |
if {[catch {set res [ILX::call $ilx_handle "deleteAddress" $ip_address]} result]} { | |
log local0.error "Client – $ip_address, ILX failure: $result" | |
return | |
} | |
} else { | |
log local0.error "Can not delete address list, client IP not defined" | |
return | |
} | |
} |
Notice the ILX::init on line 7 – this is configured with the iRules LX plugin name and the iRules LX extension inside our workspace.
What about Node.js
Next, you provide the Node.js methods your iRule calls in the index.js file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* iRulesLX RPC for AFM/APM Dynmanic Address List | |
* | |
*/ | |
/* Import the f5-nodejs module. */ | |
var f5 = require('f5-nodejs'); | |
var afm = require('./f5_afm'); | |
/* Create a new rpc server for listening to TCL iRule calls. */ | |
var ilx = new f5.ILXServer(); | |
/** | |
* add address to AFM address list | |
* | |
* @params {String} address | |
* @return {Boolean} result | |
*/ | |
ilx.addMethod('addAddress', function(req, res) { | |
afm.addAddress(req.params()[0], function(result) { | |
if(typeof result !== undefined) { | |
res.reply(true); | |
} else { | |
res.reply(false); | |
} | |
}); | |
}); | |
/** | |
* remove address from AFM address list | |
* | |
* @ params {String} address | |
* @ return {Boolean} result | |
*/ | |
ilx.addMethod('deleteAddress', function(req, res) { | |
afm.deleteAddress(req.params()[0], function(result) { | |
if(typeof result !== undefined) { | |
res.reply(true); | |
} else { | |
res.reply(false); | |
} | |
}); | |
}); |
Not too hard and it offers a world of possibilities with the inclusion of NPM modules.
Where’s the rest of the code?
For a complete view of the iRules LX code please visit the Github repository.