structurizr/onpremises

javax.net.ssl.SSLHandshakeException when loading Graphviz

TimEngie opened this issue · 5 comments

Description

I recently upgraded to the latest docker image (3054) of Structurizr on-prem.
However, when I try to change the lay-out of my diagrams, I can't use the diagram editor (https://www.structurizr.com/help/diagram-editor) anymore.
When looking into the docker logs I can see that there is a javax.net.ssl.SSLHandshakeException thrown when trying to load the diagram editor.

My PC is running from behind a proxy (ZScaler) however, editing the diagram layout was working in the previous version

Steps to reproduce

  1. From the Structurizr home page open any workspace
  2. Click on Diagrams
  3. The icon with the pencil is shown when opening the "Diagrams" page
  4. Icon disappears after loading and changing the layout is not possible anymore.

Screenshot

No response

Code sample

No response

Configuration

No response

Severity

Major

Priority

Medium

Resolution

I have no budget, please fix this for free

More information

2023-05-02 11:42:12 javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
2023-05-02 11:42:12 at java.base/sun.security.ssl.Alert.createSSLException(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.TransportContext.fatal(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.onConsumeCertificate(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.consume(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLHandshake.consume(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.HandshakeContext.dispatch(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.TransportContext.dispatch(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLTransport.decode(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLSocketImpl.decode(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
2023-05-02 11:42:12 at org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory.executeHandshake(SSLConnectionSocketFactory.java:303)
2023-05-02 11:42:12 at org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:275)
2023-05-02 11:42:12 at org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:251)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:181)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:447)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:162)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:172)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:142)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:96)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:152)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:115)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:170)
2023-05-02 11:42:12 at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:123)
2023-05-02 11:42:12 at com.structurizr.view.ThemeUtils.loadThemes(ThemeUtils.java:74)
2023-05-02 11:42:12 at com.structurizr.onpremises.web.graphviz.GraphvizController.post(GraphvizController.java:32)
2023-05-02 11:42:12 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2023-05-02 11:42:12 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
2023-05-02 11:42:12 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
2023-05-02 11:42:12 at java.base/java.lang.reflect.Method.invoke(Unknown Source)
2023-05-02 11:42:12 at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
2023-05-02 11:42:12 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
2023-05-02 11:42:12 at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
2023-05-02 11:42:12 at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
2023-05-02 11:42:12 at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
2023-05-02 11:42:12 at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
2023-05-02 11:42:12 at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
2023-05-02 11:42:12 at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
2023-05-02 11:42:12 at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
2023-05-02 11:42:12 at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
2023-05-02 11:42:12 at javax.servlet.http.HttpServlet.service(HttpServlet.java:696)
2023-05-02 11:42:12 at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
2023-05-02 11:42:12 at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
2023-05-02 11:42:12 at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
2023-05-02 11:42:12 at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
2023-05-02 11:42:12 at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
2023-05-02 11:42:12 at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:102)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:93)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
2023-05-02 11:42:12 at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
2023-05-02 11:42:12 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
2023-05-02 11:42:12 at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
2023-05-02 11:42:12 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
2023-05-02 11:42:12 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
2023-05-02 11:42:12 at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
2023-05-02 11:42:12 at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
2023-05-02 11:42:12 at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
2023-05-02 11:42:12 at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at com.structurizr.onpremises.web.NoOpSpringSessionRepositoryFilter.doFilter(NoOpSpringSessionRepositoryFilter.java:14)
2023-05-02 11:42:12 at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
2023-05-02 11:42:12 at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
2023-05-02 11:42:12 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
2023-05-02 11:42:12 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
2023-05-02 11:42:12 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
2023-05-02 11:42:12 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
2023-05-02 11:42:12 at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
2023-05-02 11:42:12 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
2023-05-02 11:42:12 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
2023-05-02 11:42:12 at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
2023-05-02 11:42:12 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
2023-05-02 11:42:12 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
2023-05-02 11:42:12 at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
2023-05-02 11:42:12 at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
2023-05-02 11:42:12 at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
2023-05-02 11:42:12 at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
2023-05-02 11:42:12 at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
2023-05-02 11:42:12 at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
2023-05-02 11:42:12 at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
2023-05-02 11:42:12 at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
2023-05-02 11:42:12 at java.base/java.lang.Thread.run(Unknown Source)
2023-05-02 11:42:12 Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
2023-05-02 11:42:12 at java.base/sun.security.validator.PKIXValidator.doBuild(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.validator.Validator.validate(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
2023-05-02 11:42:12 ... 128 more
2023-05-02 11:42:12 Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
2023-05-02 11:42:12 at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(Unknown Source)
2023-05-02 11:42:12 at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
2023-05-02 11:42:12 at java.base/java.security.cert.CertPathBuilder.build(Unknown Source)
2023-05-02 11:42:12 ... 133 more

I suspect one of the themes you're using can't be loaded via HTTPS:

...
2023-05-02 11:42:12 at com.structurizr.view.ThemeUtils.loadThemes(ThemeUtils.java:74)
2023-05-02 11:42:12 at com.structurizr.onpremises.web.graphviz.GraphvizController.post(GraphvizController.java:32)

Ok, after some experimenting I discovered that the latest version seem to expect the views to be explicitly defined before the layout can be edited, while it was still possible in the older version(s) I was using (I don't know the exact version as I have been deleting the Docker Images) to edit the "default" generated views when no view was explicitly defined.
So when I add e.g.

...
views { 
   ...
   systemlandscape "SystemLandscape" {
     include *
   }
   ....
}
...

I only have the "SystemLandscape" view, but I can modify the layout of that one.

A bit weird this causes a SSLHandshakeException ihmo.

while it was still possible in the older version(s)

I'm guessing this must have been a very old version, since the createDefaultViews() with automatic layout was introduced in June 2020 -> structurizr/java@08d7b12

A bit weird this causes a SSLHandshakeException ihmo.

As I said, I suspect it's a theme related issue. Are you using any themes?

while it was still possible in the older version(s)

I'm guessing this must have been a very old version, since the createDefaultViews() with automatic layout was introduced in June 2020 -> structurizr/java@08d7b12

It was indeed an old version but not that old, I think I started experimenting with Structurizr OnPrem since it became free to use (if I remember correctly).

A bit weird this causes a SSLHandshakeException ihmo.

As I said, I suspect it's a theme related issue. Are you using any themes?

No themes I was using just this views "code":

views { 
    theme default
  
   styles {
      element "External System" {
      background #999999
  }
}

The automatic layout support will need to load any themes in case they have element width/height properties that will alter the layout, and theme default is just a DSL shortcut for theme https://static.structurizr.com/themes/default/theme.json, so you are using a theme. I suspect that the server running your on-premises installation cannot load https://static.structurizr.com/themes/default/theme.json for some reason, which is why you're seeing the SSL handshake exception.