Uploaded image for project: 'WildFly'
  1. WildFly
  2. WFLY-9251

Security context is not thread safe

    Details

    • Steps to Reproduce:
      Hide

      Please see the attached POC for reproduction:

      • Build the maven project
      • Place the authmodule jar in a wildfly module, along with a module descriptor:
      <module xmlns="urn:jboss:module:1.3" name="be.test.testauthmodule">
      
          <dependencies>
              <!-- Needed for javax.security.auth -->
              <module name="javax.api" export="false"/>
              <module name="org.apache.logging.log4j"/>
           </dependencies>
      
          <resources>
              <resource-root path="authmodule-1.0-SNAPSHOT.jar"/>
          </resources>
      </module>
      
      • Make sure it is loaded eagerly:
         <subsystem xmlns="urn:jboss:domain:ee:4.0">
                    <global-modules>
                             <module name="be.test.testauthmodule"/>
        
      • Specify the realm and login module in the standalone config:
         <security-domain name="TestSecurityDomain" cache-type="default">
                            <authentication>
                                <login-module code="be.test.TestLoginModule" flag="required"/>
                                <login-module code="org.jboss.security.ClientLoginModule" flag="optional"/>
                            </authentication>
                        </security-domain>
        
        <security-realm name="TestRealm">
                        <authentication>
                            <jaas name="TestSecurityDomain"/>
                        </authentication>
                    </security-realm>
        
      • Deploy the war to your wildfly instance
      • Make sure you have several worker threads. In the IO subsystem, I use 8 IO threads and 64 task threads.
      • Launch a bunch of requests simultaneously. Requests must authenticate themeselves using a Basic Authorization header with a login starting by 'test-' to pass authorization. You could paste the following snippet in the browser js console opened from a page serving your wildfly content (to avoid cors):
      var count = 500;
      var failures = [];
      var responseCount = 0;
      
      let url = 'http://localhost:8080/ws-1.0-SNAPSHOT/test';
      
      function onReqLoad(index, event, xhr) {
          if (xhr.readyState === 4) {
              responseCount++;
              console.log(index + ' ' + xhr.status);
              if (xhr.status >= 400) {
                  failures.push(index);
              }
          }
      }
      
      function doit() {
          var i = 0;
          while (i < count) {
              let xhr = new XMLHttpRequest();
              let index = i;
              xhr.addEventListener('load', function (event) {
                      onReqLoad(index, event, xhr)
                  }
              );
              xhr.open('GET', url);
              xhr.setRequestHeader('Authorization', 'Basic dGVzdC1hZXJhOnRlc3Rlc3Rlc3Q=');
              xhr.withCredentials = true;
              xhr.send();
              i++
          }
      
      }
      
      function logStatus() {
          console.log('totlal: ' + responseCount + ' ; failures:');
          console.log(failures);
      }
      doit();
      window.setTimeout(function(){logStatus();}, 30000);
      

      You should notice some requests getting a 401 response, while most of them get the expected 200.

      I you modify your wildfly config to use only a single worker thread and a single task thread, then you should notice that all requests pass, although it takes much longer for the server to process them.

      Show
      Please see the attached POC for reproduction: Build the maven project Place the authmodule jar in a wildfly module, along with a module descriptor: <module xmlns="urn:jboss:module:1.3" name="be.test.testauthmodule"> <dependencies> <!-- Needed for javax.security.auth --> <module name="javax.api" export="false"/> <module name="org.apache.logging.log4j"/> </dependencies> <resources> <resource-root path="authmodule-1.0-SNAPSHOT.jar"/> </resources> </module> Make sure it is loaded eagerly: <subsystem xmlns="urn:jboss:domain:ee:4.0"> <global-modules> <module name="be.test.testauthmodule"/> Specify the realm and login module in the standalone config: <security-domain name="TestSecurityDomain" cache-type="default"> <authentication> <login-module code="be.test.TestLoginModule" flag="required"/> <login-module code="org.jboss.security.ClientLoginModule" flag="optional"/> </authentication> </security-domain> <security-realm name="TestRealm"> <authentication> <jaas name="TestSecurityDomain"/> </authentication> </security-realm> Deploy the war to your wildfly instance Make sure you have several worker threads. In the IO subsystem, I use 8 IO threads and 64 task threads. Launch a bunch of requests simultaneously. Requests must authenticate themeselves using a Basic Authorization header with a login starting by 'test-' to pass authorization. You could paste the following snippet in the browser js console opened from a page serving your wildfly content (to avoid cors): var count = 500; var failures = []; var responseCount = 0; let url = 'http://localhost:8080/ws-1.0-SNAPSHOT/test'; function onReqLoad(index, event, xhr) { if (xhr.readyState === 4) { responseCount++; console.log(index + ' ' + xhr.status); if (xhr.status >= 400) { failures.push(index); } } } function doit() { var i = 0; while (i < count) { let xhr = new XMLHttpRequest(); let index = i; xhr.addEventListener('load', function (event) { onReqLoad(index, event, xhr) } ); xhr.open('GET', url); xhr.setRequestHeader('Authorization', 'Basic dGVzdC1hZXJhOnRlc3Rlc3Rlc3Q='); xhr.withCredentials = true; xhr.send(); i++ } } function logStatus() { console.log('totlal: ' + responseCount + ' ; failures:'); console.log(failures); } doit(); window.setTimeout(function(){logStatus();}, 30000); You should notice some requests getting a 401 response, while most of them get the expected 200. I you modify your wildfly config to use only a single worker thread and a single task thread, then you should notice that all requests pass, although it takes much longer for the server to process them.
    • Affects:
      Compatibility/Configuration, User Experience
    • Workaround Description:
      Hide

      Using a single thread works around the issue, but server performances are too much impacted to consider this a viable workaround.

      Show
      Using a single thread works around the issue, but server performances are too much impacted to consider this a viable workaround.

      Description

      Using a custom JAAS login module, we sometimes fail to obtain the authenticated subject from the 'javax.security.auth.Subject.container' policy context. This appear to be related to the worker threads.

      See the reproduction steps below. When a wildfly instance attempts to authenticate 500 requests coming simultaneously, a bunch of them fail. If you configure wildfly to only use a single worker thread and a single task thread, this issue disappears.

      The issue is as follow:
      I login using HttpServletRequest#login.
      Right after that, login.getUserPrincipal return the correct principal.
      However, sometimes, PolicyContext.getContext("javax.security.auth.Subject.container") returns null. Right after the login.
      In our production app, PolicyContext.getContext("javax.security.auth.Subject.container") returns null during some EJB call, throwing random exceptions from various parts of the application.

        Gliffy Diagrams

          Attachments

            Activity

              People

              • Assignee:
                dlofthouse Darran Lofthouse
                Reporter:
                cghislai charles ghislain
              • Votes:
                0 Vote for this issue
                Watchers:
                7 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: