Edit: Added a note about JWT header validation.
I have a couple internal systems that I run at home, and that I want to be able to access from outside. I want only my partner and myself to be able to access those systems, and I want that access to be as transparent as possible for her. For that, I decided to use Google’s Identity-Aware Proxy (IAP)!
IAP is a Google Cloud feature that allows you to implement Google’s BeyondCorp security model. The goal of BeyondCorp (and of IAP) is to get rid of corporate VPNs: the bane of the existence of office workers all around the world. In this model, corporate applications are accessed through a proxy that deals with authenticating and authorizing the user. That proxy is available directly on the Internet, removing the need for a VPN. When accessing an application behind IAP, the user is authenticated, authorized, and the connection is encrypted (with SSL).
In Google Cloud, IAP is a feature that you enable on an HTTPS Load Balancer. Until now, Google Cloud Load Balancers were only compatible with Google Cloud-hosted resources (virtual machines, Cloud Storage buckets, etc.). That meant that the only way to benefit from IAP for applications hosted on-prem was to setup another reverse proxy within Google Cloud. That’s how the IAP connector does it, using Ambassador.
Note: You don’t have to configure a VPN between your VPC and On-Prem, you can route the traffic from your reverse proxy to On-Prem over the public Internet.
The problem with that architecture is that it’s really not optimized: you need to setup a whole VPC, at least a VM (or even a managed instance group, or a Kubernetes cluster, if you want HA), and optionnally a VPN.
Fortunately, Google Cloud recently released a feature that went a bit under the radar: Internet network endpoint groups (Internet NEGs). This allows Google Load Balancers to have backends outside of Google Cloud! And, best of all, that backend can be a hostname, not only a static IP! That is really convenient for me, since my ISP doesn’t attribute static IPs. You can now simplify greatly the setup:
Note: If you want the traffic between the load balancer and your application to be encrypted, then your application has to listen on HTTPS, and you need to use that as a backend for the load balancer.
Here is how I did it, details will vary for you, of course.
- Configure your router to forward incomming traffic on your application’s port to the application.
- If you can, restrict that forwarding to
34.96.0.0/20
and34.127.192.0/18
(source). Check the_cloud-eoips.googleusercontent.com
DNS TXT record for the current list of IP ranges. - If that’s not possible on your router, then setup a firewall rule to restrict incomming traffic to those ranges.
- See the note about JWT headers for a better solution to secure your endpoint on-prem.
- If you can, restrict that forwarding to
- If your ISP gives you a dynamic IP (like me), then setup some kind of dynamic DNS to get a stable hostname.
- Reserve a static IP address on GCP.
- Point a DNS record that you want to use for your application (
myapp.example.com
for example) to the newly reserved static IP. - Create an Internet NEG that targets your home IP or home hostname, and the application port, following the documentation.
- Create an HTTP(S) Load Balancer:
- Use the newly created Internet NEG as backend service.
- Create at least two frontends: one for HTTP and one for HTTPS. Use the static IP you reserved earlier.
- Create a Google-managed SSL certificate with the hostname
myapp.example.com
.
After a few minutes, the load balancer will have initialized, and you should be able to access your application
through myapp.example.com
, without any authentication nor authorization. You can now enable IAP following
the documentation on your new load balancer.
Grant the IAP-Secured Web App User IAM role to anyone who needs to access the application.
Here you go! An On-Prem application, protected Google-style with Identity-Aware Proxy!
JWT header validation
While whitelisting IPs is easy to setup, there is no guarantee that they will not change. A better, more
resilient solution is to validate the X-Goog-Iap-Jwt-Assertion
header that IAP injects in the request. You can
use something like Envoy
for that. See the documentation for more information.