Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 

For sensitive applications it's often advisable to follow a defense in depth approach. By layering multiple indepenent security controls the risk of a successful attack can be reduced to a minimum.

When the clients that are allowed to connect to a system are well-known and have fixed IP address ranges (such as from a corporate VPN service), applications can first filter for these IP addresses. This is usually done by implementing network ACLs or a firewall that is dedicated to filtering these requests before forwarding them.

In combination with strong authentication like mTLS, this can significantly reduce the risk of a successful attack. You should never rely on IP-based allow-listing alone to protect any resource.

Introducing X-CF-True-Client-IP Header

Up until now this was a difficult task when running as an application on SAP BTP, Cloud Foundry runtime and environment as the IP address of the client is not readily available to the app. When a client connects to the platform, it connects to a central load-balancer which handles (among other things) the termination of the TLS connection. When the request reaches the application, the connection is initiated from the load-balancer which uses an internal IP address that cannot be used for such traffic filtering. This gets even more complicated when route-services are in use.

To make building custom filters as easy as possible we have introduced a new request header which will be set in every reqeusts sent to applications: x-cf-true-client-ip. This header contains a single IP address (IPv4 or IPv6) that was used to connect to the platform when the request was initiated.

When the application has a route-service configured, the reported IP address will be the same for the route service and the application, always pointing to the original client which initiated the request.

How to Use X-CF-True-Client-IP

There are two main options to implementing IP-based allow-listing using x-cf-true-client-ip:

  • directly in the main application logic, or
  • in a route service which is then bound to the application route.

In either case you will need to load a pre-defined allow-list, and for every request retrieve the IP from the header and check it against the configured allow-list. To demonstrate the functionality we have implemented a sample route-service with a dummy-app as backend:

Sample Application Using Route-Services

To demonstrate this, we have added two sample applications to our cf-routing-samples. One is the route service itself at ip-allow-listing-route-service/ and the second one is a dummy to act as the protected backend at ip-allow-listing-route-service/ok/.

Deploying the applications can be done via the CF CLI and provided manifest:

# First, clone the repository.
git clone https://github.com/SAP-samples/cf-routing-samples
cd cf-routing-samples/ip-allow-listing-route-service
# Login to CF.
cf login [...]
cf target -o <org> -s <space>
# Push the applications.
cf push --var domain=<domain> --var suffix=<suffix> -f ./manifest.yml

Make sure to replace <domain> with the domain of your environment and provide a unique suffix for <suffix> to prevent collisions on the name of the route. Eventually the CLI should report success and you should see your running applications with 'cf apps'. To ensure our target application works, you can use curl:

curl https://ok-<suffix>.cfapps.eu12.hana.ondemand.com

This should print 'ok'. To protect our ok application with the allow-listing route-service we still need to configure them together. The first step is to turn the allow-listing app into a route-service we can bind to. A route service intercepts requests to an app and can decide whether to forward a request to the actual app or not. We use this mechanism to block requests that are originating from a source that is not in the allow-list.

cf create-user-provided-service allow-listing -r https://ip-allow-list-rs-<suffix>.cfapps.eu12.hana.ondemand.com

Make sure to provide the same suffix here as you did when pushing the application. Now we can bind the route service to the route of our target application:

cf bind-route-service <domain> --hostname ok-<suffix> allow-listing

To view the finished setup we can use the 'cf routes' command which will output something like this:

space     host                        domain     port   path   protocol   app-protocol   apps                             service instance   options
<space>   ip-allow-list-rs-<suffix>   <domain>                 http       http1          ip-allow-listing-route-service
<space>   ok-<suffix>                 <domain>                 http       http1          ok                               allow-listing

By default, the route-service will allow all traffic from every IPv4 address, so accessing the application should still work:

curl -4 https://ok-<suffix>.cfapps.eu12.hana.ondemand.com

If you now delete the single line from the allow-list and push the app again using the same command as before (no need to re-do the service, that persists), you will no longer be able to access it:

error: forbidden: address '192.0.2.5' is not in allow-list

Feel free to take a look at the implementation but please note that this code is only for demonstration purposes and must not be used in production / live systems.

There are some pointers in the code comments on what to do differently, but as always use good judgement and security best practices. The mechanism, by which to provide the specific allow-list to the route service is also left as exercise to the reader.

References

1 Comment
Labels in this area