Securing Applications Hosted on Tomcat or JBoss By Enabling SSL at Proxy/Reverse Proxy

A major step towards enhancing application security is to safeguard web interfaces of an application by enforcing SSL with proper TLS version and restrict access over non-secure communication over http.

In this blog, I will explain proxy and application servers coupling, as well as enablement of ssl at the proxy.

High Level Architecture

Consider a scenario where you have a set of applications hosted in tomcat or jboss servers, few of the application has single instance and others has multiple instances with session handing to ensure HA.

Please follow the below steps to achieve goal of enhancing application security is to safeguard web interfaces of an application by enforcing SSL with proper TLS version and restrict access over non-secure communication over http.

Application Server

We will use ajp protocol for communication between proxy and the application servers. The ajp13 protocol is packet-oriented, high performing protocol.

Please follow the below link to know more about AJP protocol-

https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html

Add below in tomcat server.xml to enable AJP(default port 8009)

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Add below in Jboss or to enable AJP(default port 8009),

# under web subsystem
<connector name="ajp" protocol="AJP/1.3" socket-binding="ajp"/>
#socket-binding-group
<socket-binding name="ajp" port="8009"/>

Also add jvmRoute in application servers.

Proxy

Set up a HA proxy using keepalived with VIP at least on two servers irrespective of type of proxy.

Copy certificates received from CA into both the proxy servers.

I am going to discuss configuration of most widely used proxy Apache with mod proxy, HAPROXY and nginx. You could consider having any one of them.

Apache with mod proxy

Following modules to be enabled.

Please have a module configuration as below

LoadModule proxy_module modules/mod_proxy.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so

Frontend or interface exposed for external traffic are protected with SSL.

Please place a ssl.conf inside conf.d folder.

#Enable port listener
listen 443 https

#Global SSL config
SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300
SSLRandomSeed startup file:/dev/urandom  256
SSLRandomSeed connect builtin
SSLCryptoDevice builtin

#SSL Virtual Host Context
<VirtualHost *:443>
   #DocumentRoot "/var/www/html"
   ProxyPreserveHost On
   ServerName <<FQDN or domain name>>
   LogLevel info
   TransferLog /var/log/httpd/ssl_access_log   
   ErrorLog /var/log/httpd/ajp.error.log
   CustomLog /var/log/httpd/ajp.log combined
   
   SSLEngine on
   SSLProtocol all -SSLv2 -SSLv3
   SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA
   
   #certificates
   SSLCertificateFile /etc/pki/tls/certs/domain.crt
   SSLCertificateKeyFile /etc/pki/tls/private/domain.key
   SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
   #SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
   
   #SSLVerifyClient require
   #SSLVerifyDepth  10
   <Proxy *>
     AddDefaultCharset Off
     Order deny,allow
     Allow from all
   </Proxy>
   </VirtualHost>

Proxy & reverse proxy configuration for backend without load balancer
Below entry added under ssl virtual host tag of ssl.conf

ProxyPass /ApplicationContext ajp://[Application Server IP]:[port]/ApplicationContext
ProxyPassReverse /ApplicationContext ajp://[Application Server IP]:[port]/ApplicationContext

Below entry added under ssl virtual host tag of ssl.conf

<Proxy balancer://lb_name>
    BalancerMember ajp://[ApplicationServer#1 ip]:[port]/[ApplicationContext] route=[ApplicationServerName or ID]
    BalancerMember ajp://[ApplicationServer#2 ip]:[port]/[ApplicationContext] route=[ApplicationServerName or ID]      
    ProxySet lbmethod=bytraffic
    ProxySet stickysession=JSESSIONID
</Proxy>
ProxyPass /ApplicationContext balancer://lb_name stickysession=JSESSIONID
ProxyPassReverse /ApplicationContext balancer://lb_name stickysession=JSESSIONID

Apache httpd proxy uses mod_proxy and ajp module to redirect to backends.

Below conf to be added to httpd.conf

#redirect all http request to https
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Restart the apache2/httpd service

HAPROXY

Following details to be added to haproxy.conf

HAPROXY Global Configuration

global
    log /opt/log local0
    daemon
    maxconn 50000
    ssl-default-bind-options no-sslv3
    tune.ssl.default-dh-param 2048
    stats socket /var/lib/haproxy/haproxy.sock mode 0600 level admin
defaults
    log global
    mode tcp
    option tcplog
    option dontlognull
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
#    errorfile 503 /etc/haproxy/errors/503.http
#    errorfile 504 /etc/haproxy/errors/504.http

Define a SSL enable frontend with 80 to 443 redirection

#------------SSL enabled frontend------------------------------

frontend https-app-fnd
    bind *:443 ssl crt /etc/pki/tls/certs/domain.pem force-tlsv12  
    bind *:80
    mode http
    reqadd X-Forwarded-Proto:\ https
  #http to https force redirect
    http-request redirect scheme https unless { ssl_fc }    
    use_backend proxy-app-bnd 

Define a backend for the above frontend

#------------non SSL enabled backend------------------------------
backend proxy-app-bnd 
    mode http
    balance roundrobin
    option httpchk OPTIONS /
    option forwardfor
    option http-server-close
   cookie SERVERID insert indirect nocache
#    cookie JSESSIONID prefix nocache
   server appserver1 [appserver1 ip]:[port] check cookie appserver1
   server appserver2 [appserver2 ip]:[port] check cookie appserver2
   server appserver3 [appserver3 ip]:[port] check cookie appserver3

Restart the haproxy service and test.

NGINX

Following details to be added in nginx.conf if you are using nginx.

Global ssl config

# --------- global ssl config -------------------
    ssl_certificate /etc/pki/tls/certs/domain.crt;
    ssl_certificate_key /etc/pki/tls/certs/domain.key;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_verify_depth 3;

Add the below section only if you have backend applications deployed on multiple servers(HA config)

# below http section is for applications with multiple backend only	
http {
        upstream app_with_multiple_backend {
        server [appserver1 ip]:[port];
        server [appserver2 ip]:[port];
        server [appserver3 ip]:[port];
		sticky cookie srv_id expires=1h domain=.[yourdomain] path=/;
}

Then add the below section under server tag only if you have backend applications deployed on multiple servers(HA config).

 # applications with multiple backend, definition of app_with_multiple_backend is at outside server tag
   location / {
            proxy_pass http://app_with_multiple_backend;
    }    

Add the below if you have single application instance deployed.

 # applications with single backend
    location /applicationContext1  {
         proxy_pass http://[application server ip]:[port]/[applicationContext1]
    }

Restart the nginx service and test.

Please clone below git repo for more.

Git repo: https://github.com/tech-inducers/securing_applications_ssl_proxy

Author