Two Factor Authen. (2FA) w/ Yubico OTP and JBoss EAP 6 or Wildfly 8

Version 8

    Yubico key (https://www.yubico.com/) is a really smart key to get a strong authentication to login into your e-account (e-bank, web mail, etc...).

    Several e-companies includes/are including this features to login.

     

    Contrary to  Yubico OTP and JBoss EAP 6 or Wildfly 8, this post implements the 2FA feature.

    It means user will pass his password and a token (form yubikey) to authenticate.

     

    Goal : how integrate 2FA with Yubico OTP and an other login module inside JBoss EAP/Wildfly.

     

     

    First step : Materials

     

    For this example you need several materials :

     

    This example will check and control the OTP with Yubico Cloud Service, the JBoss instance needs an acces to Internet (HTTP/HTTPS)

     

    Second step : Compile Yubico Java Client

     

    Retrieve the yubico-java-client source code and compile it

    You must retrieve my PR patch : Add JACC feature for Form-Based Authentication by gautric · Pull Request #10 · Yubico/yubico-java-client · GitHub

     

    greg@a.net> git clone git@github.com:Yubico/yubico-java-client.git
    greg@a.net> cd yubico-java-client
    greg@a.net> mvn clean package
    
    .... OMIT .....
    
    [INFO] ------------------------------------------------------------------------
    [INFO] Reactor Summary:
    [INFO]
    [INFO] Yubico OTP validation client ....................... SUCCESS [  0.003 s]
    [INFO] Yubico OTP validation client protocol 1 ............ SUCCESS [  2.254 s]
    [INFO] Yubico OTP validation client protocol 2 ............ SUCCESS [  3.798 s]
    [INFO] Yubico JAAS module ................................. SUCCESS [  0.710 s]
    [INFO] yubico-demo-server ................................. SUCCESS [  2.782 s]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 9.704 s
    [INFO] Fin
    
    
    
    
    
    
    

     

    You should get several outputs JAR

     

    the first one is the yubico cloud client and the second one is the Yubico login module (Yubico JAAS Impl)

     

    • Yubico cloud client will control and check the OTP provided by the Yubico Key and will return the authorization.
      • Yubico OTP validation client protocol 2 : yubico-validation-client2-<Version>.jar
    • Yubico login module provides a Login module implementation to plug into an application or an application server using JAAS API.
      • Yubico JAAS module : yubico-jaas-module-<Version>.jar

     

     

    Third step : Create Yubico JBoss Module

     

    Create an module.xml file

    <?xml version="1.0" encoding="UTF-8"?>
    <!--
      ~ JBoss, Home of Professional Open Source.
      ~ Copyright 2014, Red Hat, Inc., and individual contributors
      ~ as indicated by the @author tags. See the copyright.txt file in the
      ~ distribution for a full listing of individual contributors.
      ~
      ~ This is free software; you can redistribute it and/or modify it
      ~ under the terms of the GNU Lesser General Public License as
      ~ published by the Free Software Foundation; either version 2.1 of
      ~ the License, or (at your option) any later version.
      ~
      ~ This software is distributed in the hope that it will be useful,
      ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
      ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      ~ Lesser General Public License for more details.
      ~
      ~ You should have received a copy of the GNU Lesser General Public
      ~ License along with this software; if not, write to the Free
      ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
      ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
      -->
    <module xmlns="urn:jboss:module:1.1" name="com.yubico">
       <resources>
         <resource-root path="yubico-jaas-module-3.0.0-SNAPSHOT.jar"/>
         <resource-root path="yubico-validation-client2-3.0.0-SNAPSHOT.jar"/>
         <resource-root path="vt-ldap-3.3.3.jar"/>
       </resources>
       <dependencies>
         <module name="org.picketbox"/>
         <module name="javax.api"/>
         <module name="org.slf4j"/>
         <module name="org.apache.commons.codec" />
         <module name="javax.security.jacc.api" />   <!-- Mandatory -->
         <module name="javax.servlet.api"/>          <!-- Mandatory -->
         <module name="org.jboss.common-core"/>      <!-- Mandatory -->
       </dependencies>
    </module>
    
    
    
    
    
    
    
    
    
    
    

     

    Copy it and jars too into your JBoss Module directory.

     

    $JBOSS_HOME/module/com/yubico/main

     

      NB don't forget to copy this jar into the directory, pick up directly from you .m2 repository

     

       edu.vt.middleware:vt-ldap:3.3.3

     

    Tips : to debug quickly, you can create symbolic link

     

    4th step : Get client Id & Secret Key for your JBoss Instance

     

    To use the Yubico Cloud Service you need to get your own Client Id and Secret Key (aka client Key)

     

      https://upgrade.yubico.com/getapikey/

     

    Capture d’écran 2015-03-01 à 16.23.49.png

     

    Capture d’écran 2015-03-01 à 16.11.02.png

    Usefull for 6th step

    5th step : Get PublicId of you Yubico Key

     

      For each of your clients have to provide theirs own PublicId to you

     

    https://demo.yubico.com/start/otp/standard

     

     

    Capture d’écran 2015-03-01 à 16.13.12.png

     

    Capture d’écran 2015-03-01 à 16.07.53.png

    Use the Identity field as <PublicId>

     

    6th step : Create PublicId --> Login User mapping file

     

      Create a mapping file with all previous PublicId and Login user

     

    greg@a.net> cat ${jboss.server.config.dir}/id2name_textfile.conf
    
    yk.<PublicId>.user = <login>
    
    
    
    
    
    
    
    
    
    
    

     

     

    We use this Yubico JAAS option to authenticate the Yubico key with the User Login. An user with an unknown Yubikey Id cannot logon.

     

     

    7th step : Configure your JBoss Instance Security Domain

     

    Create a new security domain

     

    <security-domain name="2fa-auth" cache-type="default">
          <authentication>
              <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required">
                  <module-option name="usersProperties" value="file://${jboss.server.config.dir}/application-users.properties"/>
              </login-module>
              <login-module code="com.yubico.jaas.YubikeyLoginModule" flag="required" module="com.yubico">
                  <module-option name="clientId" value="12123"/>
                  <module-option name="clientKey" value="U873jhsYT629uuh7gban65+p2Io="/> <!-- client Key aka secret Key -->
                  <module-option name="id2name_textfile" value="${jboss.server.config.dir}/id2name_textfile.conf"/>
                  <module-option name="jacc" value="true"/>                    <!--jacc option in login module at true to retrieve j_otp field (see 9th step) -->
              </login-module>
          </authentication>
          <mapping>
              <mapping-module code="SimpleRoles" type="role">  <!-- for example only -->
                  <module-option name="<PublicId>" value="admin"/>
              </mapping-module>
          </mapping>
      </security-domain>
    
    
    
    
    
    
    
    
    
    
    

      we have to create an mapping module to retrieve all user roles, for this example we use SimpleRoles but you can change it to retrieve from an other data source.

     

    8th step : Configure your application to use yubico-auth

     

      to use the previous "yubico-auth" inject it into your jboss-web.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <jboss-web>
         <security-domain>2fa-auth</security-domain>
      </jboss-web>
    
    
    
    
    
    
    
    
    
    
    

     

    9th step :  Use the Form Auth mechanism

     

    Into your login page, you have to add an extra input form to store the otp field

     

    <form method='post' action='j_security_check'>
      <input type='text' name='j_username'>
      <input type='password' name='j_password'>
      <input type='text' name='j_otp'>                         <!-- the new field form, jacc option in login module at true to retrieve it (see 7th step) -->
    </form>
    
    
    
    
    
    
    
    
    
    
    

     

     

    into your web.xml file

     

    <web-app>
    
    <security-constraint>
      <web-resource-collection>
       <web-resource-name>User Auth</web-resource-name>
       <url-pattern>/user/*</url-pattern>
      </web-resource-collection>
    
    <auth-constraint>
      <role-name>admin</role-name>
      <role-name>manager</role-name>
    </auth-constraint>
    
    </security-constraint>
    <login-config>
    
    <auth-method>FORM</auth-method>
      <realm-name>User Auth</realm-name>
      <form-login-config>
       <form-login-page>login.jsp</form-login-page>
       <form-error-page>error.jsp</form-error-page>
      </form-login-config>
    </login-config>
    
    <security-role>
      <role-name>admin</role-name>
    </security-role>
    
    <security-role>
      <role-name>manager</role-name>
    </security-role>
    
    </web-app>
    
    
    
    
    
    
    
    
    
    
    

     

     

    10th step : Enjoy It & Debug authentication log to check the configuration

    14:07:50,155 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request GET /2fa-demo/user/
    14:07:50,155 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Calling hasUserDataPermission()
    14:07:50,156 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1)   User data constraint has no restrictions
    14:07:50,157 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Calling authenticate()
    14:07:50,157 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Save request in session 'QIFjgJB8A9nfOVza+4ea5Rs5'
    14:07:50,158 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Failed authenticate() test
    14:07:50,158 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null
    14:07:54,146 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Begin invoke, caller=null
    14:07:54,146 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request POST /2fa-demo/user/j_security_check
    14:07:54,147 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authenticating username '<LOGIN>'
    14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Initializing YubikeyLoginModule
    14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Trying to instantiate com.yubico.jaas.impl.YubikeyToUserMapImpl
    14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Begin OTP login
    14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Skipping token, not a valid YubiKey OTP (too short, 7 < 32)
    14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP from j_otp token : <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX>
    14:07:54,152 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Checking OTP <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX>
    14:07:54,807 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> verify result : OK
    14:07:54,807 INFO  [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP verified successfully (YubiKey id <YYYYYYYYYYYY>)
    14:07:54,807 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Check if YubiKey <YYYYYYYYYYYY> belongs to user <LOGIN>
    14:07:54,808 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) In commit()
    14:07:54,808 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Committing principal <YubikeyPrincipal><YYYYYYYYYYYY>
    14:07:54,808 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) User: <LOGIN> is authenticated
    14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authentication of '<LOGIN>' was successful
    14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Redirecting to original '/2fa-demo/user/'
    14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Failed authenticate() test ??/2fa-demo/user/j_security_check
    14:07:54,808 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null
    14:07:54,810 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Begin invoke, caller=null
    14:07:54,810 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Restoring principal info from cache
    14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request GET /2fa-demo/user/
    14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Calling hasUserDataPermission()
    14:07:54,810 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1)   User data constraint has no restrictions
    14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Calling authenticate()
    14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Restore request from session 'QIFjgJB8A9nfOVza+4ea5Rs5'
    14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authenticated '<LOGIN>' with type 'FORM'
    14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Proceed to restored request
    14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Calling accessControl()
    14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1)   Checking roles GenericPrincipal[<LOGIN>(admin,)]
    14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) JBWEB000017: User [<LOGIN>] has role [admin]
    14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) hasRole:RealmBase says:true::Authz framework says:true:final=true
    14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) Role found:  admin
    14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) hasResourcePermission:RealmBase says:true::Authz framework says:true:final=true
    14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1)  Successfully passed all security constraints
    14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null
    
    
    
    
    
    
    

     

     

    Into the log trace you can notice severals useful informations :

    • <LOGIN_USER> : the login user
    • XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX : the OTP sent to Yubico Cloud Service
    • YYYYYYYYYYYY : the Public Id, the Yubico key user

    Useful links :


    Glossary :