ReverseProxy module for Play!
What is ReverseProxy?
ReverseProxy allows developers to configure web applications to
automatically switch between the HTTP and HTTPS protocols per page
when used behind a front end Reverse-Proxy server such as Apache, Nginx or Lighttpd.
Configuration is done in Controllers using annotations.
Requirements
- A Reverse-Proxy server configured
- For SSL, a certificate and its key
- A Play! app
Configuration
Set the following properties in conf/application.conf
bc.
reverse_proxy.http.address=127.0.0.1
reverse_proxy.http.port=80
reverse_proxy.https.port=443
- reverse_proxy.enable – For enabling or disabling the switching between the Play-App and the Reverse-Proxy.
- reverse_proxy.http.address – The Reverse-Proxy address.
- reverse_proxy.http.port – The Reverse-Proxy HTTP listening port.
- reverse_proxy.https.port – The Reverse-Proxy HTTPS listening port.
HTTPS support
In order to communicate between Reverse-Proxy server and Play built-in server either by HTTP or HTTPS protocol,
Play needs Java keystore or simple cert and key files and _since you have to create a Certificate and its Key for the Reverse-Proxy server,
you can use the same ones_.
To start an HTTPS connector for your application, just declare the https.port configuration property in your application.conf file:
http.port=9000
https.port=9443
- http.port – The built-in server HTTP port, it must be configured explicitly
- https.port – The built-in server HTTPS port, it must be configured explicitly
Disabling ReverseProxy module
If you wish or have to disable this module by any reason without removing it from the dependencies, just set the following property in you application.conf file:
reverse_proxy.enable=false
In this case, if you have configured http.port and https.port, the process for switching process between HTTP and HTTPS schemes will use those configured ports.
If you only have configured http.port, this will be the only port used, and won’t be any switching process between HTTP and HTTPS schemes.
Configuration Exception
If the above properties configuration are not correct, the module wil throw an configure exception and will stop the application.
Certificates
You need to put your certificates in the conf directory. Play supports X509 certificates and keystore certificates. The X509 certificates must be named as follow:
host.cert for the certificate and host.key for the key. If you are using keystore, then, by default it should be named certificate.jks.
If you are using X509 certificates, then the following parameters can be configured through your application.conf:
# X509 certificates
certificate.key.file=conf/host.key
certificate.file=conf/host.cert
# In case your key file is password protected
certificate.password=secret
trustmanager.algorithm=JKS
In case your are using keystore:
keystore.algorithm=JKS
keystore.password=secret
keystore.file=conf/certificate.jks
Note that the values above are the default ones. If you use these default values, they are not required to be explicitly in application.conf file,
however, if your certificate doesn’t use the default names, you must add your custom values.
certificate.key.file=conf/localhost.key
certificate.file=conf/localhost.cert
You can generate self signed certificates using openssl:
- Generate a Private Key
openssl genrsa -des3 -out host.key 1024
- Generate a CSR (Certificate Signing Request)
openssl req -new -key host.key -out host.csr
- Remove Passphrase from Key
cp host.key host.key.org
openssl rsa -in host.key.org -out host.key
- Generating a Self-Signed Certificate
openssl x509 -req -days 365 -in host.csr -signkey host.key -out host.crt
- Installing the Private Key and Certificate
Depends on the Reverse-Proxy server.
- Configuring SSL Enabled Virtual Hosts
Depends on the Reverse-Proxy server.
If you are using the java keystore mechanism, then the following properties can be configured in your application.conf:
# Keystore
ssl.KeyManagerFactory.algorithm=SunX509
trustmanager.algorithm=JKS
keystore.password=secret
keystore.file=certificate.jks
The values above are the default values.
You can generate self signed certificates using Java Keytool:
keytool -genkey -keyalg RSA -alias selfsigned -keystore certificate.jks -storepass secret -validity 360 -keysize 1024
Self Signed Certificates Warning
In case you don’t know, when you use a Self Signed Certificate, the Web Browsers always warns about it. If you plan to use your app
in a Production Environment, then you should buy a certificate from a Certificate Authority like VeriSign, Thawte, Geotrust, GoDaddy, Comodo.
Usage
Add this module to your dependecy.yml file
Controllers
There are two annotation for forcing the communication with a Reverse-Proxy server:
- @SwitchScheme is strictly for Actions and has two optional values:
- type = SchemeType.HTTP (default value) or SchemeType.HTTPS, for using either HTTP or HTTPS protocol.
- keepUrl = true or false (default value), for storing the ReferredUrl into a cookie.
- @GlobalSwitchScheme is strictly for Controllers and has one optional value:
- type = SchemeType.HTTP (default value) or SchemeType.HTTPS, for using either HTTP or HTTPS protocol.
With GlobalSwitchScheme all Methods from a Controller will use the specified or default SchemeType value, unless
some of its methods are annotated with SwitchScheme, in which case, those method will override the global SchemeType value.
You shall annotate a method that requires authentication/authorization as:
@SwitchScheme(type = SchemeType.HTTP, keepUrl = true)
public satic void restrictedMethod(){
...
}
In this way, a cookie is created for storing the REFERRED_URL and your app shall redirect to a authentication view (e.g., login);
when the app validates a user is authentic, the app shall redirect to the restrictedMethod route (URI Pattern).
@Interceptor
If you are going to use interceptors it is suggested to decouple the controller part from the interceptor part
and annotate the Interceptor Class with @Interceptor, so that when the plugin enhances the controllers, skips the interceptors
See SecureController and SecureInterceptor for clarification, both of them included in this module.
Invoking Actions
An Action from another Action may be INVOKED in one of the following ways:
- By regular Java call, i.e., Controller.action()
- By reverse routing, i.e., UrlUtility.redirectByReverseRouting(“Controller.action”) -> Invoke GET /action Controller.action
- By render, i.e., render(“@action”) -> renders views/Controller/action.html
public class ControllerA extends Controller {
public static void actionOne() {
ControllerA.actionTwo();
}
public static void actionTwo(){
// Do logic...
// Invoked from actionOne
render()
}
}
---
public class ControllerA extends Controller {
public static void actionOne() {
ControllerA.actionTwo();
}
-
// Let's supposed this action uses HTTPS
public static void actionTwo(){
// Do logic...
// Invoked from actionOne
render()
}
}
Remember an Action must be public static void
Invoking an Action from another Action of the same Controller by Render
For invoking an Action from another Action of the same Controller, you must call one of the following methods:
- action()
- Controller.action()
- package.Controller.action()
- render(“@action”)
- render(“@Controller.action”)
- render(“@package.Controller.action”)
public class ControllerA extends Controller {
public static void actionOne() {
render("@actionTwo"); // renders views/Controller/actiontwo.html
}
public static void actionTwo(){
// Do logic...
// Invoked from actionOne
render()
}
}
Invoking an Action from another Action of different Controller by Reverse Routing
For invoking an Action from another Action of different Controller, you may use its Java Call from conf/routes file
GET /actionTwo ControllerB.actionTwo
public class ControllerA extends Controller {
public static void actionOne() {
UrlUtility.redirectByReverseRouting("ControllerB.actionTwo"); // Invoke GET /action
}
}
public class ControllerB extends Controller {
public static void actionTwo(){
// Do logic...
// Invoked from ControllerA.actionOne
render()
}
}
Note UrlUtility.redirectByReverseRouting method expects a Java Call as argument, which calculates the Reverse Routing,
and does the redirection according to the URI Pattern (in this example: /action).
Invoking an Action from another Action of different Controller by Java Call
For invoking an Action from another Action of different Controller, you may invoke it directly as a regular Java Call
public class ControllerA extends Controller {
public static void actionOne() {
ControllerB.actionTwo();
}
}
public class ControllerB extends Controller {
public static void actionTwo(){
// Do logic...
// Invoked from ControllerA.actionOne
render()
}
}
Secure
Do NOT use the Secure module bundled with Play, instead use SecureController (and its inner class Security) and SecureInterceptor included with this module and implement your own
SecureController.Security for Authentication/Authorization.
See demo-with-secure app for clarification.
Protect
For a better Authentication/Authorization mechanism, use ProtectController (and its inner class Safety) included with this module, which integrates with
Deadbolt module, and implement your own ProtectController.Safety.
See demo-with-deadbolt app for clarification.
Prohibited
There’s a ProhibitedController controller, included with this module, intended to render a forbidden view when a user doesn’t have permission to access a certain area.
See demo-with-deadbolt app for clarification.
Reverse-Proxy Server: static content
If you configured a Reverse-Proxy server for serving static content, then all content withing public folder from other modules must be copied to public
folder of your main application.
Tests
This module has been tested with Play built-in server as standalone in conjunction with Nginx and Apache.
JEE App Container
It has been tested, partially, with Jetty-1.6.26...
Sample application
Two sample demos are part of the distribution.
Don’t forget to run play deps so that it resolves dependencies.
There’s also a example for configuring Nginx as Reverse-Proxy server.
Deprecated classes
The following classes have been annotated as @Deprecated
- play.modules.reverseproxy.ReverseProxyEnhancer
- controllers.reverseproxy.ReverseProxyInterceptor
They are not used any more, but they remain in the source code as reference/example of Enhancer and Interceptor, respectively.
Play-Utilities
This module uses a small API: play-utilities
TODO
- A script for overwrite the layout and css of ReverseProxy.secure.Secure
- A script for overwrite the layout and css of ReverseProxy.deadbolt.Protect
- A script for copying all the static content from other modules into the main app
Testing Contributions
If you test this module with another Reverse-Proxy server (e.g., Lighttp) or with another JEE Container, I would really appreciate if publish the results.
Thanks ahead of time.
Credits
Author: Omar O. Román
Coauthor: Omar García G.