1 2 Previous Next 15 Replies Latest reply on May 3, 2018 9:39 AM by Jan Kalina

    Elytron and jdbc-realm

    Nicklas Karlsson Master

      So, I'm trying to integrate mutual SSL (smartcard) with realm-protected resources on WildFly 11 so I head off to the helloworld-mutual-ssl-secured example.

      I import the smartcard certificates into the truststore and it works fine but since I only want auth in parts of my sample app, I modify web.xml so that only the /secure/* area is secured and remove the need-client-auth from the https-connectors ssl-context.

      Now I can access / without authentication and the unsecure part of my app just fine but if I enter /secure/, I get a 403 without any call to the smartcard PIN-dialogue.

       

      Since I want to check the smart card against the DB, I tried to add a

       

      <jdbc-realm name="dbRealm">

          <principal-query sql="select auth(?) from dual" data-source="qsDS">

              <attribute-mapping>

                  <attribute to="Roles" index="1"/>

              </attribute-mapping>

          </principal-query>

      </jdbc-realm>

       

      and modified

       

      <aggregate-realm name="QuickstartRealm" authentication-realm="KeyStoreRealm" authorization-realm="dbRealm"/>

       

      Am I correct in guessing that the authentication is still to be done against the KeyStoreRealm (trust store and client certs) and then the

       

      <x500-attribute-principal-decoder name="QuickstartDecoder" attribute-name="cn"/>

       

      referenced in

       

      <security-domain name="QuickstartDomain" default-realm="QuickstartRealm" permission-mapper="default-permission-mapper" principal-decoder="QuickstartDecoder">

          <realm name="QuickstartRealm" role-decoder="groups-to-roles"/>

      </security-domain>

       

      should pass the principal into my jdbc-realm for role-checking?

       

      And more importantly - how can I check where in the chain there is a problem? I cranked up org.wildfly.extension.elytron and org.wildfly.security to trace and started with -Djava.net.debug and get a *lot* of stuff but nothing I can decipher

       

      Thanks in advance,

        Nik

        • 1. Re: Elytron and jdbc-realm
          Martin Choma Master

          Your general understanding is correct. So 2-way SSL works OK when whole app is secured? There is no issue so far.

           

          Problem is how to split application to secured and unsecured.

          You can't remove the need-client-auth attribute. This is how server knows he should request certificate from user.

          But when you return it it starts to ask for certificate also on unsecured part and that is problem, right? You can use want-client-auth instead of need-client-auth. This differs in that way user has possibility to cancel certificate dialog.

           

          Or can you split your app to 2 apps each secured by different ssl-context?

          1 of 1 people found this helpful
          • 2. Re: Elytron and jdbc-realm
            Martin Choma Master

            Was you able to split app to CLIEN_CERT secured and unsecured part prior to Elytron? How?

            • 3. Re: Elytron and jdbc-realm
              Nicklas Karlsson Master

              It was a long time since I tried it but looking from my notes, I had a web.xml security-constraint for a pattern and a CLIENT-CERT login config with a security-domain + DB login module. Technically, it shouldn't be impossible to have an application split up in three parts where one is unsecured, the other is form-secured and the third with client certs(?)

              • 4. Re: Elytron and jdbc-realm
                Nicklas Karlsson Master

                Thanks, I'll try with the want-client-auth. Splitting the application in parts is not practical since a plain login should be the fallback if the user cancels the smartcard-pin-dialogue

                 

                <testing>

                 

                OK. With the "wants", the dialogue still pops up even for the / and the unsecured parts but the PIN dialogue can be cancelled and you can move on so it's a start

                Now, according to the log I get a

                 

                2018-03-07 12:35:44,918 TRACE [org.wildfly.security] (default task-30) Evidence verification failed - alias [<LastName> <FirstName> <serial#>] does not exist in KeyStore

                 

                So the problem might be that

                 

                                <aggregate-realm name="QuickstartRealm" authentication-realm="KeyStoreRealm" authorization-realm="dbRealm"/>

                 

                expects to find the principal as an alias in the trust store (which it of course can't since all individual cards cannot be registered there) and I should skip the aggregate use the jdbc-realm for both authentication and authorization? Or can I skip this part completely? Jdbc-realm examples usually have queries like "select password, roles..." but since the SSL level is OK with the certificate that signed for the card and the PIN is actually the password, how is jdbc-realm supposed to work with CLIENT-CERT when it comes to the concept of password?

                • 5. Re: Elytron and jdbc-realm
                  Jan Kalina Newbie

                  CLIENT-CERT authentication in Elytron currently requires certificate verification against realm - I think currently only ldap-realm and keystore-realm supports X509PeerCertificateChainEvidence, so you need:

                  • a) KeyStore containing all user certificates (which is not practical as you say)
                  • b) LDAP contaning user certificate serial numbers/hashes

                  jdbc-realm currently does not support certificate verification.

                  Without realm verificating certificates there will be the "Evidence verification failed", regardless authorization configuration.

                  1 of 1 people found this helpful
                  • 6. Re: Elytron and jdbc-realm
                    Nicklas Karlsson Master

                    Ngh, thats annoying. Is there a simple way to write a completely custom realm that would accept the X509PeerCertificateChainEvidence that would just say "OK" for the evidence and internally use a datasource for fetching the roles? Or perhaps a handy extension-point when using a custom-realm that extends the jdbc-realm-implementation?

                    • 7. Re: Elytron and jdbc-realm
                      Jan Kalina Newbie

                      There is custom-realm - you just need to create wildfly module with custom SecurityRealm implementation (you can use custom-elytron-realm as an example).

                      You cannot use other realm defined in subsystem, but (as you will have wildfly-elytron in your module dependencies) you can create new JdbcSecurityRealm inside of your realm.

                       

                      If you dont need supported solution, you can also alternatively try my experimental patch of Elytron which allows to disable the verification: [ELY-1418]

                      Its usage is described in blogpost: CLIENT-CERT authentication with Elytron

                      • 8. Re: Elytron and jdbc-realm
                        Nicklas Karlsson Master

                        Thanks for the pointer, I started on a custom-realm since it appears to be the less intrusive approach. Essentially what I'm trying out is overriding

                         

                           public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName)

                              throws RealmUnavailableException

                           {

                              return X509PeerCertificateChainEvidence.class.isAssignableFrom(evidenceType) ? SupportLevel.POSSIBLY_SUPPORTED

                                 : SupportLevel.UNSUPPORTED;

                           }

                        and

                         

                           public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException

                           {

                              return new RealmIdentity()

                              {

                                 public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException

                                 {

                                    return true;

                                 }

                         

                                 public Principal getRealmIdentityPrincipal()

                                 {

                                    return principal;

                                 }

                         

                                 public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName)

                                    throws RealmUnavailableException

                                 {

                                    return null;

                                 }

                         

                                 public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> evidenceType, String algorithmName,

                                    AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException

                                 {

                                    return null;

                                 }

                         

                                 public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException

                                 {

                                    return null;

                                 }

                         

                                 public boolean exists() throws RealmUnavailableException

                                 {

                                    return true;

                                 }

                              };

                           }

                         

                        and then I have

                         

                                        <security-domain name="vrk_security_domain" default-realm="vrk_realm" permission-mapper="vrk_permission_mapper" principal-decoder="vrk_principal_decoder">

                                            <realm name="vrk_realm" role-decoder="groups-to-roles" role-mapper="vrk_role_mapper"/>

                                        </security-domain>

                        <custom-realm name="vrk_realm" module="my.vrk" class-name="my.VRKRealm"/>

                                        <constant-role-mapper name="vrk_role_mapper">

                                            <role name="authenticated"/>

                                        </constant-role-mapper>

                                        <simple-permission-mapper name="vrk_permission_mapper">

                                            <permission-mapping>

                                                <role name="authenticated"/>

                                                <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>

                                            </permission-mapping>

                                        </simple-permission-mapper>

                                        <x500-attribute-principal-decoder name="vrk_principal_decoder" attribute-name="SERIALNUMBER"/>

                         

                        I might be doing some unnecessary work in the granting of the roles(?) but at least now my "authenticated"-role-secured area prompts for card input and req.getUserPrincipal() is populated with the serial number and req.isUserInRole("authenticated") returns true

                         

                        I still have to do some testing so I don't make some glaring security miss with the realm-implementation ;-)

                        • 9. Re: Elytron and jdbc-realm
                          Jan Kalina Newbie

                          Just note, you should return meaningful SupportLevel from get*Support methods in an identity class too. Feel free to just delegate to appropriate methods of the realm, but you should not return null in them.

                          In verifyEvidence method I would at least recommend to check type of the evidence - if it is X509PeerCertificateChainEvidence, it was already verified by SSLContext, but you should be careful to not accept PasswordGuessEvidence.

                          • 10. Re: Elytron and jdbc-realm
                            Nicklas Karlsson Master

                            What would be the appropriate way of passing parameters to the custom realm?

                             

                            If I were to call a database function for the actual evidence verification but would like to configure what data source to use for the call? Of course I could hardcode the data source and just use different custom-realms for different applications but parameters would be nice.

                            A programmatic approach (e.g. public static ThreadLocals) would probably not work because the authentication is (surprise, surprise) called before the application method

                            • 11. Re: Elytron and jdbc-realm
                              Jan Kalina Newbie

                              To pass parameters into custom-realm (or any other custom-* in elytron) you need to implement interface org.wildfly.extension.elytron.Configurable in your realm and to pass params into "configuration" map:

                              /subsystem=elytron/custom-realm=myRealm:add(
                                  module=jk.demo.myrealm,
                                  class-name=MyRealm,
                                  configuration={myAttribute="myValue"}
                              )

                              You will need to add dependency on the org.wildfly.extension.elytron module into your module.

                              Check branch configurable in my demo:

                              GitHub - hkalina/custom-elytron-realm at configurable

                              1 of 1 people found this helpful
                              • 12. Re: Elytron and jdbc-realm
                                Nicklas Karlsson Master

                                Something strange still going on - I made the changes and on boot, I see the correct configuration - however, when I enter a protected area, I see getRealmIdentity called several times but then nothing happens - my page is entered, the principal is null but no breakpoints within the created RealmIdentity is ever hit...

                                • 13. Re: Elytron and jdbc-realm
                                  Jan Kalina Newbie

                                  Just re-tested and it works ok for me - verifyEvidence() with X509PeerCertificateChainEvidence is called correctly for me.

                                  Is your RealmIdentity returned from getRealmIdentity()?

                                  Is CLIENT_CERT first mechanism in used http-authentication-factory?

                                  • 14. Re: Elytron and jdbc-realm
                                    Nicklas Karlsson Master

                                    I ran out of time at the office but could it have something to do with the fact that I tried to hook it up to the database at the same time. Could javax.sql imports without some module dependency lead to some sort of silent semi-failure due to a ClassNotFoundException?

                                     

                                    Edited:

                                     

                                    Adding a         <module name="javax.api"/> dependency helped. A bit cruel not to throw a CNFE ;-)

                                    1 2 Previous Next