Showing posts with label tomcat. Show all posts
Showing posts with label tomcat. Show all posts

Thursday, August 5, 2021

NGINX SSL Reverse Proxy for Tomcat/ORDS/APEX

This is a simple reverse proxy to achieve any URL structure you want for your APEX server. NGINX is responsible for handle almost everything, reverse proxy, URL redirection, HTTP/2, cache, gzip, HSTS, OCSP stapling, etc. Tomcat/ORDS/APEX is sitting behind NGINX, communicating with NGINX via HTTP.

This is the stack I am running at the moment.

  • NGINX 1.21.1
  • Tomcat 9.0.50
  • ORDS 21.2.0.r1741826
  • APEX 21.1.2
C:\Program Files\nginx\conf\nginx.conf
worker_processes auto;
pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout 75;
	client_max_body_size 200M;
	proxy_cache_path "C:/Program Files/nginx/temp/proxy_cache" levels=1:2 keys_zone=STATIC:10m inactive=24h  max_size=1g use_temp_path=off;

server {
    listen 80 default_server;
    server_name _;
	
    rewrite ^ https://$host$request_uri? permanent;
}

server {
    listen 443 ssl http2 default_server;
    server_name _;
 
	gzip on;
	gzip_types	text/css text/plain text/javascript	application/javascript application/json application/x-javascript application/xml application/xml+rss application/xhtml+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject image/svg+xml image/x-icon application/rss+xml application/atom_xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
	
	ssl_certificate "E:/ssl/fullchain.cer";
	ssl_certificate_key "E:/ssl/leavemealone.com.key";
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA HIGH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
	ssl_ecdh_curve secp384r1; 
	ssl_prefer_server_ciphers on;	
	ssl_dhparam "E:/ssl/dhparam4096.pem";
	
	ssl_session_cache   shared:SSL:10m;
	ssl_session_timeout 10m;
	ssl_session_tickets off;
	
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate "E:/ssl/ca.cer";

	add_header          Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
#	add_header          X-Content-Type-Options nosniff;
#   add_header          X-Frame-Options SAMEORIGIN;
#   add_header          X-XSS-Protection "1; mode=block";

    location / {
        proxy_connect_timeout       600;
        proxy_send_timeout          600;
        proxy_read_timeout          600;
        send_timeout                600;

		proxy_set_header Host $host;
		proxy_set_header X-Forwarded-Host $host:$server_port;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header Origin "";
        proxy_pass http://127.0.0.1:8080;
		proxy_http_version 1.1;
    }
	
	# cache apex application/workspace static files
	location ~* /ords/(.*)/r/([0-9/]*)files/static/v([0-9]+)/ {
		proxy_set_header Host $host;
		proxy_set_header X-Forwarded-Host $host:$server_port;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header Origin "";
        proxy_pass http://127.0.0.1:8080;
		proxy_http_version 1.1;
		
		proxy_redirect off;
		add_header X-Cache-Status $upstream_cache_status;
		expires 300d;
		proxy_cache STATIC;
		proxy_cache_key $host$uri$is_args$args;
		proxy_cache_valid 200 24h;
		proxy_cache_use_stale  error timeout invalid_header updating http_500 http_502 http_503 http_504;
	}
  }  	
}
Let me explain this line by line:
  • Line 14, proxy cache setting
  • Line 17-20, rediect all HTTP traffic to HTTPS
  • Line 24, setup HTTP/2
  • Line 27-30, enable gzip
  • Line 32-33, SSL key pair. fullchain.cer contains the server public certificate followed by immediate certificate in the same file
  • Line 34-38, SSL protocol, chipers setting
  • Line 40-42, SSL session cache setting
  • Line 44-46, OCSP stapling setting. ca.cer contains only the immediate certificate
  • Line 48, add HSTS header
  • Line 54-57, proxy timeout setting
  • Line 59-63, pass some extra headers to ORDS, so that your app can now where this request originally comes from
  • Line 64, Google Chrome enforces stricter CORS rules, than e.g. Firefox. By setting the Origin to blank we can make reverse proxying work, otherwise Chrome would block it
  • Line 65, the actual reverse proxy command saying that traffic is internally rerouted to http://127.0.0.1:8080
  • Line 80-86, proxy cache setting. We put every file found on a path like /ords/*/r/*files/static/vnnn/ subfolder for at least 24hrs and also send a 300 day expiry header to the client

There is not a lot of changes in Tomcat. Basically we need to ensure HTTP (port 8080) is working, limit access to localhost and adding the actual IP address %{X-Forwarded-For} to tomcat log file.

E:\tomcat9\conf\server.xml
<Connector port="8080" protocol="HTTP/1.1"
		scheme="https"
        proxyPort="443"
		maxHttpHeaderSize="32767"
		maxPostSize="-1"
		disableUploadTimeout="true" />
        
   <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
            
...

		<Valve className="org.apache.catalina.valves.RemoteAddrValve"
			allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1"/>

		<Valve className="org.apache.catalina.valves.RemoteIpValve"
				internalProxies="127\.0\.[0-1]\.1"
				remoteIpHeader="X-Forwarded-For"
				requestAttributesEnabled="true"
				protocolHeader="x-forwarded-proto"
				protocolHeaderHttpsValue="https"/>
		
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%{X-Forwarded-For}i %h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
   </Engine>
  • Line 2-3, in some situations APEX internally creates a redirect to a different URL path, e.g. during Authentication using Social-Login it will redirect to …/ords/apex_authentication.callback… With the verison of the stack I am using, these two lines might not be required anymore. I am still leaving them here for the peace of mind.
  • Line 14-15, to allow access only for the clients connecting from localhost
  • Line 17-26, adding the actual IP address %{X-Forwarded-For} to tomcat log file

Thursday, November 7, 2019

Tuning Tomcat 9 for APEX/ORDS in Production

Logging

Configuration File: $CATALINA_HOME/conf/logging.properties
Change all occurrences of FINE and INFO to WARNING or SEVERE

JVM Options

Run tomcat9w.exe
Change Initial Memory pool and Maximum memory pool to 4096.


Main Tomcat Server Configurations

Configuration File: $CATALINA_HOME/conf/server.xml
<Connector port="443" protocol="org.apache.coyote.http11.Http11AprProtocol" 
  SSLEnabled="true" scheme="https" secure="true"
  maxHttpHeaderSize="32767"
  compression="on"
  disableUploadTimeout="true">
  <UpgradeProtocol overheadDataThreshold="0" compression="on" className="org.apache.coyote.http2.Http2Protocol" />

...

</Connector>

Close to the end of server.xml
<Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <Context path="/ords" reloadable="false" />

...

</Host>

Oracle REST Data Service

Configuration File: defaults.xml
Change the limits
<entry key="cache.metadata.enabled">true</entry>
<entry key="jdbc.InitialLimit">20</entry>
<entry key="jdbc.MinLimit">20</entry>
<entry key="jdbc.MaxLimit">50</entry>
<entry key="jdbc.MaxStatementsLimit">20</entry>

Sunday, December 10, 2017

Enable HTTP/2 for Tomcat 9

It is easier than I thought.

First, ensure you are using APR/native connector. You can download tomcat native from here. Put the tcnative-1.dll in the tomcat lib directory.

Second, change protocol to org.apache.coyote.http11.Http11AprProtocol and add the UpgradeProtocol tag.

$CATALINA_HOME\conf\server.xml
<Connector port="8443"
  protocol="org.apache.coyote.http11.Http11AprProtocol"
  maxThreads="150" SSLEnabled="true">
    <UpgradeProtocol overheadDataThreshold="0" compression="on" className="org.apache.coyote.http2.Http2Protocol" />
    <SSLHostConfig honorCipherOrder="false">
        <Certificate certificateKeyFile="conf/ca.key"
          certificateFile="conf/ca.crt"
          type="RSA" />
    </SSLHostConfig>
</Connector>
Restart tomcat and we are done.

Check if HTTP/2 is enabled
https://tools.keycdn.com/http2-test

Thursday, July 13, 2017

How to get A+ on SSL Labs running Tomcat

In my previous posting, we brought our tomcat server to Grade A on SSL Labs.
However, to archive A+, it requires more.

You need to enable HSTS. You can following this post to enable HSTS.

One problem I encountered with SSL Labs is the SNI. If we run the test under the default domain, HSTS test works. But if we run it under other domains, HSTS test will fail. So you end up with Grade A instead of A+.

So make sure you test it using your default domain.

Sunday, April 2, 2017

Redirect HTTP to HTTPS on Tomcat

Add this to the end of
$CATALINA_HOME\conf\web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>Entire Application</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>

Enable HTTP Strict Transport Security (HSTS) in Tomcat

Add these two sections to web.xml in the conf directory. This is one of the conditions to get to A+ in Qualyst SSL Server Test.

It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol.

One of the nice features is that, if the client issue http://abcdef.com, it will be redirected to https://abcdef.com.

$CATALINA_HOME\conf\web.xml
      <filter>
        <filter-name>httpHeaderSecurity</filter-name>
        <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>antiClickJackingOption</param-name>
            <param-value>SAMEORIGIN</param-value>
        </init-param>
        <init-param>
            <param-name>hstsMaxAgeSeconds</param-name>
            <param-value>31536000</param-value>
        </init-param>
        <init-param>
            <param-name>hstsIncludeSubDomains</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
  <!-- The mapping for the HTTP header security Filter -->
    <filter-mapping>
        <filter-name>httpHeaderSecurity</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

Monday, March 20, 2017

JasperReportsIntegration on windows Tomcat 8 & 9 High CPU usage

After deployment of the war file, edit the following file and change Context attribute reloadable from true to false.
This fix the CPU spike every 12 seconds issue on windows box.

C:\tomcat9\webapps\JasperReportsIntegration\META-INF\context.xml
<Context path="/JasperReportsIntegration" debug="5" reloadable="false"
         crossContext="true">

Friday, March 17, 2017

Tomcat 9 JDK8 server.xml for HTTPS with SNI Support

This configure file is case sensitive.
SSLCertificateKeyFile is the private key.
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol" 
  SSLEnabled="true" scheme="https" secure="true"
  maxHttpHeaderSize="32767"
  maxThreads="150" 
  URIEncoding="UTF-8"
  compression="on"
  defaultSSLHostConfigName="cmeportal.oneconceptonline.ca">
<SSLHostConfig hostName="cmeportal.oneconceptonline.ca"
  honorCipherOrder="true"
  disableSessionTickets="true"
  ciphers="TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_RSA_WITH_RC4_128_SHA,
TLS_EMPTY_RENEGOTIATION_INFO_SCSVF"
>
<Certificate 
     CertificateFile="${catalina.home}\conf\ssl\cmeportal.oneconceptonline.ca.crt"
     CertificateKeyFile="${catalina.home}\conf\ssl\cmeportal.oneconceptonline.ca.pem"
     CertificateChainFile="${catalina.home}\conf\ssl\gd_bundle-g2-g1.crt"
     type="RSA" />
</SSLHostConfig>
<SSLHostConfig hostName="*.conceptdynamics.zone"
  honorCipherOrder="true"
  disableSessionTickets="true"
  ciphers="TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
TLS_ECDH_RSA_WITH_RC4_128_SHA,
TLS_EMPTY_RENEGOTIATION_INFO_SCSVF"
>
<Certificate 
     CertificateFile="${catalina.home}\conf\ssl\conceptdynamics.zone.crt"
     CertificateKeyFile="${catalina.home}\conf\ssl\conceptdynamics.zone.pem"
     CertificateChainFile="${catalina.home}\conf\ssl\gd_bundle-g2-g1.crt"
     type="RSA" />
</SSLHostConfig>
</Connector>

Friday, January 27, 2017

Tomcat 7 JDK7 server.xml for HTTPS

This configure setup is case sensitive.
SSLCertificateKeyFile is the private key.
<Connector protocol="org.apache.coyote.http11.Http11AprProtocol"
     port="8443" maxThreads="200"
     maxHttpHeaderSize="32767" 
     URIEncoding="UTF-8"
     useServerCipherSuitesOrder="true"
     compression="on"
     enableLookups="false" disableUploadTimeout="true"
     scheme="https" secure="true" SSLEnabled="true"
     SSLCertificateFile="${catalina.home}\conf\ssl\server.crt"
     SSLCertificateKeyFile="${catalina.home}\conf\ssl\server.pem"
     SSLCertificateChainFile="${catalina.home}\conf\ssl\rapidssl.crt"
     clientAuth="false" sslProtocol="TLS"
ciphers="TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, 
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, 
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, 
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, 
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, 
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, 
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, 
TLS_DHE_DSS_WITH_AES_256_CBC_SHA, 
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, 
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, 
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, 
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, 
TLS_DHE_DSS_WITH_AES_128_CBC_SHA, 
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 
TLS_ECDH_ECDSA_WITH_RC4_128_SHA, 
TLS_ECDH_RSA_WITH_RC4_128_SHA, 
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_GCM_SHA384, 
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_EMPTY_RENEGOTIATION_INFO_SCSVF"
/>

APEX application passed Ministry of Health security check

One of the APEX applications I am working on just passed the government security audit with flying color. It is a portal where the cardholders can check out their medical claims history, payment etc. The database has the cardholder personal information, address, date of birth and claims history.

Needless to say, it must passed government security check before they let you open it up to the public. The last thing they want is on the 630 headline news like yahoo 😅

Here is want I have done.

In the worst case scenario, whoever has the login data still need year over year to brute-force the password.

Our application has 200+ pages, the first time I ran APEX-SERT, OMG, I was SHOCKED. There are over 2000 actionable items to fix. It took me over a week to clean up all the mess.

All records more than one rows are displayed via Interactive report or classic report. They can add filters via the build-in UI, I don't take parameters directly. By design, I am pretty much immune from this problem.

First test our score was C, not good. We fixed the followings and end up with an A 😃
  1. Reissue the SHA1 certificate with SHA256
  2. Update java to JDK7 with UnlimitedJCEPolicyJDK7
  3. Update tomcat to v7 and customize the server.xml
There are probably a few more minor items but I can't recall now.


Just want to say big thank you to
Oracle APEX team
APEX-SERT team
Defuse security who wrote the salted password article
QUALYS SSL Labs for their free SSL test site