App Engine To App Engine Communication through a Firewall

Image for post
Image for post

Background

App Engine firewall support has been around for over a year now. It provides basic whitelist/blacklist functionality for IPv4 and IPv6 IP ranges in CIDR notation. There are many reasons why you might want to control access to your App Engine hosted application via a whitelist. However, this limited functionality becomes problematic when you want to allow access from your other applications running in App Engine across different GCP projects. This is because App Engine does not support static IP addresses. This has to do with GCP’s way of optimizing the network path for an end user communicating with an App Engine application.

Solutions

There are a few ways to handle this problem. It comes down to the types of consumers you need to allow through the firewall.

Whitelist all the Possible App Engine IPs

App Engine’s current range of outgoing IP addresses are encoded in the sender policy framework (SPF) record of _cloud-netblocks.googleusercontent.com. You need to recursively perform DNS SPF lookups to resolve the entire list of IP ranges. The “Static IP Addresses and App Engine apps” section of this page discusses the whole process. In the end you may get one or more sets or ranges to add to your whitelist looking like this:

Non-authoritative answer:
_cloud-netblocks1.googleusercontent.com text = “v=spf1 ip4:8.34.208.0/20 ip4:8.35.192.0/21 ip4:8.35.200.0/23 ip4:108.59.80.0/20 ip4:108.170.192.0/20 ip4:108.170.208.0/21 ip4:108.170.216.0/22 ip4:108.170.220.0/23 ip4:108.170.222.0/24 ?all”

On its own, this is probably the least secure because it involves adding a very large range of IP addresses. An attacker could set up their own App Engine app and get access to your application if there isn’t any additional security beyond the firewall. Additionally, this IP range is dynamic. You will have to make sure to update this range regularly.

Create a Compute Engine Proxy

Technically, this could be any kind of proxy server. But, if you can keep this proxy in your App Engine region, you’ll get better performance. This solution is probably the best option because it allows you to extend it for better all around perimeter security and could be set up to work any type of web consumer.

Here you set up Compute Engine VM with a static IP address which you can whitelist. Then, you can install your proxy server and have all consumers of your App Engine application that can’t provide their own static IP address go through there. This Stack Overflow answer briefly gives an example of doing this with an Ubuntu Compute VM and Squid Proxy Server.

This option is a fair amount of more work in that you now have a VM to create, set up and manage. But, now you only need to add only one additional IP address to the whitelist and this will allow any consumer, not just other App Engine services to communicate with your firewall protected service.

What Worked for Us

Our team needed to provide access to other App Engine services that we have control over. In our case, these services are Java 8 App Engine Standard services.

URL Fetch Service

The App Engine URL Fetch service can be used to communicate from one App Engine service to another and more importantly, all communication will come through a single IP address if used from App Engine Standard: 0.1.0.40. You also need to include 10.0.0.1 if coming from App Engine Flex.

Our team is using Java 8, so we are able to make use of the UrlFetchServiceFactory in our consuming application’s code. We then can create a common method to wrap any calls to our firewall protected App Engine service. This example uses an HTTP GET call to keep things simple.

import com.google.appengine.api.urlfetch.*;

Here, we simply use the UrlFetchServiceFactory to get access to an instance of the URLFetchService. We then create our HTTPRequest and pass it into the fetch method of the URLFetchService instance.

It’s important to note that we had to set the doNotFollowRedirects option when creating our HTTPRequest. Otherwise, the IP address would not originate from 0.1.0.40.

You are able to pass request headers by calling your HttpUrlConnection object’s setRequestProperty() method. You can use this to help identify your consuming App Engine service.

That’s it, in the end our firewall rules can now be configured to:

Allow 0.1.0.40

Deny *

Caveats

  1. The App Engine URL Fetch Service is only available for Python, Java, PHP and Go. So it does not support Node.js.
  2. The documentation mentions adding this line to your Java 8 app’s appengine-web.xml:
<url-stream-handler>urlfetch</url-stream-handler>

However, doing so automatically wraps other calls with the URL Fetch Service which isn’t supported in places like the Google Compute Metadata server. The lack of support is pointed out in the documentation for URL Fetch Service, but it isn’t clear that certain calls will fail by adding the above. For instance we are accessing our Google Compute Storage by passing in our App Engine service’s Compute Engine Credentials via:

StorageOptions.newBuilder().setCredentials(ComputeEngineCredentials.create()).build().getService();

This call began failing for us and debugging it was challenging. In the end, we did not need to add the line to our appengine-web.xml.

3. It’s very important to access the firewall protected App Engine service via it’s appspot.com domain name and not your custom domain name or again the call will be blocked.

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store