Tomcat Docker image + Nginx SSL proxy: file upload/delete errors

Hi guys.

We are having a problem with the docker tomcat xwiki image with an upstream nginx SSL reverse proxy handling the encryption. Everything appears to work well, except when attempting to upload a file of any size or type. The UI pops up a red box with “An error occurred while uploading $FILE_NAME” However, upon refresh of the page, the file was uploaded successfully. Similar behavior occurs when attempting to delete attachments. “Failed to delete attachment:” This time no $FILE_NAME.

I have seen talk of this type of error being caused by misconfigured nginx proxy configs and the tomcat config inside the container. Here is our ngnix config:

server {
listen 80;
return 301 https://$host$request_uri;
}

server {

listen 443;
server_name dom.domain.com;

error_page 403 =404;

ssl_certificate           /etc/pki/tls/prox.cer;
ssl_certificate_key       /etc/pki/tls/prox_combined.key;

ssl on;
ssl_session_cache  builtin:1000  shared:SSL:10m;
ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;

access_log            /var/log/nginx/kb.access.log;

location / {

  proxy_set_header        Host $http_host;
  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        X-Scheme $scheme;
  proxy_redirect off;
  proxy_pass          http://dock.domain.com:8080;
  proxy_read_timeout  90;
  proxy_send_timeout  90;
  client_max_body_size 2048m;

}
  }

As suggested in the official docs for nginx reverse proxy, we add the following to the server.xml inside the docker container:

<Engine name="Catalina" defaultHost="localhost">
  <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"/>

Any pointers would be much appreciated.

I don’t know. I don’t use a reverse proxy so I’m clueless on its setup.

The only difference with the Docker image and the other distributions of XWik is that XWiki is deployed as ROOT in Tomcat.

Do you get any error in the XWiki logs?

The only error I am seeing that logs when a user attempts an attachment or deletion of the attachment is found in the localhost.$DATE.log file:

06-Mar-2019 16:57:48.205 SEVERE [http-nio-8080-exec-10] org.apache.catalina.core.ApplicationDispatcher.invoke Servlet.service() for servlet [resourceReferenceHandler] threw exception
 java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
        at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:456)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.sendError(AbstractServletResourceReferenceHandler.java:236)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.handle(AbstractServletResourceReferenceHandler.java:88)
        at org.xwiki.resource.internal.DefaultResourceReferenceHandlerChain.handleNext(DefaultResourceReferenceHandlerChain.java:79)
        at org.xwiki.resource.internal.AbstractResourceReferenceHandlerManager.handle(AbstractResourceReferenceHandlerManager.java:82)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.handleResourceReference(ResourceReferenceHandlerServlet.java:159)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.service(ResourceReferenceHandlerServlet.java:87)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.xwiki.container.servlet.filters.internal.SetHTTPHeaderFilter.doFilter(SetHTTPHeaderFilter.java:63)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:356)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316)
        at org.xwiki.resource.servlet.RoutingFilter.doFilter(RoutingFilter.java:147)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:679)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

06-Mar-2019 16:57:48.205 SEVERE [http-nio-8080-exec-10] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [default] in context with path [] threw exception
 java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
        at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:456)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.sendError(AbstractServletResourceReferenceHandler.java:236)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.handle(AbstractServletResourceReferenceHandler.java:88)
        at org.xwiki.resource.internal.DefaultResourceReferenceHandlerChain.handleNext(DefaultResourceReferenceHandlerChain.java:79)
        at org.xwiki.resource.internal.AbstractResourceReferenceHandlerManager.handle(AbstractResourceReferenceHandlerManager.java:82)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.handleResourceReference(ResourceReferenceHandlerServlet.java:159)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.service(ResourceReferenceHandlerServlet.java:87)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.xwiki.container.servlet.filters.internal.SetHTTPHeaderFilter.doFilter(SetHTTPHeaderFilter.java:63)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:356)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316)
        at org.xwiki.resource.servlet.RoutingFilter.doFilter(RoutingFilter.java:147)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:679)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

I take that back. I am also seeing this in the tomcat logs:

2019-03-06 16:57:48,201 [http://base.domain.com/webjars/wiki%3Axwiki/jquery/2.2.2/jquery.min.js?r=1] ERROR ebJarsResourceReferenceHandler - Failed to read resource [jquery/2.2.2/jquery.min.js]
org.xwiki.resource.ResourceReferenceHandlerException: Failed to read resource [jquery/2.2.2/jquery.min.js]
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.serveResource(AbstractServletResourceReferenceHandler.java:180)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.handle(AbstractServletResourceReferenceHandler.java:85)
        at org.xwiki.resource.internal.DefaultResourceReferenceHandlerChain.handleNext(DefaultResourceReferenceHandlerChain.java:79)
        at org.xwiki.resource.internal.AbstractResourceReferenceHandlerManager.handle(AbstractResourceReferenceHandlerManager.java:82)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.handleResourceReference(ResourceReferenceHandlerServlet.java:159)
        at org.xwiki.resource.servlet.ResourceReferenceHandlerServlet.service(ResourceReferenceHandlerServlet.java:87)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.xwiki.container.servlet.filters.internal.SetHTTPHeaderFilter.doFilter(SetHTTPHeaderFilter.java:63)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:728)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:470)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:356)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:316)
        at org.xwiki.resource.servlet.RoutingFilter.doFilter(RoutingFilter.java:147)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
        at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:679)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
        at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:364)
        at org.apache.catalina.connector.OutputBuffer.flushByteBuffer(OutputBuffer.java:833)
        at org.apache.catalina.connector.OutputBuffer.append(OutputBuffer.java:738)
        at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:399)
        at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:377)
        at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:96)
        at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2315)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:2270)
        at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2291)
        at org.apache.commons.io.IOUtils.copy(IOUtils.java:2246)
        at org.xwiki.resource.servlet.AbstractServletResourceReferenceHandler.serveResource(AbstractServletResourceReferenceHandler.java:178)
        ... 36 common frames omitted
Caused by: java.io.IOException: Broken pipe
        at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
        at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
        at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
        at sun.nio.ch.IOUtil.write(IOUtil.java:65)
        at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471)
        at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:134)
        at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:101)
        at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:157)
        at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1306)
        at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:726)
        at org.apache.tomcat.util.net.SocketWrapperBase.writeBlocking(SocketWrapperBase.java:496)
        at org.apache.tomcat.util.net.SocketWrapperBase.write(SocketWrapperBase.java:434)
        at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.doWrite(Http11OutputBuffer.java:623)
        at org.apache.coyote.http11.filters.IdentityOutputFilter.doWrite(IdentityOutputFilter.java:127)
        at org.apache.coyote.http11.Http11OutputBuffer.doWrite(Http11OutputBuffer.java:225)
        at org.apache.coyote.Response.doWrite(Response.java:602)
        at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:352)
        ... 46 common frames omitted

Not really addressing your problem, but it is super easy to use SSL with docker xwiki tomcat image (and I can confirm that file upload works). Just map two extra volumes: keystore and server.xml files.

add to server.xml

    <!-- Define TLS connector for xwiki -->
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Nio2Protocol" sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" defaultSSLHostConfigName="*.example.com">
        <SSLHostConfig protocols="TLSv1.2,+TLSv1.3" hostName="*.example.com">
            <Certificate certificateKeystoreFile="conf/xwiki.keystore.pkcs12" certificateKeystorePassword="*****" certificateKeyAlias="xwiki" certificateKeyPassword="*****" certificateKeystoreType="PKCS12" type="RSA" />
        </SSLHostConfig>
    </Connector>
sudo docker run -d --net=xwiki-nw --name xwiki-ssl --restart=always --expose=8443 -p 192.168.1.100:443:8443 -v /home/zzz/xwiki/xwiki:/usr/local/xwiki -v /home/zzz/xwiki/server.xml:/usr/local/tomcat/conf/server.xml -v /home/zzz/xwiki/xwiki_web.keystore.pkcs12:/usr/local/tomcat/conf/xwiki.keystore.pkcs12 -e DB_USER=xwiki -e DB_PASSWORD=***** -e DB_DATABASE=xwiki -e DB_HOST=xwiki-mysql xwiki:11-mysql-tomcat

Thanks for the reply. That’s def good to know for future reference. Unfortunately, this project requires a Nginx reverse proxy handling the SSL.

You might use the browwer “network tools” to see what it tries to request when displaying the error message. As the error message is not caused by an actual failure of the action but fetching feedback it might be the UI sends you to a wrong URL to fetch feedback, or the UI code does not handle the redirect from the server in case it accidentially requests a http-URL instead of a https-URL.

I usually add some scheme="https" secure="true" to the <Connector inside the tomcat server.xml That will make tomcat pretty useless if connected directly but helps when being accessed via a reverse proxy that handles the SSL-Encryption.

Good idea. Here is what I got:

Mixed Content: The page at 'https://dom.domain.com/bin/view/Main/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://dom.domain.com/bin/get/Main/?xpage=attachmentslist&forceTestRights=1'. This request has been blocked; the content must be served over HTTPS.

So clearly this is an issue with the wiki sending non-ssl links during the file upload process. I do have scheme=“https” secure=“true” set in my server.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
    maxThreads="150" minSpareThreads="4"/>
-->


<!-- A "Connector" represents an endpoint by which requests are received
     and responses are returned. Documentation at :
     Java HTTP Connector: /docs/config/http.html
     Java AJP  Connector: /docs/config/ajp.html
     APR (HTTP/AJP) Connector: /docs/apr.html
     Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />
       secure="true"
           scheme="https" />
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
           port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8080" />
-->
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
     This connector uses the NIO implementation. The default
     SSLImplementation will depend on the presence of the APR/native
     library and the useOpenSSL attribute of the
     AprLifecycleListener.
     Either JSSE or OpenSSL style configuration may be used regardless of
     the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                     type="RSA" />
    </SSLHostConfig>
</Connector>
-->
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
     This connector uses the APR/native implementation which always uses
     OpenSSL for TLS.
     Either JSSE or OpenSSL style configuration may be used. OpenSSL style
     configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
           maxThreads="150" SSLEnabled="true" >
    <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
    <SSLHostConfig>
        <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                     certificateFile="conf/localhost-rsa-cert.pem"
                     certificateChainFile="conf/localhost-rsa-chain.pem"
                     type="RSA" />
    </SSLHostConfig>
</Connector>
-->

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


<!-- An Engine represents the entry point (within Catalina) that processes
     every request.  The Engine implementation for Tomcat stand alone
     analyzes the HTTP headers included with the request, and passes them
     on to the appropriate Host (virtual host).
     Documentation at /docs/config/engine.html -->

<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">
  <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"/>

  <!--For clustering, please take a look at documentation at:
      /docs/cluster-howto.html  (simple how to)
      /docs/config/cluster.html (reference documentation) -->
  <!--
  <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
  -->

  <!-- Use the LockOutRealm to prevent attempts to guess user passwords
       via a brute-force attack -->
  <Realm className="org.apache.catalina.realm.LockOutRealm">
    <!-- This Realm uses the UserDatabase configured in the global JNDI
         resources under the key "UserDatabase".  Any edits
         that are performed against this UserDatabase are immediately
         available for use by the Realm.  -->
    <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
           resourceName="UserDatabase"/>
  </Realm>

  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">

    <!-- SingleSignOn valve, share authentication between web applications
         Documentation at: /docs/config/valve.html -->
    <!--
    <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
    -->

    <!-- Access log processes all example.
         Documentation at: /docs/config/valve.html
         Note: The pattern used is equivalent to using pattern="common" -->
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern="%h %l %u %t &quot;%r&quot; %s %b" />

  </Host>
</Engine>

Interestingly this setting seems not to be used in this case, as it contains redirectPort="8443" which should mean rewritten http-ulrs should be send to https://dom.domain.com:8443/ instead. (Then I think URLs generated via JavaScript are not affected by this setting.)

Without digging deeper into the issue I remember there is a “Secure (SSL):” -Setting for each wiki, i.e. for the main wiki at: https://dom.domain.com/xwiki/bin/view/XWiki/XWikiServerXwiki - maybe it helps to set this one to “true” ?

Edit: the error seems to happen when the list of attachments is going to be refreshed after a successful file upload. To refresh the list, XWiki generates a relative URL to /xwiki/bin/get/Some/Page/?xpage=attachmentslist&forceTestRights=1 and it should be up to the browser to complete this URL to an absolute one, especially deciding if it is HTTPS or HTTP. As the page is delivered via HTTPS, it should obviously select HTTPS too. (Unless I am overlooking something …)

As we are fiddling with the browser tools anyway, can you please open the browser JavaScript console while on some wiki page and type in:

 XWiki.currentDocument.getURL('get','xpage=attachmentslist&forceTestRights=1')

I get back a relative URL (like "/xwiki/bin/get/Main/?xpage=attachmentslist&forceTestRights=1" ) but maybe this is different in your case?

That page does not seem to exist in our environment:

2019-03-11_9-10-42

Perhaps this is part of the problem?

You are correct that this error happens when the attachment list is refreshed. I opened up the console and ran the query and also got a relative URL, but without “/xwiki” in the path:

>> XWiki.currentDocument.getURL('get','xpage=attachmentslist&forceTestRights=1')
<< "/bin/get/Main/?xpage=attachmentslist&forceTestRights=1"

The missing /xwiki in the URL is ok, as you are running XWiki as the ROOT webapp (unlike my test setup), so this is expected and no problem. About the missing .../XWiki/XWikiServerXwiki I did not realize you are running it as the ROOT webapp; the path should be https://dom.domain.com/bin/view/XWiki/XWikiServerXwiki instead in your case.

Duh. I should have figured that on my own! The setting was indeed set to “no.” However, changing it to “Yes” and restarting the wiki had no effect on the behavior.

-Scott

What’s interesting about this problem is that it seems to be exactly what the Admin guide talks about in the “Configuring tomcat for https” section, specifically the first bullet point:

* If using HTTPS for accessing XWiki, several modifications have to be made to ensure proper functionality. Since urls are generated from relative path (/xwiki/bin/show/Space/Page), Tomcat has to know which protocol to use, otherwise JSON requests with redirect fails (attachment uploads, extension updating, etc.)
* Modify connector (in server.xml) to <Connector port="8080" ... secure="true" scheme="https" />
* Modify host (in server.xml) and add Remote Ip Valve <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="x-forwarded-for" protocolHeader="x-forwarded-proto" /> (only needed if using another server for HTTPS)

If using another server as a HTTPS proxy (such as Nginx or Apache httpd), X-Forwarded-For and X-Forwarded-Proto headers have to be set!

So we have code for that in server.xml:

<Engine name="Catalina" defaultHost="localhost">
  <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"/>

Sigh, I still cannot make any sense out why this error happens.

As the latest shot in the dark, can you check if the HTML source of the page contains a <base element? Maybe this exists and is pointing to the wrong protocol, like e.g.

<base href="http://dom.domain.com/bin/view/Main/" />

I really appreciate you going down the rabbit hole with me on this!

I took a look at the page source and there is no <base element pointing to an http protocol.

I’m am out of ideas. Starting to think about trying to put Xwiki on top of a Apache docker image to see if I can isolate this to Tomcat.

Hi, I’d like to know how to get the tomcat ssl config running as I like to use the docker container with ssl on port 8443 (cause of the reverse proxy problems with ssl).

So far I did:

  • Created a new volume:
  • xwiki-tomcat:/usr/local/tomcat/conf
  • Create a certificate for tomcat like that:
    Tomcat Certificate

    Changed the server.xml to (sorry for the format but I couldn’t use the code snippet here properly):

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443">
           

<Connector port="8443" 
protocol="org.apache.coyote.http11.Http11Nio2Protocol" 
sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation" 
maxThreads="150" SSLEnabled="true" scheme="https" secure="true" 
defaultSSLHostConfigName="wiki.domain.de">
<SSLHostConfig protocols="TLSv1.2,+TLSv1.3" hostName="wiki.domain.de">
<Certificate certificateKeystoreFile="tomcat.pfx" certificateKeystorePassword="testtest" certificateKeyPassword="testtest" certificateKeystoreType="PKCS12" type="RSA" />
</SSLHostConfig>
</Connector>

tomcat-ssl

But when I start the docker container I can’t see the server listening on Port 8443, just 8080.

My ports on the docker-composer file:

version: '2'
networks:
  bridge:
    driver: bridge
services:
  web:
    # Use an already built XWiki image from DockerHub.
    image: "xwiki:lts-mysql-tomcat"
    restart: always
    container_name: xwiki-mysql-tomcat-web
    depends_on:
      - db
    ports:
      - "8080:8080"
      - "8443:8443"