Friday, July 20, 2012

Integrating FreeIPA and Alfresco...

After describing how to install CentOS, DNS and reverse DNS, FreeIPA and Alfresco, in this post I'm going to describe how to integrate Alfresco with FreeIPA. I want to achieve the following goals with the integration:
  • Users and groups are kept within FreeIPA and authentication is done by FreeIPA.
  • Alfresco Web interface honors Kerberos tickets. Upon opening Web interface users are immediately presented with their pages withoug necessity for authentication (if, of course, they have valid Kerberos tickets).
  • Authentication when mounting DAV share is also done via Kerberos tickets.
In short, I want to achieve SSO (Single Sign-On) as much as possible. Users sign in when they start to use their workstations once, that's the only time they have to enter password.


Preparation step


In case you don't have any users in FreeIPA it is a good idea to create at least one so that you can test integration process.

Also, alfresco host has to join Kerberos domain. This is done using ipa-client-install tool from ipa-client package. So, install ipa-client using yum and run the tool. This is the transcript for my case:
# ipa-client-install
DNS domain 'example-domain.com' is not configured for automatic KDC address lookup.
KDC address will be set to fixed value.

Discovery was successful!
Hostname: alfresco.example-domain.local
Realm: EXAMPLE-DOMAIN.COM
DNS Domain: example-domain.local
IPA Server: ipa.example-domain.local
BaseDN: dc=example-domain,dc=com


Continue to configure the system with these values? [no]: yes
User authorized to enroll computers: admin
Synchronizing time with KDC...
Password for admin@EXAMPLE-DOMAIN.COM:

Enrolled in IPA realm EXAMPLE-DOMAIN.COM
Created /etc/ipa/default.conf
Configured /etc/sssd/sssd.conf
Configured /etc/krb5.conf for IPA realm EXAMPLE-DOMAIN.COM
SSSD enabled
NTP enabled
Client configuration complete.
As you can see, the tool automatically figured out all the parameters using DNS. In case you DNS isn't configured you'll have to provide all this information manually.

Integrating with LDAP


First, we are going to connect Alfresco to LDAP. Basically, LDAP will be used for storing user data.

Go to the directory $WEBAPPS/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/ldap. Open file ldap-authentication.properties in text editor and change the following lines:
  • ldap.authentication.active should be set to false, i.e. we are not using LDAP for authentication!
  • Set ldap.authentication.userNameFormat to uid=%s,cn=users,cn=accounts,dc=example-domain,dc=com
  • ldap.authentication.java.naming.provider.url should be set to point to your FreeIPA instance. In our case that should be ldap://ipa.example-domain.local:389
  • Set default principal for user and group synchronization to Directory Manager, i.e. set ldap.synchronization.java.naming.security.principal should be set to cn\=Directory\ Manager (note backslashes!).
  • Define password of the user for synchronization, i.e. set value of ldap.synchronization.java.naming.security.credentials to whatever password you defined for the user.
  • Change base DN of users, i.e. set ldap.synchronization.userSearchBase to cn\=users,cn\=accounts,dc\=example-domain,dc\=com. Again, note backslashes.
  • Change base DN of groups, i.e. set ldap.synchronization.groupSearchBase to cn\=groups,cn\=accounts,dc\=example-domain,dc\=com
Two things to note. The first one is security related! Namely you should not (ab)use Directory Manager to connect to directory server in production environment, i.e. you should create a separate user for that role. Next thing to note is that base DN in LDAP mirrors Kerberos domain, i.e. EXAMPLE-DOMAIN.COM and not DNS domain (example-domain.local).

Now, open file $WEBAPPS/ alfresco/WEB-INF/classes/alfresco-global.properties and find the following line:
#authentication.chain=alfrescoNtlm1:alfrescoNtlm
it is commented out, and shown is default value (i.e. the value that will be assumed if variable isn't defined). So, remove comment mark (hash) and modify line to read like this:
authentication.chain=alfrescoNtlm1:alfrescoNtlm,ldap1:ldap
What you are basically saying is that first local authentication should be tried and then LDAP. Later we will add kerberos for authentication.

Now, start Alfresco and look into log file. There should be no errors related to user synchronization. For additional check, go to the Alfresco Web console (as user admin) and get user list. In the user list should appear users pulled from FreeIPA. Also, in the log file you should see the lines about successful LDAP synchronization.

Ok, now stop tomcat again and let's proceed to Kerberos integration.

Integrating with Kerberos


First, we need to add HTTP service principal and retrieve its keytab. To do that first you need to create principal, so go to IPA administration console and then select Services tab. You'll see there predefined services for the ipa host itself. Click on Add button and fill in the fields so that service's type and name is HTTP and for host name select host on which alfresco is running (i.e. alfresco.example-domain.local).

Now, go to Alfresco host and first obtain admin ticket:
# kinit admin
Password for admin@EXAMPLE-DOMAIN.COM: <type your admin password>
Then, obtain ticket for Alfresco HTTP service using the following command:
# ipa-getkeytab -k /etc/alfrescohttp.keytab -p HTTP/alfresco.example-domain.local@EXAMPLE-DOMAIN.COM -s -P
New Principal Password: <type password>
Verify Principal Password:
<retype password>
ipa.example-domain.localKeytab successfully retrieved and stored in: /etc/alfrescohttp.keytab
Again, open the file $WEBAPPS/alfresco/WEB-INF/classes/alfresco-global.properties and modify the variable authentication.chain to have the following value:
 authentication.chain=alfrescoNtlm1:alfrescoNtlm,krb:kerberos,ldap1:ldap
we are basically adding Kerberos into authentication chain. Now, go to directory $WEBAPPS/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/kerberos and open file kerberos-authentication.properties in text editor. In the file, only one line is important and that is authenticateCIFS which should be set to false:
kerberos.authentication.realm=EXAMPLE.COM
kerberos.authentication.user.configEntryName=Alfresco
kerberos.authentication.defaultAdministratorUserNames=
kerberos.authentication.cifs.configEntryName=AlfrescoCIFS
kerberos.authentication.cifs.password=secret
kerberos.authentication.authenticateCIFS=false
Note that there is also variable kerberos.authentication.http.password and in case you protected alfresco's http ticket with pasword you'll have to use that variable to tell alfresco which password to use to unlock ticket.

Now, open the file  kerberos-filter.properties that is in the same directory as the kerberos-authentication.properties file. In this file set the value of property kerberos.authentication.http.password to the password you entered when you've run ipa-getkeytab command.

Now, go to the directory /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/security/. Note that if you are using other version of Java you should change directory name to reflect the Java version. In that directory create file named java.login.config with the following content:
Alfresco {
   com.sun.security.auth.module.Krb5LoginModule sufficient;
};
AlfrescoCIFS {
   com.sun.security.auth.module.Krb5LoginModule required
   storeKey=true
   useKeyTab=true
   keyTab="/etc/alfrescocifs.keytab"
   principal="cifs/.";
};
AlfrescoHTTP {
   com.sun.security.auth.module.Krb5LoginModule required
   storeKey=true
   useKeyTab=true
   keyTab="/etc/alfrescohttp.keytab"
   principal="HTTP/alfresco.example-domain.local@EXAMPLE-DOMAIN.LOCAL";
};
com.sun.net.ssl.client {
   com.sun.security.auth.module.Krb5LoginModule sufficient;
};
other {
   com.sun.security.auth.module.Krb5LoginModule sufficient;
};
In the same directory in which you created java.login.config you'll find file java.security. Open it and find the following line:
#login.config.url.1=file:${user.home}/.java.login.config
Uncomment it and change it, or add another one, that reads like this:
login.config.url.1=file:${java.home}/lib/security/java.login.config
Save and close the file.

Now to test if everything is working as expected...

Testing kerberos authentication


To test if kerberos authentication is working you need properly configured Firefox browser and a computer on which you can request tickets (via kinit command).

To be able to request Kerberos tickets use ipa-client-install command on some workstation. This will configure DNS resolution and /etc/krb5.conf file. Alternatively, if you don't want or can not run ipa-client-install, you can copy /etc/krb5.conf from Alfresco to some computer and change /etc/resolv.conf to point to IPA server. Then issue ticket for, e.g. admin@EXAMPLE-DOMAIN.COM principal. To have Firefox browser properly configured go to IPA Web UI (see my previous post about IPA installation for details). This will configure Firefox to handle Kerberos tickets.

Finally, in the Firefox browser enter Alfresco's URL, i.e. https://alfresco.example-domain.local/alfresco/. What should happen is that you should be automatically logged in as admin user (or whichever user's ticket you obtained).

As an additional test you can do the following. First, select Logout in Alfresco's Web console. You are now presented with the option to Re-login. If you click that link you'll be logged in again. But, in the command line run kdestroy which will remove your ticket. Click on Re-login now! What should happen is that you are presented with a Alfresco's Login screen.

Error messages


Here are error messages I had to resolve why trying to configure Alfresco and FreeIPA, in case you stumble on them to know what's wrong.

07:53:32,266 ERROR [org.alfresco.fileserver] CIFS server configuration error, Error creating bean with name 'authenticationComponent' defined in file [/var/lib/tomcat6/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/ldap/../common-ldap-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'active'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [${ldap.authentication.active}]

This is CIFS server complaining that no authentication is defined. But, since I don't want CIFS, I simply disabled it by editing file file-servers.properties in directory $WEBAPPS/alfresco/WEB-INF/classes/alfresco/subsystems/fileServers/default.


org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationComponent' defined in file [/var/lib/tomcat6/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/ldap/../common-ldap-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'active'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [${ldap.authentication.active}]

This one was real pain to discover what's wrong. Namely, the value was set to false, which is (you guessed it) boolean!? But Alfresco claims it's not. What happened actually is that I moved properties file in another directory and Alfresco couldn't find it. So, variables were not initialized and obviously they were left as is instead of being replaced with values, in this case false.

20:16:23,243 ERROR [org.alfresco.web.app.servlet.KerberosAuthenticationFilter] HTTP Kerberos web filter error
javax.security.auth.login.LoginException: Integrity check on decrypted field failed (31) - PREAUTH_FAILED
    at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:713)
    at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:559)
For this message question on Stackoverflow was the first info I managed to find. Then, I stumbled on this post, but it didn't help either. The latest info about this is this post. After I published initial version of this post I realized that I was actually configuring CIFS instead of HTTP. In other words, I had to disable CIFS and properly configure HTTP. The text is updated now.

In case you see the previous entry in the log file than one of the reasons could be that keytab is encrypted but you (that is application) is trying to decrypt it using a wrong key/passpharse.

3 comments:

Unknown said...

quick question... where you indicate the creation of the "java.login.config" do you need to change "principal="HTTP/alfresco.example-domain.local@EXAMPLE-DOMAIN.LOCAL";" with what our actual entry is that we created when we generated the keytab or is this line referencing something else?

Stjepan Groš (sgros) said...

Yes, that entry is specific to Alfresco, and thus it has to be changed.

Unknown said...

thank you so much for you quick turn around. I have one last hurdle (i hope) after following your kerberos config and making changes in the java.security and the java.login.config i get the following error when i http connect to /alfresco or when i tail the logs when trying to auth with an existing IPA user from /share

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalAuthenticationFilter' defined in file [/opt/alfresco-4.0.d/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/kerberos/kerberos-filter-context.xml]: Invocation of init method failed; nested exception is java.lang.SecurityException: Unable to locate a login configuration
caused by:
java.lang.SecurityException: Unable to locate a login configuration
caused by:
java.io.IOException: Unable to locate a login configuration

what did I do wrong?

About Me

scientist, consultant, security specialist, networking guy, system administrator, philosopher ;)

Blog Archive