Pentaho j_spring_security_check mixed content. HTTPS to HTTP - spring

I have installed Pentaho (9.x) on Tomcat 8.5 and OpenJDK 1.8 as required.
In front of it there is Apache 2.4 with mod_proxy_http.
My website is served with HTTPS and I have these Proxy rules:
RequestHeader set X-Forwarded-Proto "https"
ProxyPreserveHost on
ProxyPass "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPassReverse "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPass "/pentaho/" "http://tomcat_host_ip:8080/pentaho/"
ProxyPassReverse "/pentaho/" "http://tomcat_host_ip:8080/pentaho/"
ProxyPass "/pentaho/Login" "http://tomcat_host_ip:8080/pentaho/Login"
ProxyPassReverse "/pentaho/Login" "http://tomcat_host_ip:8080/pentaho/Login"
When I try to log in a get an error during the POST:
https://pentaho.mywebsite.org/pentaho/j_spring_security_check
The application try to responde with HTTP protocol instead HTTPS.
In the request header I have the correct Referer and Origin:
Origin: https://pentaho.mywebsite.org
Referer: https://pentaho.mywebsite.org/pentaho/Login
But the response header reply with HTTP and NOT https:
Location http://pentaho.mywebsite.org/pentaho/

I solved the problem just adding proxyPort="443" and scheme="https" to my http connector in Tomcat.
The rule
RequestHeader set X-Forwarded-Proto "https"
on Apache was unusefull. This is my correct Apache configuration
ProxyPreserveHost on
ProxyPass "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPassReverse "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPass "/pentaho/" "http://tomcat_host_ip:8080/pentaho/"
ProxyPassReverse "/pentaho/" "http://tomcat_host_ip:8080/pentaho/"
And this is my Tomcat HTTP connector
<Connector URIEncoding="UTF-8"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="443"
scheme="https"
redirectPort="8443"
relaxedPathChars="[]|"
relaxedQueryChars="^{}[]|&"
maxHttpHeaderSize="65536"
/>

Servlet applications use the scheme, serverName and serverPort properties of a ServletRequest to generate hyperlinks. Usually Tomcat gets the latter two from the Host request header, while scheme depends on the connector.
If you use a reverse proxy, the above logic may not be enough. You have two solution:
Setting scheme statically
In your case the proxy uses HTTPS, while Tomcat uses HTTP, so you must override the scheme and secure properties:
<Connector
port="8080"
scheme="https"
secure="true"
...
while the Apache HTTP Server configuration can be shortened to:
ProxyPreserveHost on
ProxyPass "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPassReverse "/pentaho" "http://tomcat_host_ip:8080/pentaho"
Remark that in your answer you didn't set the secure attribute: this attribute decides whether the transport is confidential. If you don't set it to true, Tomcat will automatically redirect the browser to redirectPort whenever the application asks for a confidential transport (cf. Securing Web Applications).
This solution only works correctly, if your proxy forwards only HTTPS requests to Tomcat.
Setting scheme dynamically
If you forward both HTTP and HTTPS requests to Tomcat, the server needs a way to distinguish between them. Therefore you need to add a RemoteIpValve to your Tomcat configuration:
<Valve className="org.apache.catalina.valves.RemoteIpValve" />
<Connector
port="8080"
redirectPort="443"
...
and ask Apache HTTP Server to add an X-Forwarded-Proto header:
RequestHeader set X-Forwarded-Proto "expr=%{REQUEST_SCHEME}"
ProxyPreserveHost on
ProxyPass "/pentaho" "http://tomcat_host_ip:8080/pentaho"
ProxyPassReverse "/pentaho" "http://tomcat_host_ip:8080/pentaho"
This solution has also the advantage to set the client's remoteHost and remoteAddr instead of those of the proxy.

Related

How to write configuration Apache load balancer for many Spring-Boot?

We want to use Apache load balance to spread the load over several servers. We plan to do many http://load_balancer:port/app_name redirects on Apache LB e.g:
http://load_balancer:port/app1 ---> Apache LB ---> http://server1:port1, http://server2:port1
http://load_balancer:port/app2 ---> Apache LB ---> http://server1:port2, http://server2:port2
....
For Apache LB configuration file as below, when we use http://load_balancer:port in web browser then Apache LB works as expected.
How to rewrite the configuration so that you can enter http://load_balancer:port/app in web browser?
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy "balancer://mycluster">
BalancerMember "http://server1:port" route=1 keepalive=On smax=1 connectiontimeout=10 retry=600 timeout=900 ttl=900
BalancerMember "http://server2:port" route=2 keepalive=On smax=1 connectiontimeout=10 retry=600 timeout=900 ttl=900
ProxySet stickysession=ROUTEID
</Proxy>
<Proxy "balancer://myws">
BalancerMember "ws://http://server1:port" route=1 keepalive=On smax=1 connectiontimeout=10 retry=600 timeout=900 ttl=900
BalancerMember "ws://http://server2:port" route=2 keepalive=On smax=1 connectiontimeout=10 retry=600 timeout=900 ttl=900
ProxySet stickysession=ROUTEID
</Proxy>
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) balancer://myws/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) balancer://mycluster/$1 [P,L]
Second configuration
We used the hint as below for the proxy flag. Apache LoadBalancer worked fine for "http://load_balancer:port/". My configuration file:
ProxyPass "/" "balancer://mycluster/" stickysession=JSESSIONID|jsessionid scolonpathdelim=On
<Proxy "balancer://mycluster">
BalancerMember "http://server1:8727" route=1
BalancerMember "http://server2:8193" route=2
</Proxy>
When we changed the configuration to ProxyPass "/test" to refer to "http://load_balancer:port/test" from the web browser, there were errors (404) in the access.log file:
GET /test/ HTTP/1.1 200 2375
GET /static/css/app.77ac3251.css HTTP/1.1 404 196
...
You can use apache proxy pass instead of a rewriterule with proxy flag. You can find all the detail about the configuration on the official documentation: https://httpd.apache.org/docs/2.4/mod/mod_proxy_balancer.html
We wrote the configuration as below and it works as expected:
<Proxy balancer://test.cl>
Header add Set-Cookie "test.ROUTEID=ROUTE.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
BalancerMember http://server1:8727 route=test_00 keepalive=On connectiontimeout=5 retry=180
BalancerMember http://server2:9393 route=test_10 keepalive=On connectiontimeout=5 retry=180
ProxySet lbmethod=bybusyness stickysession=ROUTEID
</Proxy>
<Location /test>
ProxyPreserveHost On
ProxyPass balancer://test.cl
ProxyPassReverse balancer://test.cl
</Location>

How to enable login functionality for elastic search & Kibana of Bitnami ELK Stack solution?

Below I provided both kibana vhost file. what changes I have to make so that kibana dashboard is username & password protected.
kibana-https-vhost.conf
<VirtualHost 127.0.0.1:443 _default_:443>
ServerAlias *
SSLCertificateFile "/opt/bitnami/apache/conf/bitnami/certs/server.crt"
SSLCertificateKeyFile "/opt/bitnami/apache/conf/bitnami/certs/server.key"
ProxyPass /elasticsearch http://127.0.0.1:9200
ProxyPassReverse /elasticsearch http://127.0.0.1:9200
ProxyPass / http://127.0.0.1:5601/
ProxyPassReverse / http://127.0.0.1:5601/
# HTTP Basic authentication layer
<Location />
AuthType Basic
AuthName "Introduce your ELK credentials. If you have problems,.."
AuthBasicProvider file
AuthUserFile /opt/bitnami/kibana/conf/password
Require user user
</Location>
</VirtualHost>
kibana-vhost.conf
<VirtualHost 127.0.0.1:80 _default_:80>
ServerAlias *
ProxyPass /elasticsearch http://127.0.0.1:9200
ProxyPassReverse /elasticsearch http://127.0.0.1:9200
ProxyPass / http://127.0.0.1:5601/
ProxyPassReverse / http://127.0.0.1:5601/
# HTTP Basic authentication layer
<Location />
AuthType Basic
AuthName "Introduce your ELK credentials. If you have problems, .."
AuthBasicProvider file
AuthUserFile /opt/bitnami/kibana/conf/password
Require user user
</Location>
</VirtualHost>

Configure Tomcat8 behind Apache with HTTPS

I have Tomcat 8 installed in my server in port 8080 that I am exposing with a secured Apache (using Proxy Pass).
Here is my Apache configuration:
<VirtualHost *:443>
ServerName myserver.com
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /odata/ http://172.31.36.251:8080/
ProxyPassReverse /odata/ http://172.31.36.251:8080/
<Proxy *>
allow from all
</Proxy>
RequestHeader set X-Forwarded-Port 443
RequestHeader set X-Forwarded-Scheme https
</VirtualHost>
Here is the Tomcat server.xml configuration
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
address="172.31.36.251"
proxyName="myserver.com"
scheme="https" proxyPort="443" />
All works well until here. If I call my application:
https://myserver.com/odata/D3a1593adae89/odata.svc/
I get:
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xml:base="https://myserver.com:443/D3a1593adae89/odata.svc/">
<workspace>
<atom:title>Default</atom:title>
<collection href="Maintables">
<atom:title>Maintables</atom:title>
</collection>
</workspace>
</service>
The problem: If you see the attribute xml:base in the result, Tomcat decorates the address with the port and I really don't know how to remove it. Also the address is wrong: It should be https://myserver.com:443/odata/D3a1593adae89/odata.svc/ . I been looking around and trying things like setting proxyPort to blank but nothing. I think this is related to broken links when using a reverse proxy as described at https://cwiki.apache.org/confluence/display/HTTPD/TomcatModProxyHTML . I tried some rewrites like:
ProxyHTMLURLMap http://172.31.33.105:8080 /odata
RewriteEngine On
RewriteRule ^/odata$ https://myserver.com/odata/ [R,L]
But I just cannot make it work. The xml:base should be https://myserver.com/odata/D3a1593adae89/odata.svc/
Any idea is appreciated
For the port thing, as you use ProxyPreserveHost On and you set X-Forwarded-* headers, you can use the RemoteIpValve:
<Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" />
The connector will be simply:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="443"
address="172.31.33.105"
/>
The valve detect the proto and will assume the connection is secured with the default port number (https://xxxxx/yyy).
For the location, you should deploy your application in the odata context so that you can use
ProxyPass /odata http://172.31.33.105:8080/odata
modifying the context on the proxy pass (from /odata/ to /) is somehow tricky as you would need to filter all text coming from your backend server to fix some url path. It can be really painful ( should I replace all "/xxx" with "/odata/xxx" ? )

Web Socket behind proxy

I have Spring Boot app with WebSocket running behind Apache 2. When trying connect I am getting the following error:
Server Log:
Handshake failed due to invalid Upgrade header: null
Client Log:
Here is the Apache 2 config:
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin tom_marik#hotmail.com
ServerName www.languageexchange.eu
ProxyPass "/ws2/" "ws://www.languageexchange.eu:92/"
ProxyPass "/wss2/" "wss://www.languageexchange.eu:92/"
ProxyPass / http://31.31.74.54:92/
ProxyPassReverse / http://31.31.74.54:92/
ProxyPassReverseCookiePath / /
ErrorLog /var/log/apache2/languageexchange.log
LogLevel warn
RewriteEngine on
[END,QSA,R=permanent]
SSLCertificateFile /etc/letsencrypt/live/languageexchange.eu/cert.pem
SSLCertificateKeyFile
/etc/letsencrypt/live/languageexchange.eu/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateChainFile
/etc/letsencrypt/live/languageexchange.eu/chain.pem
</VirtualHost>
</IfModule>
Thanks for any help!
You need to have mod_proxy_wstunnel installed (mod_proxy and mod_ssl as well). Then in your Apache's configuration:
ProxyPass /ws2 ws://languageexchange.eu:92 keepalive=On
ProxyPassReverse /ws2 ws://languageexchange.eu:92
ProxyPass /wss2 wss://languageexchange.eu:92 keepalive=On
ProxyPassReverse /wss2 wss://languageexchange.eu:92
If this is a single machine it may be better to set 127.0.0.1 instead of languageexchange.eu, so:
ProxyPass /ws2 ws://127.0.0.1:92 keepalive=On
ProxyPassReverse /ws2 ws://127.0.0.1:92
ProxyPass /wss2 wss://127.0.0.1:92 keepalive=On
ProxyPassReverse /wss2 wss://127.0.0.1:92

Forcing HTTPS redirect on Wildfly 10.0 directs to https://localhost:8443

I am having a very challenging time forcing HTTPS on a Bitnami Ubutnu Wildfly 10 install.
The HTTPS works fine (e.g. https://example.com works great)
I have tried many different things with no result. Here are some highlights of what I've done:
I modified my web.xml to add this (note MYWEBNAME was replaced with my war file name):
<security-constraint>
<web-resource-collection>
<web-resource-name>MYWEBNAME</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
I modified /opt/bitnami/apache2/conf/bitnami/bitnami.conf (as per https://docs.bitnami.com/aws/components/apache/):
<VirtualHost _default_:80>
DocumentRoot /opt/bitnami/apache2/htdocs"
ADD: RewriteEngine On
ADD: RewriteCond %{HTTPS} !=on
ADD: RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [R,L]
...
</VirtualHost>
I modified standalone.xml
<management-interfaces>
<http-interface security-realm="ApplicationRealm" http-upgrade-enabled="true">
<socket-binding https="management-https"/>
</http-interface>
</management-interfaces>
I modified my root index.html to redirect to:
<SCRIPT>document.location="https://example.com";</SCRIPT>
As per Wildfly 9 http to https, I tried this:
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket interface="management" secure-port="${jboss.management.http.port:9990}"/>
</http-interface>
this resulted in a 503 error and wildfly to die, so I removed it.
What I have now, is http://example.com redirecting to https://localhost:8443
So I think it's close, I just cannot figure out how to make it redirect to https://example.com:8443 instead
I m not using Apache proxing Wildfly. But in my setup, all request on port 80 or 8080 (http://example.com or http://example.com:8080) is redirected to port 443 (https://example.com).
It is done making iptables to redirect traffic from 80 to 8080 and 443 to 8443 and than wildfly redirects CONFIDENTIAL transport requests to port 443 instead 8443.
Please see if it is helpful: make wildfly listen on port 443 not 8443
By the way, use javascript or any other client side script to redirect to SSL is not safe enough once the responsability of the redirection is in the client side.
For others looking for a solution, here's a summary of what I did - all in one spot. This is a summary of the links located in this thread, so h/t to those authors who answered the question. The credit belongs to them, this is just a summary of what worked for me.
1. Add an IPTABLES routing rule to route port 443 to 8443.
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
Hint: to see what rules you already have in place, use:
sudo iptables -t nat -L -n -v
2. Add a Rewrite Filter and a Predicate to the configuration. Add the entries shown on line 10 and 24 of the snippet.
<subsystem xmlns="urn:jboss:domain:undertow:3.0">
<buffer-cache name="default"/>
<server name="default-server">
<http-listener name="default" socket-binding="http" redirect-socket="https"/>
<https-listener name="default-ssl" security-realm="ApplicationRealm" socket-binding="https"/>
<host name="default-host" default-web-module="YOURWARFILENAMEHERE.war" alias="localhost">
<location name="/" handler="welcome-content"/>
<filter-ref name="server-header"/>
<filter-ref name="x-powered-by-header"/>
<filter-ref name="http-to-https" predicate="equals(%p,8080)"/>
<!-- ADD THE filter-ref ENTRY ABOVE -->
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
<filters>
<response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
<response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
<rewrite name="http-to-https" redirect="true" target="https://DOMAINNAMEHERE:8443%U"/>
<!-- ADD THE rewrite ENTRY ABOVE, BE SURE TO SUBSTITUTE YOUR DOMAIN NAME -->
</filters>
</subsystem>
Note: I wondered if adding an iptables reroute from 8080 to 8443 using the command in step 1 would be sufficient and eliminate the need for step 2. But step 2 worked for me so I went with it. I'll leave trying that option up to the reader if they want.
3. Modify The Management Interfaces section of the standalone.xml.
<management-interfaces>
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket-binding https="management-https"/>
</http-interface>
</management-interfaces>
Note that this replaced the binding to http. Also note this step may not be directly related to the forwarding of HTTP to HTTPS but rather just a step in the HTTPS setup.
4. Restart your Wildfly instance.

Resources