How to implement Kerberos authentication with Teiid over JDBC

Version 5

    In this "how to" guide I will go over the steps I took to make kerberos authentication work with Teiid JDBC based login. My setup includes two laptop machines running Fedora OS. They both are connected to my wireless router under same subnet. For simplicity sake, I edited "/etc/hosts" file added following lines

     

    192.168.1.76 primary.example.com
    192.168.1.98 secondary.example.com
    
    
    
    
    
    
    
    

     

    Most of you already may have your enterprise kerberos system installed and configured to go, however in for my testing I had to install kerberoes server. I took instructions from http://fedoraproject.org/wiki/Kerberos_KDC_Quickstart_Guide here. On my "secondary.example.com" machine, with root permissions

     

     

    yum -y install krb5-libs krb5-server krb5-workstation

     

     

    Once the installation is complete, I needed to create database for the kerberos credentials, typically enterprises may attach to LDAP etc, for my test case that is not important, so a simple local store is sufficient. To achieve that execute

     

     

    kdb5_util create -s

     

     

    Typically when the time difference between machine is off, kerbeors gives issues, so you can synchronize time using NTP, for that execute

     

    yum install ntp
    service ntpd restart
    
    
    
    
    
    
    
    

     

    Edit the "/var/kerberos/krb5kdc/kadm5.acl" to look like

     

     

    */admin *

     

     

    now edit the "/etc/krb5.conf" file, and it should look like

     

    [logging]
    default = FILE:/var/log/krb5libs.log
    kdc = FILE:/var/log/krb5kdc.log
    admin_server = FILE:/var/log/kadmind.log
    
    [libdefaults]
    dns_lookup_realm = false
    ticket_lifetime = 24h
    renew_lifetime = 7d
    forwardable = true
    rdns = false
    default_realm = EXAMPLE.COM
    
    [realms]
    EXAMPLE.COM = {
      kdc = secondary.example.com:88
      admin_server = secondary.example.com:749
    }
    
    [domain_realm]
    .example.com = EXAMPLE.COM
    example.com = EXAMPLE.COM
    
    
    
    
    
    
    
    

     

    Now edit firewall permissions and allow "kerberos", for my setting, I used graphical management tool that come with fedora to accomplish that. Then start the kerberos server using following commands

     

    /sbin/service krb5kdc start
    /sbin/service kadmin start
    
    
    
    
    
    
    
    

     

    Now we are ready to create users in the kerberos, to begin with we need a "root" user, you can add that by issuing

     

    kadmin.local -q "addprinc root/admin"
    
    
    
    
    
    
    
    

     

    Now login with "root" user in kadmin to create another user

     

    kadmin.local -p root/admin
    
    
    
    
    
    
    
    

     

    Then at the command prompt, to add a user say "rareddy", issue command

     

    ank rareddy
    ank HTTP/primary.example.com
    
    
    
    
    
    
    
    

     

    You can also view all the users in the system by issuing

     

    listprincs
    
    
    
    
    
    
    
    

     

    you will see users like

     

    root/admin@EXAMPLE.COM
    rareddy@EXAMPLE.COM
    krbtgt/EXAMPLE.COM@EXAMPLE.COM
    HTTP/primary.example.com@EXAMPLE.COM
    
    
    
    
    
    
    
    

     

    Here I am going to use "HTTP/primary.example.com@EXAMPLE.COM" user as Service Provider Principle. I could also have used the SPN "krbtgt/EXAMPLE.COM@EXAMPLE.COM". In Kerberos, there are three systems, one is client user (that is you, ex:rareddy), second is where the service you want to access (that is Teiid server), and then the kerberos server itself. In order for the service provider system to authenticate with Kerberos system, you do not want configure using your user token, but need to use a trusted user called SPN. Initially, the client machine talks directly to the Kerberos system gets a token, and then uses that token with service system to negotiate a login. In back end, the service provider system, based on its SPN credentials with authorize the client or not.

     

    To proceed we need to create a keytab, which holds the credentials. To create a keytab, execute below on kadmin console

     

    ktadd -k /path/to/http.keytab  HTTP/primary.example.com
    ktadd -k /path/to/rareddy.keytab rareddy
    
    
    
    
    
    
    
    

     

    Now kerberoes is all setup, copy the file "path/to/http.keytab" to the system where Teiid is running, to a known location. In my case it was "primary.example.com" machine. Install JBoss EAP and Teiid on this machine. If you installation directions see Installation Guide. Copy the "/etc/krb5.conf" file to this machine too in the same location.

     

    Now edit the standalone-teiid.xml file, add


        <system-properties>
            <property name="java.security.krb5.conf" value="/etc/krb5.conf"/>
            <property name="java.security.krb5.debug" value="true"/>
            <property name="java.security.disable.secdomain.option" value="true"/>
            <property name="javax.security.auth.useSubjectCredsOnly" value="false"/>
        </system-properties>
    
    
    
    
    
    
    
    

     

    right after the "<extensions>" element. Add the following in the "security-domains" configuration

     

        <security-domain name="host">
            <authentication>
                <login-module code="Kerberos" flag="required">
                    <module-option name="storeKey" value="true"/>
                    <module-option name="useKeyTab" value="true"/>
                    <module-option name="keyTab" value="/path/to/http.keytab"/>
                    <module-option name="principal" value= "HTTP/primary.example.com@EXAMPLE.COM"/>
                    <module-option name="doNotPrompt" value="true"/>
                    <module-option name="debug" value="true"/>
                </login-module>
            </authentication>
        </security-domain>
        <security-domain name="EXAMPLE.COM">
            <authentication>
                <login-module code="SPNEGO" flag="requisite">
                    <module-option name="password-stacking" value="useFirstPass"/>
                    <module-option name="serverSecurityDomain" value="host"/>
                </login-module>
                <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="requisite">
                    <module-option name="password-stacking" value="useFirstPass"/>
                    <module-option name="usersProperties" value="${jboss.server.config.dir}/teiid-security-users.properties"/>
                    <module-option name="rolesProperties" value="${jboss.server.config.dir}/teiid-security-roles.properties"/>
                </login-module>
            </authentication>
        </security-domain>
    
    
    
    
    
    
    
    

     

    Edit transport setting in the "teiid" subsystem to look like

     

    <transport name="jdbc" socket-binding="teiid-jdbc" protocol="teiid">
         <authentication security-domain="EXAMPLE.COM" type="GSS"/>
    </transport>
    
    
    
    
    
    
    
    

     

    Save the file, and start the JBoss EAP + Teiid server using

     

    <jboss-as>/bin/standalone.sh -c standalone-teiid.xml -b primary.example.com
    
    
    
    
    
    
    
    

     

    Deploy a VDB to this server, and try connecting to it using a JDBC client.

     

    Copy the "rareddy.keytab" file to the client machine where you going to run the JDBC Client application. The idea is, the JDBC client uses "rareddy" user to login, where "HTTP/primary.example.com@EXAMPLE.COM" is the service principle on the server side. The reason I choose HTTP/primary.example.com as SPN is, for REST based applications I could not switch the SPN (l am sure there may be a way, IDK)

     

    Before you can connect using JDBC, you need to create a file "client.conf" with contents like

     

    Client {
        com.sun.security.auth.module.Krb5LoginModule required
        useTicketCache=true
        storeKey=true
        useKeyTab=true
        keyTab="/path/to/rareddy.keytab"
        doNotPrompt=true
        debug=true
        principal="rareddy@EXAMPLE.COM";
    };
    
    
    
    
    
    
    

     

    and set following system properties

     

    -Djava.security.krb5.conf=/etc/krb5.conf
    -Djava.security.auth.login.config=/path/to/client.conf
    -Djavax.security.auth.useSubjectCredsOnly=false
    -Dsun.security.krb5.debug=true
    
    
    
    
    
    
    

     

    Finally you need to use the following properties on the JDBC connection URL string

     

    jaasName=Client;kerberosServicePrincipleName=HTTP/primary.example.com@EXAMPLE.COM

    so, your JDBC connection URL should look like

     

    jdbc:teiid:<vdb>@mm:<host>:31000;jaasName=Client;kerberosServicePrincipleName=HTTP/primary.example.com@EXAMPLE.COM"
    
    
    
    
    
    
    

     

    Once you connect then you can issue the SQL queries. I used SimpleClient from quick starts from here teiid-quickstarts/simpleclient at master · teiid/teiid-quickstarts · GitHub as the JDBC client as it is easier to configure than the tools like SquirreL.

     

    Gotchas: There are plenty!!!

     

    Specified version of key is not available (44)


    Caused by: KrbException: Specified version of key is not available (44)
    
    
    
    
    
    

    I found two reasons for this,

     

    * Your keytab is not correct. You may have added a user, or changed password for the user etc and keytab is no longer valid. Note that each time you update the user, there is KVNO identifier which gets incremented, those need to match with your keytab's version. To see what are the KVNO numbers and supported encryption types, execute

     

    $klist -e -k -t /home/rareddy/development/jbossqe-eap-tests-kerberos-datasource/keytabs/rareddy.keytab
    Keytab name: FILE:/home/rareddy/development/jbossqe-eap-tests-kerberos-datasource/keytabs/rareddy.keytab
    KVNO Timestamp           Principal
    ---- ------------------- ------------------------------------------------------
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (aes256-cts-hmac-sha1-96) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (aes128-cts-hmac-sha1-96) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (des3-cbc-sha1) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (arcfour-hmac) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (camellia256-cts-cmac) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (camellia128-cts-cmac) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (des-hmac-sha1) 
      11 06/16/2014 10:07:32 rareddy@EXAMPLE.COM (des-cbc-md5) 
    
    
    
    
    
    

     

    * Another is, your Service User Principal is wrong. What I found is, I can typically see what my user's service principle when I execute

     

    $klist
    Ticket cache: DIR::/run/user/1000/krb5cc/tktk9TXbf
    Default principal: rareddy@EXAMPLE.COM
    
    Valid starting       Expires              Service principal
    06/16/2014 10:27:55  06/17/2014 10:27:55  krbtgt/EXAMPLE.COM@EXAMPLE.COM
      renew until 06/16/2014 10:27:55
    06/16/2014 10:28:15  06/17/2014 10:27:55  HTTP/primary.example.com@EXAMPLE.COM
      renew until 06/16/2014 10:27:55
    
    
    
    
    
    

     

    For JDBC client, when I used "krbgt/EXAMPLE.COM@EXAMPLE.COM" it worked fine, same was not true when I used the same principal to make a HTTP call. I am not aware who chooses to assign a Service Principal (SPN) in KDC, in my case I took default by KDC. I need to probably further investigate how this works. For HTTP call, it was automatically looking for "HTTP/{host}/{relam}" based SPN.

     

    Checksum Failed

     

    Caused by: java.security.GeneralSecurityException: Checksum failed
      at sun.security.krb5.internal.crypto.dk.AesDkCrypto.decryptCTS(AesDkCrypto.java:451) [rt.jar:1.7.0_60]
      at sun.security.krb5.internal.crypto.dk.AesDkCrypto.decrypt(AesDkCrypto.java:272) [rt.jar:1.7.0_60]
      at sun.security.krb5.internal.crypto.Aes256.decrypt(Aes256.java:76) [rt.jar:1.7.0_60]
      at sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType.decrypt(Aes256CtsHmacSha1EType.java:100)
    
    
    
    
    

    What I saw was typically keytab was wrong, may be configuration or the password has changed.

     

    Encryption Related Issues

     

    Found unsupported keytype (8) for rareddy@EXAMPLE.COM
    
    
    
    
    


    Often you will see the keytype not supported issues, I installed java security libs from "http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html". Here you would need to copy JARs into your "jre/lib/security" directory ans restart the VMs.


    Another place you two places you can control keys being used is, when using "ktadd .." in the kadmin routine above, you can use "-e" option to control which encryption to use. If you do not have control over it, then you edit "krb5.conf" file and in [libdefaults] section you can something like below (below is just an example)


    default_tgs_enctypes = des3-hmac-sha1 des-cbc-crc des-cbc-md5 des3-cbc-sha1 aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 arcfour-hmac
    default_tkt_enctypes = des3-hmac-sha1 des-cbc-crc des-cbc-md5 des3-cbc-sha1 aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 arcfour-hmac
    permitted_enctypes = des3-hmac-sha1 des-cbc-crc des-cbc-md5 des3-cbc-sha1 aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 arcfour-hmac
    
    
    
    
    

     

     

    Further Steps: (for me)

    * Need to secure a Windows machine to see if the I can do the same

    * Test ODBC from windows machine. This is a must need to test.

    * How to use kerberos without using the keytabs?

     

    Hopefully above gave some instructions for configuring kerberos with Teiid. If you got any ideas on above further steps please do post.

     

    Ramesh..