![]() Version: 9.3.11.M0 |
private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ... scalability guidance for your apps and Ajax/Comet projects ... development services for sponsored feature development
Typical website deployments have Apache (or Nginx) configured as reverse proxy to talk to one or more backend Jetty instances. This configuration cannot be used for HTTP/2 because Apache does not yet support HTTP/2 (nor does Nginx).
HAProxy is an open source solution that offers load balancing and proxying for TCP and HTTP based application, and can be used as a replacement for Apache (or Nginx) when these are used as reverse proxies, and has the major benefit that supports HTTP/2. It also offers load balancing and a ton of other features, so you can probably use it as a replacement for Apache (or Nginx).
The deployment proposed here will have HAProxy play the role that Apache (or Nginx) usually do: to perform the TLS offloading (that is, decrypt and encrypt TLS) and then forwarding the now clear-text traffic to a backend Jetty server, speaking either HTTP/1.1 or HTTP/2.
The instructions that follow are for Linux.
You will need HAProxy 1.5 or later, because it provides support for SSL and ALPN, both required by HTTP/2. Most Linux distributions have the HAProxy package available to be installed out of the box. For example on Ubuntu 15.04:
$ sudo apt-get install haproxyAlternatively you can download the HAProxy source code and build it on your environment, by following the README bundled with the HAProxy source code tarball.
Note
HAProxy supports ALPN only if built with OpenSSL 1.0.2 or greater. Alternatively, HAProxy supports NPN when built with OpenSSL 1.0.1 or greater. You must upgrade OpenSSL if you have a version earlier than 1.0.1.
Use
haproxy -vvto know with which OpenSSL version HAProxy has been built.
HAProxy will perform the TLS decryption and encryption much more efficiently than a Java implementation.
HAProxy will need a single file containing the X509 certificates and the private key, all in PEM format, with the following order:
Let’s use keytool to generate a self signed certificate:
$ keytool -genkeypair -keyalg RSA -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -ext SAN=DNS:domain.com
What is your first and last name?
[Unknown]: *.domain.com
What is the name of your organizational unit?
[Unknown]: Unit
What is the name of your organization?
[Unknown]: Domain
What is the name of your City or Locality?
[Unknown]: Torino
What is the name of your State or Province?
[Unknown]: TO
What is the two-letter country code for this unit?
[Unknown]: IT
Is CN=*.domain.com, OU=Unit, O=Domain, L=Torino, ST=TO, C=IT correct?
[no]: yesThe above command will generate a self signed certificate and private key for domain.com and subdomains, stored in the keystore.p12 file in PKCS#12 format.
We need to extract the certificate and the private key in PEM format.
To extract the certificate into certificate.pem:
$ keytool -exportcert -keystore keystore.p12 -storetype pkcs12 -storepass storepwd -rfc -file certificate.pemTo export the private key into private_key.pem:
$ openssl pkcs12 -in keystore.p12 -nodes -nocerts -out private_key.pem -passin pass:storepwdAt this point you just need to concatenate the two files into one, in the correct order:
$ cat certificate.pem private_key.pem > domain.pemThe domain.pem file will be used later by HAProxy.
Now we can setup haproxy.cfg to configure HAProxy.
This is a minimal configuration:
global tune.ssl.default-dh-param 1024 defaults timeout connect 10000ms timeout client 60000ms timeout server 60000ms frontend fe_http mode http bind *:80 # Redirect to https redirect scheme https code 301 frontend fe_https mode tcp bind *:443 ssl no-sslv3 crt domain.pem ciphers TLSv1.2 alpn h2,http/1.1 default_backend be_http backend be_http mode tcp server domain 127.0.0.1:8282
The HAProxy configuration file works in the following way.
The fe_http front-end accepts connections on port 80 and redirects them to use the https scheme.
The fe_https front-end accepts connections on port 443 and it is where the TLS decryption/encryption happens.
You must specify the path to the PEM file containing the TLS key material (the crt domain.pem part), the ciphers that are suitable for HTTP/2 (the ciphers TLSv1.2 part), and the ALPN (or NPN if you are using old OpenSSL versions) protocols supported (the alpn h2,http/1.1 part).
This front-end then forwards the now decrypted bytes to the back-end in mode tcp. The mode tcp) means that HAProxy will not try to interpret the bytes as HTTP/1.1 but just opaquely forward them to the back-end.
The be_http back-end will forward (again in mode tcp) the clear-text bytes to a Jetty connector that talks clear-text HTTP/2 and HTTP/1.1 on port 8282.
The Jetty setup follows the usual steps of having Jetty installed in the JETTY_HOME directory, creating a JETTY_BASE directory and initializing it using Jetty’s command line tools.
You must enable the http2c module, that is the module that speaks clear-text HTTP/2.
Since the http2c module depends on the http module, the http module will be enabled transitively, and the final setup will therefore support both HTTP/2 and HTTP/1.1 in clear text.
Additionally, you will also enable the deploy module to be able to deploy a sample web application:
$ JETTY_BASE=haproxy-jetty-http2
$ mkdir $JETTY_BASE
$ cd $JETTY_BASE
$ java -jar $JETTY_HOME/start.jar --add-to-start=http2c,deployNow let’s deploy a demo web application and start Jetty:
$ cd $JETTY_BASE
$ cp $JETTY_HOME/demo-base/webapps/async-rest.war $JETTY_BASE/webapps/
$ java -jar $JETTY_HOME/start.jar jetty.http.host=127.0.0.1 jetty.http.port=8282Now you can browse https://domain.com/async-rest (replace domain.com with your own domain, or with localhost, to make this example work).
Note
You want the Jetty connector that listens on port 8282 to be available only to HAProxy, and not to remote clients. For this reason, you want to specify the
jetty.http.hostproperty on the command line (or instart.inito make this setting persistent) to bind the Jetty connector only on the loopback interface (127.0.0.1), making it available to HAProxy but not to remote clients. If your Jetty instance runs on a different machine and/or on a different (sub)network, you may want to adjust both the back-end section of the HAProxy configuration file and thejetty.http.hostproperty to match accordingly.
Browsers supporting HTTP/2 will connect to HAProxy, which will decrypt the traffic and send it to Jetty. Likewise, HTTP/1.1 clients will connect to HAProxy, which will decrypt the traffic and send it to Jetty.
The Jetty connector, configured with the http2c module (and therefore transitively with the http module) is able to distinguish whether the incoming bytes are HTTP/2 or HTTP/1.1 and will handle the request accordingly.
The response is relayed back to HAProxy, which will encrypt it and send it back to the remote client.
This configuration offers you efficient TLS offloading, HTTP/2 support and transparent fallback to HTTP/1.1 for clients that don’t support HTTP/1.1.