NrgXnat / xnat-docker-compose

Build an XNAT server configuration with Docker Compose
Other
82 stars 85 forks source link

Login redirect from / to /app/template/Login.vm changes from https to http #86

Open ianhinder opened 2 years ago

ianhinder commented 2 years ago

Describe the bug Login redirect from / to /app/template/Login.vm changes from https to http.

To Reproduce Steps to reproduce the behavior:

git clone -b features/dependency-mgmt https://github.com/NrgXnat/xnat-docker-compose xnat-docker-compose-test

cd xnat-docker-compose-test

git apply <<'EOF'
diff --git a/docker-compose.yml b/docker-compose.yml
index b381fc3..9cbb863 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,6 +13,7 @@ services:
     ports:
       - "80:80"
       - "8080:8080"
+      - "443:443"
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock:ro
       - ./traefik/config/traefik.yml:/etc/traefik/traefik.yml
@@ -61,7 +62,11 @@ services:
       - "8144:8144"
     labels:
       - "traefik.http.routers.xnat-web.rule=PathPrefix(`/`)"
+      - "traefik.http.routers.xnat-web.entrypoints=web"
       - "traefik.http.services.xnat-web.loadbalancer.server.port=8080"
+      - "traefik.http.routers.xnat-web-secure.rule=Host(`example.com`) && PathPrefix(`/`)"
+      - "traefik.http.routers.xnat-web-secure.entrypoints=websecure"
+      - "traefik.http.routers.xnat-web-secure.tls=true"
     volumes:
       - ./xnat-data/archive:/data/xnat/archive
       - ./xnat-data/build:/data/xnat/build
diff --git a/traefik/config/traefik.yml b/traefik/config/traefik.yml
index 3446a96..9f0f963 100644
--- a/traefik/config/traefik.yml
+++ b/traefik/config/traefik.yml
@@ -17,3 +17,9 @@ log:
 accessLog:
   filePath: "/var/log/access.log"

+entryPoints:
+  web:
+    address: ":80"
+
+  websecure:
+    address: ":443"
EOF

sudo ./gradlew composeBuild composeUp

Check http on port 80 works:

curl -I -v --resolve example.com:80:127.0.0.1 http://example.com
* Added example.com:80:127.0.0.1 to DNS cache
* Hostname example.com was found in DNS cache
*   Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to example.com (127.0.0.1) port 80 (#0)
> HEAD / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
HTTP/1.1 302 Found
< Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors 'self'
< Date: Thu, 10 Mar 2022 10:03:07 GMT
Date: Thu, 10 Mar 2022 10:03:07 GMT
< Location: http://example.com/app/template/Login.vm
Location: http://example.com/app/template/Login.vm
< Set-Cookie: JSESSIONID=BF3513D322BDFA96EDBA88BD7C4418E5; Path=/; HttpOnly
Set-Cookie: JSESSIONID=BF3513D322BDFA96EDBA88BD7C4418E5; Path=/; HttpOnly
< Set-Cookie: SESSION_EXPIRATION_TIME="1646906587007,900000"; Version=1; Path=/
Set-Cookie: SESSION_EXPIRATION_TIME="1646906587007,900000"; Version=1; Path=/
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
X-Frame-Options: SAMEORIGIN
< X-Xss-Protection: 1; mode=block
X-Xss-Protection: 1; mode=block

< 
* Connection #0 to host example.com left intact

Note the Location line, which gives a correct redirection.

Now check https on port 443:

curl I -v --insecure --resolve example.com:443:127.0.0.1 https://example.com

* Added example.com:443:127.0.0.1 to DNS cache
* Hostname example.com was found in DNS cache
*   Trying 127.0.0.1:443...
* TCP_NODELAY set
* Connected to example.com (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=TRAEFIK DEFAULT CERT
*  start date: Mar 10 09:56:29 2022 GMT
*  expire date: Mar 10 09:56:29 2023 GMT
*  issuer: CN=TRAEFIK DEFAULT CERT
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55c96d7a0880)
> GET / HTTP/2
> Host: example.com
> user-agent: curl/7.68.0
> accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 302 
< content-security-policy: frame-ancestors 'self'
< date: Thu, 10 Mar 2022 10:04:56 GMT
< location: http://example.com/app/template/Login.vm
< set-cookie: JSESSIONID=82EBA7F926B6648064A5BCB4B92215EF; Path=/; HttpOnly
< set-cookie: SESSION_EXPIRATION_TIME="1646906696481,900000"; Version=1; Path=/
< x-content-type-options: nosniff
< x-frame-options: SAMEORIGIN
< x-xss-protection: 1; mode=block
< content-length: 0
< 
* Connection #0 to host example.com left intact

Note the location: line, which has redirected to http from https. When used in a browser, you can replace the http after redirection with an https, and the rest of XNAT works fine over https, until you get another redirection, at which point it uses http and you need to change it manually to https. Note that it is redirecting to the correct host and path; it's just the scheme (https vs http) that is wrong.

This is all independent of the site URL configured in XNAT; even if you set that to the correct https URL, it breaks in the same way.

Expected behavior I expect https requests to be redirected to https, not to http.

Screenshots n/a

Docker server environment (please complete the following information):

Configuration:

Additional context

This is a cut down example which makes use of the internal self-signed traefik certificate, and I use --insecure in curl to handle this. I have set up traefik's LetsEncrypt support in this docker-compose file successfully, and everything works apart from this redirect. Once the redirect works, I can add my config to #1 for other people to use.

I don't know if this is caused by XNAT itself, or if the traefik reverse proxying is not working, or if traefik needs to be configured differently to work with XNAT. But it would be great to get this working, as it provides a very simple way to set up https access to XNAT with docker-compose.

prantikk commented 1 year ago

Hi Ian, Did you find a resolution to this redirect issue?

jmzumg commented 1 year ago

Hey, I was experiencing the same problem (using nginx rather than traefik, but I think it still applies)

I solved it by making the following changes:

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- blah blah blah -->
<Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/>
</Host>

This solved the issue for me.

References:

ianhinder commented 1 year ago

Apologies; I was able to fix it a while back. Here was my solution:

commit 20b167cb0ada9d22574e4d5bceade485c2eb3481
Author: Ian Hinder <XXX>
Date:   Tue Mar 15 22:57:02 2022 +0000

    Add proxy header configuration to tomcat server.xml during image build

    This allows xnat to work correctly behind traefik with TLS

diff --git a/xnat/Dockerfile b/xnat/Dockerfile
index 8021783..b7b2704 100644
--- a/xnat/Dockerfile
+++ b/xnat/Dockerfile
@@ -31,7 +31,7 @@ ADD wait-for-postgres.sh /usr/local/bin/wait-for-postgres.sh
 ADD ${XNAT_PATH} /webapps

 RUN apt-get update && \
-    apt-get --yes install postgresql-client wget && \
+    apt-get --yes install postgresql-client wget patch && \
     apt-get --yes --auto-remove upgrade && \
     rm -rf ${CATALINA_HOME}/webapps/* && \
     mkdir -p \
@@ -50,6 +50,10 @@ RUN apt-get update && \
     rm /usr/local/bin/make-xnat-config.sh && \
     apt-get clean

+ADD RemoteIpValve.patch /tmp/RemoteIpValve.patch
+RUN patch /usr/local/tomcat/conf/server.xml /tmp/RemoteIpValve.patch
+RUN rm -f /tmp/RemoteIpValve.patch
+
 EXPOSE 8000
 EXPOSE 8080

diff --git a/xnat/RemoteIpValve.patch b/xnat/RemoteIpValve.patch
new file mode 100644
index 0000000..ffdd278
--- /dev/null
+++ b/xnat/RemoteIpValve.patch
@@ -0,0 +1,16 @@
+--- server.xml 2022-02-21 21:01:10.000000000 +0000
++++ server.xml 2022-03-15 22:41:45.228952764 +0000
+@@ -137,6 +137,13 @@
+       <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
+       -->
+ 
++      <!-- Ensure that original protocol and port information is
++           available when behind a reverse proxy -->
++      <Valve className="org.apache.catalina.valves.RemoteIpValve"
++             protocolHeader="x-forwarded-proto"
++             portHeader="x-forwarded-port"
++           />
++
+       <!-- Use the LockOutRealm to prevent attempts to guess user passwords
+            via a brute-force attack -->
+       <Realm className="org.apache.catalina.realm.LockOutRealm">

So similar to @jmzumg's solution, though not quite the same.