In this "how to" guide I will go over the steps I took to make Kerberos authentication work with a Simple REST based webapp. My setup includes two laptop machines running Fedora OS. They both are connected to my wireless router under same subnet. Note that most of the time you do not need to do configure KDC server at all, your enterprise already may be configured with it, you need to ask your operation guys on how to get the keytabs or how to use SSO without keytabs.
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 Kerberos 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, Kerberos 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. In Kerberos, there are three systems, one is client user (that is you, ex:rareddy), second is where the service you wants access to is running (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 "path/to/http.keytab" to the system where JBoss EAP is running, to a known location. In my case it was "primary.example.com" machine. Install JBoss EAP on this machine.https://docs.jboss.org/author/display/TEIID/Installation+GuideCopy the "/etc/krb5.conf" file to this machine too in the same location.
Now edit the standalone.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"/> </system-properties>
right after the "<extensions>" element. Add the following in the "security-domains" configuration
<security-domain name="host" cache-type="default"> <authentication> <login-module code="Kerberos" flag="required"> <module-option name="storeKey" value="true"/> <module-option name="useKeyTab" value="true"/> <module-option name="refreshKrb5Config" value="true"/> <module-option name="keyTab" value="/path/to/krbtgt.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" cache-type="default"> <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>
teiid-security-roles.properties
# A roles.properties file for use with the UsersRolesLoginModule # username=role1,role2 rareddy@EXAMPLE.COM=krb5 HTTP/primary.example.com@EXAMPLE.COM=krb5
Save the file, and start the JBoss EAP server using
<jboss-as>/bin/standalone.sh -c standalone.xml -b primary.example.com
Web Application
HelloWorld.java
package org.teiid.rest.webapp.kerberoes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; @Path("/hello") public class HelloWorld extends Application { @Context private SecurityContext mySecurityContext; @GET @Path("world") public String helloworld() { return "<message>Hello World! User="+mySecurityContext.getUserPrincipal().getName()+"</message>"; } }
WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>resteasy</display-name> <session-config> <session-timeout>30</session-timeout> </session-config> <listener> <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <context-param> <param-name>resteasy.scan</param-name> <param-value>true</param-value> </context-param> <security-constraint> <display-name>require valid user</display-name> <web-resource-collection> <web-resource-name>Kerberos Application</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>krb5</role-name> </auth-constraint> </security-constraint> <security-role> <description>security role</description> <role-name>krb5</role-name> </security-role> </web-app>
WEB-INF/jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?> <jboss-web> <security-domain>java:/jaas/EXAMPLE.COM</security-domain> <valve> <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name> </valve> <context-root>kerberoes</context-root> </jboss-web>
META-INF/jboss-deployment-structure.xml
<jboss-deployment-structure> <deployment> <dependencies> <module name="org.jboss.security.negotiation" /> </dependencies> </deployment> </jboss-deployment-structure>
A sample project is attached. Build the WAR file, and deploy to the JBoss EAP instance.
Testing:
For testing I chose curl, as I did not find correct way to configure chrome or firefox, that I can see how they issue the negotiate call for kerberos. CURL was much more clear and simple
Before you can issue the http call, execute the following
$kinit -f -k -t /path/to/rareddy.keytab rareddy@EXAMPLE.COM $klist Ticket cache: DIR::/run/user/1000/krb5cc/tktgaFBoW Default principal: rareddy@EXAMPLE.COM Valid starting Expires Service principal 06/16/2014 13:20:48 06/17/2014 13:20:47 krbtgt/EXAMPLE.COM@EXAMPLE.COM renew until 06/16/2014 13:20:48 $curl --negotiate -u foo:bar http://primary.example.com:8080/kerberoes/hello/world <message>Hello World! User=rareddy@EXAMPLE.COM</message>
References:
http://www.slideshare.net/josef.cacek/dev-conf2013-ltkerberosas7
http://doc.opensuse.org/products/draft/SLES/SLES-security_sd_draft/cha.net.kerberos.html
http://cxf.apache.org/docs/jaxrs-kerberos.html
How to implement Kerberos authentication with Teiid over JDBC
Ramesh..
Comments