If you need easy way to provide GeoIP information (Geo location of the IP) to existing set of data, then these code and deployment samples might be just the thing for you; Or if you just want to experiment with Azure Functions 🙂
Obviously many services allow you to check Geo IP information, ranging from simple reverse lookups – to searching IP with various website based services. When you start to look at documented, supported and maintained API’s the list becomes smaller, this is where this blog helps.
- Good maintained API’s exist, but for testing this is one the best approaches
Maxmind database files
In this blog we are exploring the latter (MMDB. files) which we use to build an API without direct throttling limitations – obviously throttling and quotas is something you want to use in commercial API
One of the best known providers of Geo IP information is Maxmind. Maxmind offers two options: a paid API with comprehensive information set, or free option, a basic information set based on .MMDB files which provide GeoIP dB to your apps using supported Modules.
Before I delve into building the API with Azure Functions, I highlight that MMDB. databases can be used to enrich files also as direct part of your application code. No point calling the API, if you can invoke the library directly from your application without any overheads.

Where external API approach becomes useful, is when you want to have more modular architecture between different services, that don’t for example share the same code base /runtime or platform – or you benefit of decoupling different services for microservice style architecture. For example I could provide GeoIP service for external process which supports inline lookups of data via HTTP requests in their process pipeline.

If you plan to build something commercial based on GeoLite2 databases visit their site for terms. While my motivation is not commercial (at least directly). Its still Easy to follow their straightforward licensing term of including this snippet in this site.
This product includes GeoLite2 data created by MaxMind, available from
https://www.maxmind.com.
Prerequisites
- Azure subscription / Get yours here, if you don’t have one, it was about time anyway :)… https://azure.microsoft.com/en-us/free/
- NodeJS installed (link)
- VSCode
- Azure Functions extension for VScode

- Read article about ’GeoLite2 Free Downloadable Databases’
- Registered account maxmind ’signup’

1. Get the MMDB files
- Download the database files from maxmind

Select the GZIP version
- Extract the downloaded archive to a folder you can later copy from the MMDB file
- I used 7ZIP to extract it. Note that depending on your extracting tool /distro you might have dig through two archives to get into the .MMDB file /Picture example
- This is the archive you should see in the end
2. Create the Azure Function
- VSCode: under functions select new project

- VSCode: under functions new function

- VSCode: select JavaScript

- VScode: For template select ’HTTP Trigger’

- Name the trigger

- Select authorization level ’Function’, and select open in new window at step 6/6

- Your workspace should look now like this

Sprinkle the code
If this was more serious project, I would put this all to GitHub repo, but since this is just few snippets, lets go with this 🙂
- in the workspace run the following commands from the Integrated Console

(No NPM init is needed as the extension takes care of it)

npm install @maxmind/geoip2-node --save
index.js


- Overwrite contents of index.js with the contents of the snippet below
const {getIPv4Info} = require(`${__dirname}/src/lookups`)
module.exports = async function (context, req) {
var azureanswer
if (req.headers['x-forwarded-for']) {
var x = req.headers['x-forwarded-for']
azureanswer = await getIPv4Info(x.split(':')[0]).catch((error) => {return error})
} else {azureanswer = 'Incorrect params'}
var data = await getIPv4Info(req.query.ip).catch((error) => {return error})
if (req.query.ip) {
context.res = {
headers:{
'content-type':'text/plain; charset=utf-8'
},
body:data
};
}
else {
context.res = {
status: 200,
body: azureanswer
};
}
};
lookups.js
Create new folder called ’src’ in the workspace (Remember no capital letters!)

const Reader = require('@maxmind/geoip2-node').Reader;
const path = require('path')
const fs = require('fs')
var db = path.join(__dirname + "/GeoLite2-Country.mmdb")
fs.readFileSync(db)
function getIPv4Info (ip) {
console.log('opening IPinfo')
return new Promise ((resolve,reject) => {
Reader.open(db, null).then(reader => {
try {
return resolve(reader.country(ip)) } catch { reject(`cant parse ${ip}`)
}
});
})
}
/* debug here if standalone
getIPv4Info('1.1.1.1').then((data) => console.log(data)).catch((error) => console.log(error))
*/
module.exports={getIPv4Info}
Copy the .mmdb file to src folder


Test
- If all is correctly in place your workspace should look like this
- With F5 (windows) you can run the local version of the function


3. Deploy
- Select ’Create Function App in Azure’

- Enter suitable name for the function

- Select Node.JS 12 for the runtime version

- Select windows as platform, this due to the remote debugging feature with VScode which is very useful and exclusive to the platform choice

- select consumption plan

- Create new resource group, or select existing

- Create new or select exisisting storage account

- If you want some good debug info, select Appinsights, for this demo I chose to skip it

- You should have as output something like this

- Select then deploy to function app

- This is the point where the overwrite happens, regardless if this was existing or new function app


- Start streaming logs

- Now fire an request by copying the function url

- Test the function for output.
- With no params it uses your public IP
- With params it uses the ip in params

Invoke-RestMethod 'https://sentinelhelpers.azurewebsites.net/api/AzureSentinelHelper?code=codehere&ip=1.1.1.1'

from here?
- You could add any enriching function to the app, such like VirusTotal info, using their free API. The sky is the limit here 🙂
If you need to update the MMDB. files. Something like this can be used in helper function since you get permalinks for the files after registering
var https = require('https')
var fs = require('fs')
var key =
var uri = `https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${key}&suffix=tar.gz`
function updateDb () {
var dbfile = fs.createWriteStream(`${__dirname}/geoLite2.tar.gz`)
https.get(uri, (res) => {
res.pipe(dbfile)
dbfile.on('close', () => console.log('file write finished') )
}).on('finish',() => console.log('download finish')).end()
}
updateDb()
module.exports={test}
Till next time!
Br, Joosua
Paluuviite: PoC part 0 – Integrating Azure Security Center Alerts with MS Teams! – SecureCloudBlog