If you’re a software developer, or have been involved in the development of a software project in any capacity, then you will be aware that at some point you will inevitably need to make use of external services to enhance your application. These services, called “APIs”, could be used for any number of things, such as importing data, payment processing (e.g. Stripe), posting to social media platforms, and so on.
Most APIs are built to be generally available to the public, and so connecting to them is simple, often as easy as just generating an API key. You then authenticate the connection with the key, and your application is able to communicate with the API.
However, every now and then, it isn’t so simple. Occasionally you will need to connect to an API with more stringent security requirements, as we recently had to here at reinteractive. When the operators of such an API don’t want the whole world connecting to them, they can usually just not give out any API keys to developers. But sometimes, that’s not enough, and they don’t even want outside developers to be able to connect to their API, let alone authenticate. There could be many reasons for this; maybe the information that the API contains is confidential or classified, or maybe the API is behind a corporate firewall or needs to be available for only a specific geographic area.
In such cases, the operators of the API might configure their servers to block requests unless they originate from an IP address on a whitelist of allowed IPs. Depending on how your application is deployed, this might not be a problem; if your server environment has static IP addresses, you can simply have them add your IPs to the whitelist. Problem solved.
But what if you don’t have a static IP address? What if, for example, your application is hosted on Heroku, which doesn’t provision each application its own static IP addresses, and instead dynamically allocates them from a subset of the IP ranges of the AWS EC2 instances that they run on? In such a case, you would have to ask the operators of the API to whitelist a broad range of IP addresses which would effectively amount to the entire AWS region, which they would likely not be too happy about!
To connect to the API in such a scenario, you will need to “proxy” your outbound HTTP requests through another service, which does have its own static IP addresses. By doing so, you’re adding another hop to the route that your requests take to reach the API, with the effect being that your requests to the API will come from a static IP address that can be whitelisted.
We’ll assume that your application is already deployed to Heroku for this. As for which proxy service we will use, there are quite a few to choose from in the Heroku third-party add-on library. We will use Fixie.
First, add the Fixie add-on to your Heroku environment using the Heroku CLI. We’ll start with the free plan for now:
$ heroku addons:add fixie
This does a few things:
- The Fixie add-on is added to your Heroku environment.
- A Fixie proxy instance is provisioned for your application to use. This allocates your application a URL for you to proxy your requests through, and a set of static IP addresses.
FIXIE_URLenvironment variable is added to your Heroku environment configuration, set to the URL mentioned above. This means you don’t have to hard-code it into your application; you just reference the environment variable. You can test this by running
heroku config:get FIXIE_URL.
You would have seen something like the following output from the above command, so next, you will want to send those two static IP addresses to the operator of the API and ask them nicely to add them to the whitelist.
Adding fixie to [your application name here]... done, v1 (free)
Your static IP addresses are 184.108.40.206, 220.127.116.11
Lastly, you need to update your code to make use of the proxy. Presumably, your Ruby on Rails application is using an HTTP client to make requests, such as the built-in Net::HTTP, HTTParty or Faraday. The implementation will of course be a little different for each client, but it’s the same principle. The Fixie documentation has examples for the common clients.
For Faraday, as an example, it’s as easy as setting the
proxy argument when creating the connection object:
Faraday.new(url: 'http://super.secret.api.com', proxy: ENV['FIXIE_URL'])
And that’s it! Now, any HTTP requests made through that connection will be proxied via your Fixie instance (located in the
FIXIE_URL environment variable), and will appear to the external API’s firewall to originate from the whitelisted static IP addresses, and thus be allowed through.
The only thing you need to do going forward is keep an eye on the amount of requests you make; the free tier of Fixie has limits on the number of requests and total bandwidth. You will likely want to upgrade the plan as you get a better idea on how many requests your application will be making via Fixie.
But for now, you’re all done. Your application can now access APIs that require static IPs, despite being on Heroku.