jgraph / docker-drawio

Dockerized draw.io based on whichever is the most secure image at the time.
GNU General Public License v3.0
1.49k stars 361 forks source link

Self-hosted Drawio does not trust internal SSL certificate used by self-hosted Gitlab #90

Open pitastrudl opened 2 years ago

pitastrudl commented 2 years ago

Hi all,

This issue has been pestering for some time and I am at loss. I am deploying docker-drawio self-contained instance in docker swarm which works almost 100% except Gitlab authorization.

Setup information:

I have set all of the below variables, the DRAWIO_GITLAB_URL is set in the format of gitlab.example.com

DRAWIO_GITLAB_ID: <id> DRAWIO_GITLAB_SECRET: <secret> DRAWIO_GITLAB_URL: https://gitlab.example.com

All is well until I open the Gitlab storage type in Drawio and try to authorize it. My reverse proxy gives out an 502 error that the service is unavailable but the drawio docker container says:

draiwo@host   | 18-Aug-2022 11:28:29.826 SEVERE [http-nio-8080-exec-1] com.mxgraph.online.AbsAuthServlet.contactOAuthServer AUTH-SERVLET: [https://git.example.com/oauth/token] ERROR: PKIX path building failed: sun.security.prov
ider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target ->                                                                                                                                                                      
draiwo@host   | javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target                                       
draiwo@host   |        at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)                                                                                                                                                 
draiwo@host   |        at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)                                                                                                                                                   
draiwo@host   |        at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)                                                                                                                                                   
draiwo@host   |        at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)                                                                                                                                                   
draiwo@host   |        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(Unknown Source)                                                                                                               
draiwo@host   |        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(Unknown Source)                                                                                                                  
draiwo@host   |        at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(Unknown Source)                                                                                                                        
draiwo@host   |        at java.base/sun.security.ssl.SSLHandshake.consume(Unknown Source)                                                                                                                                                     
draiwo@host   |        at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)                                                                                                                                                
draiwo@host   |        at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)                                                                                                                                                
draiwo@host   |        at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)                                                                                                                                                
draiwo@host   |        at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)                                                                                                                                                      
draiwo@host   |        at java.base/sun.security.ssl.SSLSocketImpl.decode(Unknown Source)                                                                                                                                                     
draiwo@host   |        at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(Unknown Source)                                                                                                                                        
draiwo@host   |        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)                                                                                                                                             
draiwo@host   |        at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)                                                                                                                                             
draiwo@host   |        at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)                                                                                                                                       
draiwo@host   |        at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)                                                                                                                     
draiwo@host   |        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)                                                                                                                              
draiwo@host   |        at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
draiwo@host   |        at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
draiwo@host   |        at com.mxgraph.online.AbsAuthServlet.contactOAuthServer(Unknown Source)
draiwo@host   |        at com.mxgraph.online.AbsAuthServlet.doGet(Unknown Source)
draiwo@host   |        at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
draiwo@host   |        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
draiwo@host   |        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
draiwo@host   |        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
draiwo@host   |        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
draiwo@host   |        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
draiwo@host   |        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
draiwo@host   |        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
draiwo@host   |        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
draiwo@host   |        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
draiwo@host   |        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
draiwo@host   |        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
draiwo@host   |        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
draiwo@host   |        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
draiwo@host   |        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
draiwo@host   |        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
draiwo@host   |        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
draiwo@host   |        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)
draiwo@host   |        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1787)
draiwo@host   |        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
draiwo@host   |        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
draiwo@host   |        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
draiwo@host   |        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
draiwo@host   |        at java.base/java.lang.Thread.run(Unknown Source)
draiwo@host   | Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
draiwo@host   |        at java.base/sun.security.validator.PKIXValidator.doBuild(Unknown Source)
draiwo@host   |        at java.base/sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
draiwo@host   |        at java.base/sun.security.validator.Validator.validate(Unknown Source)
draiwo@host   |        at java.base/sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source)
draiwo@host   |        at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
draiwo@host   |        at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
draiwo@host   |        ... 43 more
draiwo@host   | Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
draiwo@host   |        at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
draiwo@host   |        at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
draiwo@host   |        at java.base/java.security.cert.CertPathBuilder.build(Unknown Source)
draiwo@host   |        ... 49 more
draiwo@host   | 18-Aug-2022 11:28:29.893 SEVERE [http-nio-8080-exec-2] com.mxgraph.online.AbsAuthServlet.contactOAuthServer AUTH-SERVLET: [https://git.example.com/oauth/token] ERROR: PKIX path building failed: sun.security.prov
ider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target ->

First attempt

If I execs into the container and try: curl https://gitlab.example.com, it does not work since it does not trust the SSL certificate by default. My solution to this was to mount a ca-certificate file and running update-ca-certificates after the container is finished booting. This worked when using curl but not for the issue above.

Second attempt

I've found some stackoverflow answers saying to use keytool inside the docker container to import the .crt file which can be extracted from the https URL using openssl like:

openssl s_client -connect git.example.com:443  2>&1 < "/" | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'  > /root/tmp.crt

Then using keytool to import it into the cacerts keystore

keytool -import -trustcacerts -alias example.com  -file /root/tmp.crt -keystore /opt/java/openjdk/lib/security/cacerts -storepass changeit

This is imported if you try:

keytool -list -trustcacerts -keystore " /opt/java/openjdk/lib/security/cacerts" -storepass changeit 

Third attempt

I try to do the second attempt but try to use the keystore of tomcat:

keytool -import -trustcacerts -alias example.com  -file /root/tmp.crt -keystore /usr/local/tomcat/.keystore -storepass V3ry1nS3cur3P4ssw0rd

Sadly, this does not change anything as well and if I try to stop and restart tomcat via catalina.sh stop, the container stops and the files get destroyed. I am not sure if it needs a restart once these files are imported. I know all this is temporary as I am changing the container within instead of making a custom image, but first I would like to just make it work and see what could be changed later.

Questions

  1. When I am importing into the keystore, it seems to say: Warning: use -cacerts option to access cacerts keystore which I am not sure how to use, maybe it could do a difference?
  2. Maybe there are some CATALINA_OPTS or JAVA_OPTS that i could pass to make sure it could use the keystore or local ssl cert storage?
  3. Disabling SSL verification or even using http would not be desirable as that can expose the gitlab user passwords and the browser will display a warning.
davidjgraph commented 2 years ago

A sign signed SSL isn't valid, why would it trust it?

pitastrudl commented 2 years ago

Hi David,

This particular use case is strictly for internal use only, otherwise I understand there would be no issues. It is not valid because it is not signed by any public Certificate Authority but an internal one that is trusted in the internal network, therefore I am trying to import it into the container or use it someway else.

pitastrudl commented 2 years ago

Updated title for clarity.

m-mohamedin commented 11 months ago

Is using HTTP only an option since this is an internal setup? It is insecure but maybe suitable for an internal setup

You can do that by setting environment variable DRAWIO_USE_HTTP=1

More details: https://github.com/jgraph/docker-drawio/issues/91

pitastrudl commented 11 months ago

Hi,

To me it seems like a security issue to just go via HTTP even internally. I'm wondering if it's just a matter of importing the certificate correctly in the tomcat docker image.

And just to mention to @davidjgraph , it's not a self-signed certificate but an internally issued Certificate authority. My answer was a bit vague.

Either way, I'll see if I can try to play with the docker image more in the near future.

m-mohamedin commented 11 months ago

Hi,

To me it seems like a security issue to just go via HTTP even internally. I'm wondering if it's just a matter of importing the certificate correctly in the tomcat docker image.

And just to mention to @davidjgraph , it's not a self-signed certificate but an internally issued Certificate authority. My answer was a bit vague.

Either way, I'll see if I can try to play with the docker image more in the near future.

Yeah, unfortunately, we don't have enough experience with that

github-actions[bot] commented 1 month ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See the FAQ for more information.