Securing Non-Production Vercel Environments via Vercel Firewall

Published on
Authors

If you're using your own DNS and Firewall in front of Vercel, this article looks at how you can effectively secure your non-production environments so that they're only accessible from within your organisation's network using Vercel's Firewall.

The problem statement

If you have an existing edge-network/Firewall layer which you manage your DNS through and you can't easily re-locate that to or don't wish to move all of that security over to Vercel you will find that it's not possible to restrict access to Vercel hosted applications/environments from the outer edge layer.

When you're using SaaS based offerings, they are managing the infrastructure and hosting and updates/upgrades and it's all hidden away from us. Behind the scenes, they're sure to be using blue/green strategies to allow them to move your instances around so they can deploy their changes without you knowing and that is generally all driven by DNS and routing behind the scenes. For this reason (among others) SaaS based products don't provide static IPs unless you pay a premium for private networking (if it's even available).

Vercel uses DNS and it's own edge networking layer to configure environments and as a result, the DNS routing hop which is targeting Vercel must be publically available for Vercel to be able to hit the DNS endpoint to validate/complete it's own set up. You simply point your DNS to Vercel's provided target endpoint and it will auto generate Certificates and allow you to point each record to a Git Branch which is how you can then manage environments (via GitOps).

Vercel Environment DNS Configuration

Let's consider a topology like the one shown below...

Sample Composable Sitecore Architecture with an Edge network fronting Vercel

As mentioned above, Vercel is a SaaS based offering and as with most SaaS products, it doesn't have static IPs to white list at the external Edge/WAF end (or we're not prepared to pay the big $$ for private networking). As a result, it is not possible to block traffic by IP address at the external Edge/WAF layer as we need that to be open so that Vercel and external Edge/WAF can communicate and complete their DNS configuration.

Where does that leave us for Security? We want our security team to manage all our security at a single point and in the same way everything else is managed.

The answer is to use Vercel's Firewall layer to manage JUST the environment access restrictions and to do so via DNS-based rules.

Security Rules of Thumb

As a general rule of thumb, we can therefor employ an approach as follows:

  • All security controls that do no require IP-based configuration should be configured at the External WAF layer.

    • DDOS protection
    • Request monitoring
    • Logging
    • Any other security controls required
  • All security controls that require IP-based configuration should be configured at the Vercel layer

    • IP Restrictions to non-prod environments to block all traffic with allowances ONLY for:
    • Internal Company network
    • Any additional networks which aren't routed through your internal company network
    • Any additional specialty rules/exceptions which we need to add and manage

Each Vercel Project will define similar rules using the Vercel Rules-based Firewall configurations here such that:

  • Non-Production and Production all have their standard security managed at the external company WAF/Edge layer
  • Non-Production environments are locked down using the IP-Based restrictions mentioned above
  • Production is generally "open" so we don't need to apply IP restrictions (unless we're managing an Intranet or internal application)

Non-Prod Environments Deployment Protection

Non-Production Vercel Environments could be configured to only allow a range of Trusted IPs.

Deployment Protection Enabling this requires that you also enable Vercel Authentication protection which requires anyone that needs to access the non-prod environments would also need to be a Vercel user. We wanted the Non-Prod environments accessible by anyone on the business' network and so this has been deemed not a viable option as we only have a certain number of Vercel ‘seats’ in the licensing and is too much management overhead for the business.

The better solution is to use the Vercel Firewall.

The challenges with using Vercel's Firewall when you have an external WAF/Edge layer

At a glance, you would think that you could just dive into the Vercel Firewall and set up something like this:

In Vercel, environments are defined by “Preview” or “Production” such that anything which isn’t a “Production” environment (there is only one per project) is configured as “Preview on XXXX Branch”. This means the Environment Equals Preview conditional below caters to all non-prod environments.

Vercel Firewall Standard

On the surface this seems nice and easy, however as you will discover, the incoming IPs are always your External WAF/Edge proxy layer IPs and so we cannot do this when we have an external WAF/Edge layer in play...

What then?

This was a little bit of a challenge to work out and the fantastic team at Vercel were very accommodating with discussion and exploration of what might work best. At first, we thought we could make use of the X-Forwarded-For headers however as mentioned in their documentation here if you are behind a proxy like we are in this scenario, Vercel override the header and do not forward external IPs to prevent IP spoofing. There is also another x-vercel-forwarded-for header however this also wasn't able to work for the same reasons.

The Vercel Firewall Conditional list of options includes:

  • Request Path
  • Target Path
  • Method
  • User Agent
  • Request Header
  • Query
  • Cookie
  • Hostname
  • Environment
  • IP address
  • Protocol
  • Vercel Region
  • location-based string values
  • some other specific things which aren't relevant

In the end, we solved this problem by using a custom Request Header which only is applied at the external Firewall layer and is read at the Vercel Firewall layer and does not get passed through any further. Perfect. Because we were using Imperva as our external Firewall, they even include an OOTB custom header that contained the same values as the X-Forwarded-For contents for us in a header called "Incap-Client-Ip". Even better!

More complications

The trouble with X-Forwarded-For headers and the InCap-Client-IP header is that it is a string and actually contains a series of IPs in a comma separate list as described here. It uses a format

X-Forwarded-For: <client>, <proxy1>, <proxy2>

This is all very well however Vercel's current Firewall conditional rule set for request headers is all string-based functions

  • Equals
  • Does not Equals
  • Is any of
  • contains
  • Does not contain
  • Starts with
  • Does not start with
  • Ends with
  • Does not end with
  • Matches expression (regex)
  • Does not match expression (regex)
  • Exists
  • Does not exist

This meant that we had to check a string for ips in an x-forwarded-for like string and only allow the request through if it came from an IP that we wanted to allow.

The final solve

The Logic as mentioned to make this work is as follows:

  • If the request that comes into Vercel from the external Imperva WAF has a Request Header "Incap-Client-IP" (will always be the case and enforced by the Imperva WAF)
  • AND that request header DOES NOT CONTAIN one of our accepted IP's that we want to white list / Allow.
  • THEN Deny/Block the request
Vercel Firewall Settings1

We had to get creative with some Regex (use ChatGPT for this) to allow for "does not contain" with IP ranges

Vercel Firewall Settings2

You can also add a conditional for Environment to set it to only apply the rule to Vercel "Preview" (IE. Non-Prod) environments as well as required. This means that we're now happily only allowing access to the Vercel Application Non-Prod environments from the organisation's internal network.

Give it a test and confirm all is well and you should be golden.