No-rewrite free TLS offloading, WAF and more for legacy web applications with Azure Front Door
Tags: azure, frontdoor, tls, waf
Published: 2021-11-05 15:27:29

You have a one or more legacy web applications, running on virtual machines and no time or resources to rewrite and implement security; you can get all those features and more just with infrastructure leveraging **Azure Front Door**.

There are two flavours we are going to explore:

1) **Front Door** forwarding requests to your VM on its public IP
2) **Front Door Premium** (preview at the time of writing) with private origins forwarding requests directly into your **VNET** without public internet exposure of the machine

### Azure Front Door forwarding requests to your VM's public IP

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ipgst3wuvz0t3hdv0itj.png)

In this scenario you can set up the **backend** of **Azure Front Door** directly to the **public IP** attached to the **VM**.

This has the following advantages:

* Works with **Azure Front Door** classic (GA)
* Simpler infrastructure and configuration

This approach has the downside of being **less** secure; using an **NSG** against the **VM** (**subnet**/**NIC** level) you can still ensure that the traffic originating from the internet will never hit your **VM** by allowing only traffic from the **Azure Front Door service tag**, this way, any other source will be blocked, with two pitfalls:

1) **DDoS** attacks could still block access to the **VM** (although they can be mitigated by adding standard **DDoS** protection for **VNETs**)
2) A sophisticated and knowledgeable attacker (perhaps an internal agent) who manages to find the **public IP** of the **VM**, could spawn up their own **Front Door** instance and point it to your **public IP**, bypassing the existing **Front Door** security.

Those threats can be still mitigated, but we will not explore that in this article; just to share them at high level:

You can filter incoming traffic by checking the `X-Azure-FDID` header that **Front Door** adds to the forwarded requests with its unique ID; using that and the **service tag** will ensure that the traffic is coming only from your **Front Door**.

If you still do not want to make any application changes, you can add an instance of **Application Gateway** and let it do this filtering for your before forwarding the traffic to your **VM**.

If you go through a threat modelling exercise and decide this risk stil needs mitigation, you can go for the second option below.

### Azure Front Door Premium forwarding requests to your VM's internal IP via Private Link

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkwd8a9pgdlo5gr5h850.png)

This approach improves significantly your security posture, but bears increased costs (**Front Door Premium** is required for private origins support) and a slightly more complex architecture and configuration (requires extra components such as a **Standard Load Balancer**, which also adds to the costs, and **Private Link**).

Using this approach, you can get completely rid of the **public IP** and project your **Front Door** instance directly into your **VNET**, eliminating the attack vectors of **DDoS** and **Front Door hijacking**.

Without the IP, an attacker has no endpoint to use beside **Front Door** which carries enhanced security and can optionally be set up with a **WAF** (Web Application Firewall), mitigating the most common attacks with several security rules.

Advantages:

* No public endpoint for the **VM**
* No **DDoS** risk
* No **Front Door hijacking** attack

Both the options enable your application to leverage advanced security without application changes. You get:

* Free **TLS** certificates with **Azure Front Door** also on your **custom domains** 
* **WAF** capabilities (protect against **OWASP** common attacks and more)
* Dynamic site acceleration
* Enables **HTTP/2**
* Global **HA** with load balancing if you have multiple instances of your application

### Multiple instances with path-based routing

Now, let's pretend you love this, but have multiple apps to secure behind **Azure Front Door** and want use a single domain in front of those applications:

```
mydomain.com/app1
mydomain.com/app2
...
```

You can easily set up routing rules to redirect all the traffic to your **backends** depending on the **URL** segment.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozeutd37bx4uuq2stxwz.png)

You just set up multiple **backend pools** in **Front Door** and create **routing rules** to forward the traffic that has `/webapp1/` to the backend 1.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xpw221hjs3xm27d7z1d.png)
 
Assuming again that you do not want to make any changes to your web application, you can also use the rules to rewrite the **URL** to avoid forwarding the `/webapp1/` and just keep the rest of the **URL**:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz98ffk3na5476jrmana.png)
 
So your application does not need to be aware of **Front Door** at all.

What if your application, not being aware, redirects then the user to a `/*` endpoint?

Imagine that your application calls a `/api/dosomething` or `/login`, that will not preserve the `/webapp1`, hence **Front Door** will not know where to direct the traffic.

Once again, we can have a pure infrastructure solution, without application changes.

The idea is that: on a first call to `/webapp1`, **Azure Front Door** can alter the response and add a new header value; this header can easily be a `Set-Cookie` header that sets a value unique to the `web app 1` backend; on subsequent calls, the browser will include that **cookie** and the **Front Door rules engine** can override the routing configuration based on the content of the returned `Cookie` header and make the correct decision to forward `/*` traffic to `/webappX/*`. See configuration below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yddbnhan0jpajpy9pz2l.png)
 
This sets the **cookie** to the correct **backend** on the initial call, the full header value contains: 

`BACKEND=backendIdentified; Domain=mydomain.com; Path=/`

It requires to specify the path, if omitted, it will assume that the **cookie** will be specific to `mydomain.com/webapp1/*` and will not work also on `mydomain.com/*`.

Now the rule to override the behaviour on calls to `mydomain.com/*`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cah9x5eq779ssxh22w7.png) 

Now you need to add the **rules engine** rules to your routing configuration and you are done.

You can optionally set up an error page **backend** if the **cookie** is not present and someone requests directly `mydomain.com`.

Et voilà, with few simple infrastructure changes, you have seamlessly added free **TLS** and **WAF** and more to your legacy web applications without changing a single line of code.