While scrolling through the visitor’s report of my blog, I noticed visitors from a “strange” ISP: “Magistrat der Stadt Wien, Magistratsabteilung 01”, which is the IT department of the magistrate of the City of Vienna. Digging further through the logs, I found out they are a regular visitor, and so are multiple ministries, the Austrian unemployment service, and multiple other public bodies (Maybe more, but due to IP anonymization I only know about the ones that have their own subnets). So I’ve decided to give them a special greeting when visiting. I wanted it to look like this:
Preparation #
Find the relevant IP ranges #
There are multiple ways to do this. Since I already knew before that the City of Vienna has their own ASN, I went over to Hurricane Electric’s BGP Toolkit (after I had to look up their ASN again) to see what IP ranges they announce. For this example, at the moment those are as follows:
Prefix | Description |
---|---|
138.22.0.0/17 | Zentralanstalt fuer Meteorologie und Geodynamik |
138.22.128.0/17 | Zentralanstalt fuer Meteorologie und Geodynamik |
141.203.0.0/16 | Magistrat der Stadt Wien, Magistratsabteilung 01 |
144.65.0.0/17 | Austrian Federal Ministry of Science, Research and Economy |
144.65.128.0/17 | Austrian Federal Ministry of Science, Research and Economy |
193.170.66.0/24 | Bundesministerium fuer Nachhaltigkeit und Tourismus |
193.170.168.0/21 | Zentralanstalt fuer Meteorologie und Geodynamik |
193.170.182.0/23 | Office of the Federal Austrian President |
193.171.152.0/24 | |
193.171.153.0/24 | Bundesministerium fuer Landesverteidigung |
193.171.154.0/24 | Bundesministerium fuer Landesverteidigung |
194.37.10.0/24 | Bundesministerium fuer Nachhaltigkeit und Tourismus |
217.149.224.0/20 | Magistrat der Stadt Wien, Magistratsabteilung 01 |
What we notice is that one range doesn’t have a description. A quick glance at the whois information reveals that it also belongs to the Austrian Ministry of Defense.
If we repeat this procedure, we find out all the IP ranges that we want to use. A good tip to do so is to look at who the first target is peering with if they belong to the same type of organization. For example, one of the magistrate’s peers is the Ministry of the Interior, another one is the federal data center, the parliament and so on. We end up with a list of IPs.
Create the list #
For later use, the list should have two columns, that have the subnet in the first column and an abbreviation of the target without spaces in the second one, separated by tabs or spaces. For example like this:
The problem #
I use a static site generator to deploy this blog (Jekyll), and I can’t have serverside code.
nginx to the rescue! #
Nginx has a module called ngx_http_sub_module, which can replace strings in the response body before sending it to the client.
Prepare the modal #
I added this to the top of my default template
and to make sure it is hidden for visitors that are not in the list, we also need this class definition in the stylesheet:
What we also notice is the placeholder %%NAME%%
, which will be replaced by the name of the visitor’s institution.
nginx configuration #
As mentioned before, we need the ngx_http_sub_module and ngx_http_geo_module compiled into nginx. The version Debian distributes includes those modules. We basically need to add three things to our config.
Disclaimer: I’m not 100% sure if this is a good way to do this.
Prepare the list #
First, we prepare our list and add them to a variable of the “geo” type. This is later matched to the $remote_addr
variable, but we could also use other ones, for example if your webserver is behind a reverse proxy (See the documentation for the ngx_http_geo_module to find out more).
The finished list looks like this in my case:
So our variable is called “ranges”, and we add a default value “nothing”, that always matches if none of the others does. This has to be outside of out server {}
block.
Just as a side story, the short names I’m using here are bmi
, short for Bundesministerium für Inneres, the Ministry for the Interior; wien
for the magistrate of the City of Vienna; ams
, short for Arbeitsmarktservice, the unemployment agency, bmlv
for Bundesministerium für Landesverteidigung, the Austrian defense ministry; wienit
, which is the ISP of something called Wiener Stadtwerke which is the Viennese public utility company and fhstp
, the University of Applied Sciences in St. Pölten, as they are all very loyal visitors, among other public authorities .
Define replacement strings #
Here we need two maps. One maps the placeholder in the modal to the name, and it looks like this:
So we have the short name from above mapped to what we want to show the user. The other one is the one that removes the class that hides the modal itself, for this we replace the .is-hidden-iprange
class with an empty string, like this:
For some reason that I’ve not discovered yet, we also need to replace the is-hidden-iprange
class with itself.
Add the filter #
In the server block, we need to tell nginx what to replace with what. This is done with the following three lines:
The first one replaces the placeholder with the name, the second removes the class, and the third statement tells nginx to only replace the first occurrence of the strings we search for.