Many of you may have landed here based on the first half of the title, maybe the title is a tad misleading. Sorry about the false alarm I may have set off, but this is a technical post! “Why racist?” you ask, read more for “the truth”.
At Swym, we have a number of interconnected systems. Some of them are public, some internal and some have both public and internal usage. For public interface points, a user authentication with role authorization strategy was the go-to solution. For the internal interfaces, the access is implicitly restricted to the virtual subnet of the network, relying on the Network firewall policies.
For the public-internal interfaces, we segregated the APIs into public-authenticated+authorized, internal-authenticated groups. For the public-authenticated+authorized, reused the user authentication with role authorization strategy. The public group also included anonymous access with no authentication strategy. We used the chummy auth library — friend (https://github.com/cemerick/friend), with some tweaks for super user access.
For the internal-authenticated group, there are many different strategies available. Some strategies involved sharing an access token and validating them. But this meant the tokens needed to be expired regularly, and in turn need a token exchange/refresh mechanism. We could have just created a user for the caller system. But it’s not really a user, as it could mess with the public-authenticated+authorized group. It felt as if there were nothing directly simple. Or we could have just said Screw you guys, and just split the system into two pieces — public and internal. That’s where our design principles kicked in. At Swym, as a growing startup, resources are far more precious than gold. In other words, we only build something if we are really, really convinced its worth it. Following were the constraints we placed on ourselves:
- Only one node/node group should be used for deployments, monitoring, etc.
- Only one API gateway/server should be running, i.e. one server process that handles the frequent public calls and less-frequent internal calls
- Internal auth should not interfere with public auth, so Network Firewall policies won’t work
After we had all that staring at us, the solution became quite clear. Good ol’ IP whitelisting! (C’mon now, that definitely sounds racist, my usage of that title is totally justified :)) We began searching for a Clojure library, as it felt we were encountering a fairly common need, and someone else must surely have solved it before us. But, nope, couldn’t find it.(If you do, please do share!)
We ended up building a small lightweight middleware on top of Clojure’s ring web framework to easily wrap IP whitelist strategy around the API group. And here is the code:
Currently, the middleware supports a CSV of allowed IP addresses and a pass-through for “dev” environments. It is hosted as a gist, doesn’t deem a clojar library container yet. It can be easily used around an API group, i.e. defroutes defined for ring.
Feel free to use the code as you please. Do comment on your love(or hate) for IP whitelisting.
Now the title. Why racist? Its obvious, no? Whitelist is allowed. Blacklist is banned. Even if the colors are reversed, still sounds kinda racist doesn’t it? :P
From all those colors, just pick any two. Imagine if we had a Papayawhip-list and Tomato-list. Sounds less racist right? Just saying. (Peachpuff and Papayawhiff look very similar unfortunately)
P.S: Other title ideas that were in contention, but less edgy — Colored Security, Security in black and white.