Microsoft Office 365 IP Intelligence - V0.2
Problem this snippet solves:
Update to V0.1 - Microsoft Office 365 IP intelligence
Added a new method to check if a URL is part of Office365 IPI
How to use this snippet:
Refer to this Article: Intelligent Proxy Steering - Office365
Code :
/** *** Name : office365_ipi_extension *** Author : Niels van Sluis,*** Modified by: Brett Smith @f5 *** Version: 0.2 *** Date : 2018-02-25 **/ 'use strict'; // Import the f5-nodejs module and others. var f5 = require('f5-nodejs'); var parseString = require('xml2js').parseString; var https = require('https'); var repeat = require('repeat'); var loki = require('lokijs'); var ipRangeCheck = require('ip-range-check'); // Create (in-memory) LokiJS database. var db = new loki('db.json'); var products = db.addCollection('products'); // Create a new rpc server for listening to TCL iRule calls. var ilx = new f5.ILXServer(); // URL to Microsoft Office 365 XML file. var url = "https://support.content.office.net/en-us/static/O365IPAddresses.xml"; // Function to get XML file and convert to JSON object. function xmlToJson(url, callback) { var req = https.get(url, function(res) { var xml = ''; res.on('data', function(chunk) { xml += chunk; }); res.on('error', function(e) { callback(e, null); }); res.on('timeout', function(e) { callback(e, null); }); res.on('end', function() { if(res.statusCode == 200) { parseString(xml, function(err, result) { callback(null, result); }); } }); }); } // Function that uses the data in the XML file to create a database // that can be used to perform IP address and URL lookups. function getOffice365() { xmlToJson(url, function(err,data) { if(err) { console.log("Error: xmlToJson failed"); return; } // if xml happens to be empty due to an error, do not continue. if(!data) { console.log("Error: No data in XML file"); return; } // Get date updated: 7/13/2017 var productsUpdated = data.products.$.updated; // Only update if version changed var versionCheck = products.findObject({'name':'any'}); if(versionCheck && versionCheck.version == productsUpdated) { console.log("Info: product version didn't changed; No update required"); return; } var allIpAdresses = []; var allUrls = []; data.products.product.forEach(function (product) { var ipAddresses = []; var urls = []; product.addresslist.forEach(function (addresslist) { if(addresslist.$.type == "IPv4" || addresslist.$.type == "IPv6") { if ( typeof addresslist.address !== 'undefined' && addresslist.address ) { addresslist.address.forEach(function (address) { ipAddresses.push(address); allIpAdresses.push(address); }); } } else if(addresslist.$.type == "URL") { if ( typeof addresslist.address !== 'undefined' && addresslist.address ) { addresslist.address.forEach(function (address) { if(address.indexOf('*') !== -1) { // Start Version 0.2 - Added by Brett Smith @f5.com // remove the wilcard (*) from the URL address = address.substr(address.indexOf('*')+1); // End Version 0.2 urls.push(address); allUrls.push(address); } else { urls.push(address); allUrls.push(address); } }); } } }); var p = products.findObject({'name':product.$.name.toLowerCase()}); if(!p) { products.insert({ name: product.$.name.toLowerCase(), ipAddresses: ipAddresses, urls: urls, version: productsUpdated }); } else { p.ipAddresses = ipAddresses; p.urls = urls; p.version = productsUpdated; } }); var p = products.findObject({'name':'any'}); if(!p) { products.insert({ name: 'any', ipAddresses: allIpAdresses, urls: allUrls, version: productsUpdated }); } else { p.ipAddresses = Array.from(new Set(allIpAdresses)); p.urls = Array.from(new Set(allUrls)); p.version = productsUpdated; } console.log("Info: update finished; " + products.data.length + " product records in database."); }); } // refresh Microsoft Office 365 XML every hour repeat(getOffice365).every(1, 'hour').start.now(); // Start Version 0.2 - Added by Brett Smith @f5.com // ILX::call to check if an URL is part of Office365 ilx.addMethod('checkProductURL', function(objArgs, objResponse) { var productName = objArgs.params()[0]; var hostName = objArgs.params()[1]; var verdict = false; var req = products.findObject( { 'name':productName.toLowerCase()}); if(Array.isArray(req.urls)) { for (let url of req.urls) { if(hostName.indexOf(url) !== -1) { console.log("match found:" + url); verdict = true; break; } } } // return verdict to Tcl iRule objResponse.reply(verdict); }); // End Version 0.2 // ILX::call to check if an IP address is part of Office365 ilx.addMethod('checkProductIP', function(objArgs, objResponse) { var productName = objArgs.params()[0]; var ipAddress = objArgs.params()[1]; // fail-open = true, fail-close = false var verdict = true; var req = products.findObject( { 'name':productName.toLowerCase()}); if(req) { verdict = ipRangeCheck(ipAddress, req.ipAddresses); } // return AuthnRequest to Tcl iRule objResponse.reply(verdict); }); // Start listening for ILX::call and ILX::notify events. ilx.listen();
Tested this on version:
13.0- Jack_wAltostratus
Thank you for code share !!!
I found an announcement saying that it will be changed to the REST API. Are you planning to change iRuleLX?
Announcing: Office 365 endpoint categories and Office 365 IP Address and URL web service
- SmithyCirrostratus
Hi, yes I'm aware of the new JSON method. I do have plans to update the code once it's out of preview.
- SmithyCirrostratus
Niels van Sluis, DevCentral MVP has re-written the Microsoft Office 365 IP Intelligence iRuleLX to support the new REST API: https://github.com/nvansluis/f5.office365_endpoints_extension