Skip to main content

Route services

Tenants may wish to apply some processing to requests before they reach an application. Common examples of use cases are authentication, rate limiting, and caching services.

If you need to run a private app that can only be accessed by other apps in the same space, you should refer to the documentation on private apps.

Cloud Foundry allows the tenants to bind application routes to route services. A route service acts as a full proxy. Once it is bound to a route, the platform routing layer will send every request for that route to the route service endpoint. The route service can then process the incoming request, proxy it back to the original application, and finally process the response request before returning it to the original client.

Using route services has some consequences to be aware of:

  • Every request will include additional latency as it will be proxied through the Routing Service.
  • The route service will be able to access all the request content in clear.
  • The route service would become a critical point of failure, and if it is not available, the application will not be available for the end users.

User-provided route services

Tenants can define their own route service instance by using a user-provided service instance that points to any HTTPS service. This endpoint must fulfill the following requirements:

  • It must be a HTTPS endpoint with a valid certificate.
  • It can be a application running in the platform itself or an external service on the Internet.
  • It must be reachable from the platform (ie. not blocked by a firewall or in a private network).
  • It must implement the route service protocol

This is how you define an user-provided route service instance and map it to the route of your app:

  1. From the command line, run:

    cf create-user-provided-service SERVICE_INSTANCE -r ROUTE_SERVICE_URL
    

    where SERVICE_INSTANCE is a unique, descriptive name for this route service instance, and ROUTE_SERVICE_URL is the url of the route service endpoint; for example:

    cf create-user-provided-service my-route-service -r https://route-service.example.org
    

    The service will be immediately ready to be used, and you can query its status by running:

    cf service my-route-service
    
  2. Bind this route service instance to the application route

    cf bind-route-service DOMAIN SERVICE_INSTANCE --hostname HOSTNAME
    

    where:

    • DOMAIN is your app domain, and varies depending on which region your app is hosted in
    • SERVICE_INSTANCE the name of the service that you have just created
    • HOSTNAME the host or app name assigned to the app

    For example, if your app is named myapp and is hosted in the London region:

    cf bind-route-service london.cloudapps.digital my-route-service --hostname myapp
    
  3. You can list the routes of the current space to see the applications and route services bound to them:

    cf routes
    

    If the route service endpoint is not responding correctly, you might get the following response when querying the route:

    502 Bad Gateway: Registered endpoint failed to handle the request.

    If you get this error, double check that the endpoint is working and reachable from the platform, that it is using a valid SSL certificate, that responds timely and that it implements the route service protocol.

Implement a route service

A route service can be implemented as a HTTPS application that will receive all requests for the associated route from the Cloud Foundry router. The request will contain the following additional headers:

  • X-CF-Forwarded-Url header contains the URL of the application route. The route service should forward the request to this URL.
  • X-CF-Proxy-Signature and X-CF-Proxy-Metadata: Signature and metadata used by the platform route itself to validate the request sent by the route service. The route service must always include these headers.

The route service must proxy back the request to the application route defined in header X-CF-Forwarded-Url within 60 seconds, otherwise the request will be refused. In addition, the total time to process the request and send a response back must be within 900 seconds.

Route service request life cycle

You can refer to the Cloud Foundry documentation on route services for more information.

Example: Route service to add username and password authentication

In the following example we will add a route service to provide basic HTTP authentication to the bound routes. This can be used to restrict access to a beta application.

An example of such a route service application code can be found in the Cloud Foundry basic auth route service. Please note this is a proof-of-concept and is not intended to run in production.

We will deploy it as an app in the platform itself. Then we will bind this route service to an app named myapp, deployed in the London region and therefore accessible at https://myapp.london.cloudapps.digital.

  1. Deploy the route service as any other other application. Do not start it yet, as we need to configure it first.

    git clone https://github.com/alphagov/paas-cf_basic_auth_route_service
    cd paas-cf_basic_auth_route_service
    cf push my-basic-auth-service-app --no-start
    

    Note: You might want to change my-basic-auth-service-app with a different name to avoid conflicts with other tenants’ apps.

  2. This example route service can only authenticate one user with fixed username and password. Choose any value for username and password then pass them to the application using environment variables AUTH_USERNAME and AUTH_PASSWORD. Finally the route service can be started:

    cf set-env my-basic-auth-service-app AUTH_USERNAME myuser
    cf set-env my-basic-auth-service-app AUTH_PASSWORD pass1234
    cf start my-basic-auth-service-app
    

    The new service is serving in https://my-basic-auth-service-app.london.cloudapps.digital.

  3. Register the route service endpoint as an user-provided service instance:

    cf create-user-provided-service my-basic-auth-service -r https://my-basic-auth-service-app.london.cloudapps.digital
    
  4. Finally, bind the route service to the application route:

    cf bind-route-service london.cloudapps.digital my-basic-auth-service --hostname myapp
    

    The application in https://myapp.london.cloudapps.digital will now ask for basic HTTP authentication, with login myuser and password pass1234.

Example: Route service to add IP address authentication

GDS maintains an example nginx app which you can use as a route service to add IP address authentication. This ensures that only HTTP requests originating from a trusted set of IP addresses can access your app.

This example app uses the X-Forwarded-For header added by the GOV.UK PaaS routers to HTTP requests originating from outside the platform.

Using an IP address to authenticate HTTP requests is not sufficient as a standalone authentication mechanism. You should use other authentication and authorization methods in your apps.

Note: the example nginx configuration used in this app will not work out of the box when used with requests routed through a CDN. This includes requests routed through our own AWS CloudFront-based cdn-route services. For these to work, you need to add set_real_ip_from entries to nginx.conf for all IP ranges the CDN may forward traffic from. For CloudFront, this includes potentially hundreds of address ranges which are published by AWS through a JSON file. These ranges are not completely static and you will need to update them occasionally.

Using the X-Forwarded-For header

The X-Forwarded-For header is useful for working out the source IP address of the originating HTTP request, and can be used for authentication, or analytics.

The GOV.UK PaaS routers set headers in the following format: X-Forwarded-For: SOURCE-IP-ADDRESSES, ROUTER-INTERNAL-IP-ADDRESS, where:

  • SOURCE-IP-ADDRESSES are the source IP addresses of the original request
  • ROUTER-INTERNAL-IP-ADDRESS is the internal IP address of the router handling the request

For example: X-Forwarded-For: 208.80.152.201, 10.0.0.49

If you are using the X-Forwarded-For header for IP address authentication, then you should trust the CIDR range 10.0.0.0/8 to set the X-Forwarded-For header.

Proxied requests

If the X-Forwarded-For header is already set when the router receives the request, then the router appends the source IP address of the request and router’s IP address to the header.

For example:

  • an HTTP request sent from 208.80.152.201 to the router has the header X-Forwarded-For: 1.2.3.4
  • the router loopback IP address is 127.0.0.1
  • the router internal IP address is 10.0.0.49

The X-Forwarded-For header presented to your app will be: X-Forwarded-For: 1.2.3.4, 208.80.152.201, 10.0.0.49