What do you think? Discuss, post comments, or ask questions at the end of this article [More about me]

Skip to end of metadata
Go to start of metadata

Simplified guide for setting up a reverse proxy that allows installing multiple apps (e.g. tomcat apps) on a single server. 

The guide below is for a debian/ubuntu machine.  For CentOS or Amazon linux, adapt this guide here.

An SSL reverse proxy allows secured connections between client and an apache server (terminated at reverse proxy), then the apache server distributes connections to various ports (or applications) on the server, like this:

Image from Atlassian

This method is advantageous and can avoid the whole (painful) keystore SSL approach.

Preparing Apache2

Adapted from this excellent guide here and atlassians apache ssl guide here.

Enable apache2 modules:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_ajp
sudo a2enmod proxy_wstunnel
sudo a2enmod rewrite
sudo a2enmod deflate
sudo a2enmod headers
sudo a2enmod proxy_balancer
sudo a2enmod proxy_connect
sudo a2enmod proxy_html
sudo a2enmod ssl
sudo a2enmod remoteip

Disable sites at /etc/apache2/sites-available by using a2dissite, e.g:

sudo a2dissite 000-default

We first create a .conf file which will contain the VirtualHost blocks for the reverse proxy

sudo nano /etc/apache2/sites-available/proxy-ssl-host.conf

Below is an example of an initial proxy-ssl-host.conf for a reverse-proxy setup for Atlassian's confluence, crowd, and JIRA.  It contains all directives apart from the SSL certificates (see the following section for information on how to get certbot-auto to add these automatically for you).

Click the '> Expand source' link on the right to view file contents.

/etc/apache2/sites-available/proxy-ssl-host.conf
<VirtualHost *:443>
    ServerName confluence.example.com
    
    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On
    
    <Proxy *>
         Require all granted
    </Proxy>
	
	RemoteIPHeader X-Forwarded-For
    RemoteIPTrustedProxy 127.0.0.1

    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/synchrony
    RewriteRule ^/(.*) http://127.0.0.1:8090/$1 [P]
    
    ProxyPass /synchrony http://127.0.0.1:8091/synchrony
    <Location /synchrony>
        Require all granted
        RewriteEngine on
        RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
        RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
        RewriteRule .* ws://127.0.0.1:8091%{REQUEST_URI} [P]
    </Location>

    ProxyPass / http://127.0.0.1:8090/
    ProxyPassReverse / http://127.0.0.1:8090/  

    # Your SSL certificate information for this sub-domain can go here.
	# If you use certbot-auto, it will automatically add it here if you use the --apache flag.
	# See the following 'LetsEncrypt for SSL certificates' section for details. 
</VirtualHost>

<VirtualHost *:80>
    ServerName confluence.example.com
    Redirect Permanent / https://confluence.example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName crowd.example.com
     
    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On
    
    <Proxy *>
         Require all granted
    </Proxy>
     
    RemoteIPHeader X-Forwarded-For
	RemoteIPTrustedProxy 127.0.0.1

    ProxyPass / http://127.0.0.1:8095/
    ProxyPassReverse / http://127.0.0.1:8095/  
    
    # Your SSL certificate information for this sub-domain can go here.
	# If you use certbot-auto, it will automatically add it here if you use the --apache flag.
	# See the following 'LetsEncrypt for SSL certificates' section for details. 
</VirtualHost>

<VirtualHost *:80>
    ServerName crowd.example.com
    Redirect Permanent / https://crowd.example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName jira.example.com
    
    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On
    
    <Proxy *>
         Require all granted
    </Proxy>
	
	RemoteIPHeader X-Forwarded-For
    RemoteIPTrustedProxy 127.0.0.1
 
    ProxyPass / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/
    
    # Your SSL certificate information for this sub-domain can go here.
	# If you use certbot-auto, it will automatically add it here if you use the --apache flag.
	# See the following 'LetsEncrypt for SSL certificates' section for details. 
</VirtualHost>

<VirtualHost *:80>
    ServerName jira.example.com
    Redirect Permanent / https://jira.example.com/
</VirtualHost>

Note1: each subdomain is resolved and forwarded by apache to various localhost ports.  Also includes http redirects to https.  Replace <...> with actual path to SSL certificates and sub-domains with actuals sub/domains.

Note2: see here for implementing X-Forwarded-For for proper client ip address forwarding (I think might need for Crowd SSO?).

Next we want to enable the file we just created.  On Ubuntu you can enable .conf site files by:

sudo a2ensite proxy-ssl-host.conf

Letsencrypt for SSL certificates

Process is basically the same as outlined here.  Certbot-auto has a fantastic apache plugin that makes obtaining an SSL certificate (and renewing) drop dead simple.  Simply running:

sudo certbot-auto --apache

allows you to select which virtual host to obtain the certificates for and will automatically update your apache .conf file (which was proxy-ssl-host.conf in my case).

Renewing can then be done simply by calling the code below.  Note that since we used the --apache arugment in obtaining the certificate, certbot-auto will gracefully renew and reload apache config (no need to stop, restart apache2).

sudo certbot-auto renew

Passing client ip address properly for logging

See Logging remote ip address when using reverse proxy for a guide on properly logging client ip addresses (instead of the reverse proxy ip).

Atlassian apps

For Atlassian apps, you'll need to enable secure proxy forwarding in their server.xml files.  See here for more detail.

For each application, find the normal (non-SSL) Connector directive in the Tomcat server.xml file, and update the scheme and proxyPort attributes inside the Connector directive, as below. You would have already added these attributes when configuring the reverse proxy. You need to change scheme to "https" and proxyPort to the port that Apache is listening for SSL on, e.g. "443", like this:
<Connector port=<default>
	maxThreads=<default>
    minSpareThreads=<default>
    connectionTimeout=<default>
    enableLookups=<default>
    maxHttpHeaderSize=<default>
    protocol=<default>
    useBodyEncodingForURI=<default>
    redirectPort=<default>
    acceptCount=<default>
    disableUploadTimeout=<default>
	compression="off"
	proxyName="<subdomain>.<domain>.com"
	proxyPort="443"
    secure="true"
	scheme="https"/>

Note1: is basically just adding the last five entries.

Note2: Atlassian recommends turning compression off when using a reverse proxy.

Restart Atlassian apps and you should be good to go.

References: