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:
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, andROUTE_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
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 inSERVICE_INSTANCE
the name of the service that you have just createdHOSTNAME
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
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
andX-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.
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.
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.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
andAUTH_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.
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
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 passwordpass1234
.
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 requestROUTER-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 headerX-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