OpenUnison / openunison-k8s

Access portal for Kubernetes
Apache License 2.0
102 stars 5 forks source link

XForward filter does not support WebSocket URL #102

Open tmarback opened 7 months ago

tmarback commented 7 months ago

It seems that the XForward filter on the reverse proxy doesn't support WebSocket connections that use a URL with the wss:// protocol.

I have an application being reverse proxied through the Orchestra pod for authentication, and as per the sample in the documentation I had set it to include the XForward filter in the filter chain.

It worked mostly fine, but eventually I noticed some websocket errors on the console, and after looking through the stack I noticed errors in the orchestra logs:

[2024-02-24 10:41:04,765][XNIO-1 task-10] INFO  AccessLog - [Error] - longhorn-dashboard - wss://<snip>/v1/ws/1s/events - uid=<snip>,ou=shadow,o=Tremolo - NONE [10.42.4.19] - [<snip>]
[2024-02-24 10:41:04,765][XNIO-1 task-10] ERROR ConfigSys - Could not process request
jakarta.servlet.ServletException: Could not execute request
at com.tremolosecurity.proxy.ProxySys.doURI(ProxySys.java:112) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.embedd.NextEmbSys.nextSys(NextEmbSys.java:141) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.auth.AuthMgrSys.doAuthMgr(AuthMgrSys.java:138) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.embedd.NextEmbSys.nextSys(NextEmbSys.java:126) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.auth.AzSys.doAz(AzSys.java:139) ~[unison-sdk-1.0.39.jar:?]
at com.tremolosecurity.embedd.NextEmbSys.nextSys(NextEmbSys.java:111) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.auth.AuthSys.doAuth(AuthSys.java:140) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.embedd.NextEmbSys.nextSys(NextEmbSys.java:105) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.ConfigSys.doConfig(ConfigSys.java:296) [unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.embedd.NextEmbSys.nextSys(NextEmbSys.java:93) [unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.filter.UnisonServletFilter.doFilter(UnisonServletFilter.java:299) [unison-server-core-1.0.39.jar:?]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101) [undertow-servlet-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:393) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859) [undertow-core-2.3.10.Final.jar:2.3.10.Final]
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) [jboss-threads-2.3.6.Final.jar:2.3.6.Final]
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982) [jboss-threads-2.3.6.Final.jar:2.3.6.Final]
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) [jboss-threads-2.3.6.Final.jar:2.3.6.Final]
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377) [jboss-threads-2.3.6.Final.jar:2.3.6.Final]
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282) [xnio-api-3.8.12.Final.jar:3.8.12.Final]
at java.base/java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: java.net.MalformedURLException: unknown protocol: wss
at java.base/java.net.URL.<init>(URL.java:652) ~[?:?]
at java.base/java.net.URL.<init>(URL.java:541) ~[?:?]
at java.base/java.net.URL.<init>(URL.java:488) ~[?:?]
at com.tremolosecurity.proxy.filters.XForward.doFilter(XForward.java:44) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.filter.HttpFilterChainImpl.nextFilter(HttpFilterChainImpl.java:86) ~[unison-server-core-1.0.39.jar:?]
at com.tremolosecurity.proxy.ProxySys.doURI(ProxySys.java:97) ~[unison-server-core-1.0.39.jar:?]
... 43 more

From the error, it seems to boil down to the URL class not supporting wss://.

Granted, I'm not sure if this scheme is very common at all, but it would be nice if it was supported. Given that this URL is only being used to populate the X-Forwarded-Proto header anyway, maybe the URI class might work instead? If my memory serves, newer Java versions did deprecate creating URL instances directly and instead say to create URIs :)

mlbiam commented 7 months ago

Can you please post your Application yaml?

tmarback commented 7 months ago

Right here:

apiVersion: openunison.tremolo.io/v2
kind: Application
metadata:
  name: longhorn-dashboard
  namespace: openunison
spec:
  azTimeoutMillis: 3000
  isApp: true
  urls:
  - hosts:
    - "<snip>"
    filterChain:
    - className: com.tremolosecurity.proxy.filters.XForward
      params:
        createHeaders: "true"
    - className: com.tremolosecurity.proxy.filters.HideCookie
      params: {}
    uri: "/"
    proxyTo: "http://longhorn-frontend.longhorn-system.svc:80${fullURI}"
    authChain: longhorn-oidc
    azRules:
    - scope: filter
      constraint: (groups=k8s-admins)
    results:
      azFail: default-login-failure
    overrideHost: true
    overrideReferer: true
    proxyConfiguration:
      connectionTimeoutMillis: 5000
      requestTimeoutMillis: 5000
      socketTimeoutMillis: 5000
  cookieConfig:
    sessionCookieName: tremolosession
    domain: "<snip>"
    secure: true
    httpOnly: true
    logoutURI: "/logout"
    keyAlias: session-unison

With the proxied service being Longhorn's UI.

Removing the XForward entry in url.filterChain made the websocket errors stop.

mlbiam commented 7 months ago

opened a bug in the core OpenUnison project. We'll get this fixed pretty quickly. I'd like to cut our next release some time in the next week so stay tuned.

mlbiam commented 7 months ago

So I can't reproduce the issue. in my testing environment the URL doesn't come through as wss:// but https://. However the fix for this particlar exception was pretty easy so can you try docker.io/tremolosecurity/betas:1.0.40?