Alfresco With mod auth cas

From alfrescowiki

Revision as of 16:01, 12 June 2012 by Alexmadon (Talk | contribs)

Jump to: navigation, search

The following is a recipe for installing the Alfresco Explorer and Share web applications on a Red Hat Enterprise Linux 5.2 server that uses a Central Authentication Service (CAS) server as a Single Sign-On (SSO) solution. The mechanisms used are fairly generic, and thus may work with other SSO systems too. This recipe makes use of the new External authentication subsystem, and thus will only work with a build of the very latest code from HEAD or the upcoming Alfresco v3.2 Enterprise release.

NOTE: CAS 3.3.5 is the only version that has been confirmed to work with Alfrecso with the configuration given below. CAS 3.4.3.1 requires changes to this configuration. The Alfresco end point and filter should be fine if the same request headers are generated.

Step 1: Install Required Packages

We will require an Apache server with mod_ssl installed, as well as the packages required to compile and install other apache modules. We also require MySQL to support Alfresco. So ensure the following packages are installed

  • httpd
  • mod_ssl
  • httpd-devel
  • apr
  • apr-devel
  • apr-util
  • apr-util-devel
  • subversion
  • mysql

Please also ensure that Red Hat's tomcat packages are not installed, as CAS and Alfresco will require their own 'virgin' Tomcat 6 installation later.

Step 2: Install Supporting Tools

Download the Sun JDK 1.6 rpm.bin from http://java.sun.com and run it to install Sun JDK 1.6

Download and extract the following under /opt

  • Apache Maven 2.2
  • Apache Tomcat 6
  • Apache Tomcat 5.5 (only required if you plan to run the virtualization server)
  • Apache Ant 1.6.5

Put this line in /opt/apache-tomcat-6.0.20/conf/catalina.properties to enable the shared classloader, required by Alfresco

shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar

Add in the extension classpath directories that will be required by Alfreso

mkdir -p /opt/apache-tomcat-6.0.20/shared/classes/alfresco/extension
mkdir -p /opt/apache-tomcat-6.0.20/shared/classes/alfresco/web-extension

We are going to be relying on the Apached HTTP Server for authentication rather than tomcat, so in /opt/apache-tomcat-6.0.20/conf/server.xml, disable Tomcat authentication on the AJP 1.3 connector by setting tomcatAuthentication="false" like so:

   <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" tomcatAuthentication="false"/>

Now edit /root/.bashrc to set up your environment appropriately

export JAVA_OPTS="-Xmx1024M -XX:MaxPermSize=128M -Dcom.sun.management.jmxremote -Dalfresco.home=."
export JAVA_HOME=/usr/java/latest
export MVN_HOME=/opt/apache-maven-2.2.1
export ANT_HOME=/opt/apache-ant-1.6.5
export CATALINA_HOME=/opt/apache-tomcat-6.0.20
export TOMCAT_HOME=$CATALINA_HOME
export APP_TOMCAT_HOME=$CATALINA_HOME

# Set the following variable, even if the directory doesn't exist
# This is so that we can use the incremental-tomcat target in the alfresco build
export VIRTUAL_TOMCAT_HOME=/opt/apache-tomcat-5.5.28

export PATH=$JAVA_HOME/bin:$CATALINA_HOME/bin:$MVN_HOME/bin:$ANT_HOME/bin:$PATH

Step 3: Set up a Certificate Authority and Issue Server and Client Certificates

Open the file /etc/pki/tls/openssl.cnf (or /etc/ssl/openssl.cnf on Ubuntu) in a text editor (such as gedit). Edit the "[req_distinguished_name]" section so that it has defaults appropriate for your organization. For example:

countryName_default		= GB
stateOrProvinceName_default	= Berkshire
localityName_default		= Maidenhead
0.organizationName_default	= Alfresco Software Inc.

Now we will create the self-signed certificate for our Certificate Authority (CA). Use the exact paths listed here, as they are already referenced in openssl.cnf. (For Ubuntu replace /etc/pki/CA/... with /etc/ssl/demoCA/... Or check the openssl.cnf file for the dir option.) When asked for the certificate subject, enter the name for your authority, such as "Dave's Certificate Authority".

mkdir /etc/pki/CA/{certs,crl,newcerts,private}
touch /etc/pki/CA/index.txt
echo 01 > /etc/pki/CA/serial
cd /etc/pki/CA/
(umask 077; openssl genrsa -out private/cakey.pem -des3 2048)
openssl req -new -x509 -key private/cakey.pem -days 365 > cacert.pem

Now let's ensure that certificates issued by our CA are trusted by the Apache HTTP Server. We need to create a symbolic link to the certificate using a computed hash to add it to the chain of trust. There's a little bit of extra configuration we make later to complete this.

cp cacert.pem /etc/pki/tls/certs/
cd /etc/pki/tls/certs/
ln -s cacert.pem `openssl x509 -hash -noout -in cacert.pem`.0

While we're here, let's replace the HTTP server's test certificate with a 'real' one issued by our CA. The advantages of it being issued by the same CA are that we'll have fewer certificates to add to Alfresco Share's trust store later on! The certificate subject must match the external DNS name of your server. We use the -nodes option to avoid having to enter a key password every time we start apache!

cd /etc/pki/tls/certs
openssl req -nodes -new -out localhost.csr -keyout ../private/localhost.key
openssl ca -out localhost.crt -infiles localhost.csr
rm localhost.csr

Now let's issue a certificate that we will use to securely identify the Alfresco Share application to the Alfresco repository application. This time we protect the private key with a password and also export the key and its certificate chain to a password protected PKCS12 keystore alfresco-system.p12 in the Tomcat classspath so that it may be used by the Share application. Use the same password for both the key and the keystore. For the subject name, use alfresco-system. We will later configure Alfresco to trust that this subject is the Share application, by using the external authentication handler in the authentication chain. By default Alfresco trusts the CN of alfresco-system as a proxy user (essentially share is proxying authentication to alfresco by using a certificate). (Note that the trusted user can be anything you want, by defining it in the external.authentication.proxyUserName option of alfresco-global.properties file.)

cd /etc/pki/tls/certs
openssl req -new -out alfresco-system.csr -keyout ../private/alfresco-system.key
openssl ca -out alfresco-system.crt -infiles alfresco-system.csr
rm alfresco-system.csr
openssl pkcs12 -export -out /opt/apache-tomcat-6.0.20/shared/classes/alfresco/web-extension/alfresco-system.p12 -in alfresco-system.crt -inkey ../private/alfresco-system.key -chain

The -chain option looks for the cacert.pem file in the default location (defined in openssl.conf). If this does not get picked up properly, you may get "Error unable to get local issuer certificate getting chain.". To get around this you can explicitly set the cacert to use, by using the -certfile option instead of -chain (so -certfile /etc/ssl/certs/cacert.pem, for example).

Step 4: Configure, Build and Install Jasig CAS Server

We are going to use the method described here to build our own pre-configured CAS Server using Maven that integrates with our Enterprise's authentication systems.

Execute the following commands to set yourself up an appropriate directory structure in which to work.

cd /root
mkdir -p custom-cas-server/src/main/webapp/WEB-INF/classes
cd custom-cas-server

First, let's create the Maven Project Object Model (POM) for our customized server. Create a file pom.xml that pulls in the required CAS Server dependencies. We are using CAS 3.3.5, as 3.4 introduced some incompatibilities (namely switching from Spring Webflow 1 to Spring Webflow 2) that prevent the solution described in this page working with the latter. Note that we include LDAP and X509 certificate support:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.alfresco.cas</groupId>
    <artifactId>alfresco-cas</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Alfresco CAS webapp</name>
    <organization>
        <name>Alfresco</name>
        <url>http://www.alfresco.com</url>
    </organization>
    <description>Alfresco's configuration of the JA-SIG CAS server.</description>
    <dependencies>
        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-webapp</artifactId>
            <version>3.3.5</version>
            <type>war</type>
        </dependency>

        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-core</artifactId>
            <version>3.3.5</version>
        </dependency>

        <!-- if you need LDAP handler -->
        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-support-ldap</artifactId>
            <version>3.3.5</version>
        </dependency>

        <!-- if you need X509 handler -->
        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-support-x509</artifactId>
            <version>3.3.5</version>
        </dependency>

      </dependencies>
    <build>
        <finalName>cas</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>RELEASE</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>jasig-repository</id>
            <name>JA-SIG Maven2 Repository</name>
            <url>http://developer.ja-sig.org/maven2</url>
        </repository>
    </repositories>
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-changelog-plugin</artifactId>
            </plugin>
        </plugins>
    </reporting>
</project>

Now, we just need to add in the files we wish to customize. Create the main Spring configuration file in src/main/webapp/WEB-INF/deployerConfigContext.xml

A few points to note

  • I have configured the URL to my company's LDAP directory in the contextSource bean definition, along with the name and password of a user with the rights to search for users in the directory
  • As my LDAP directory is Active Directory, the usernames I bind with take the form of User Principal Names (UPNs). These consist of a username followed by an @ sign followed by the UPN suffix
  • You can find out the format of the UPN suffix by browsing to a user in an LDAP browser and finding their userPrincipalName attribute
  • This means that the userDn property of contextSource plus the filter property of FastBindLdapAuthenticationHandler must use this same UPN format.
  • For other types of LDAP directory, you may need to bind with a full Distinguished Name (DN), in which case userDn should also be a DN and you will have to use BindLdapAuthenticationHandler instead of FastBindLdapAuthenticationHandler in order to be able to resolve a user's DN from their user ID. See this page for more details.
  • I have enabled authentication via SSL certificates with X509CertificateCredentialsToIdentifierPrincipalResolver and X509CredentialsAuthenticationHandler. This will enable the share web application to authenticate itself as a client of alfresco web application in a secure manner. See this page for more details, but do not worry about the Tomcat configuration steps it suggests.
  • In trustedIssuerDnPattern I have included a regular expression that matches the full distinguished name of the Certificate Authority certificate I generated in the previous step. Note that this pattern must match the entire DN, so should begin with ^ and end in $ and use .* for wildcards.
  • I have included an extra x509Check bean, that will be required by the Spring webflow configuration to automatically forward clients with a trusted SSL certificate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

   <!-- Preconfigure our LDAP directory -->
   <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
      <property name="pooled" value="true" />
      <property name="urls">
         <list>
            <value>ldap://ldap.host.com:389/</value>
         </list>
      </property>
      <property name="userDn" value="alfresco@domain" />
      <property name="password" value="secret" />
      <property name="baseEnvironmentProperties">
         <map>
            <entry>
               <key>
                  <value>java.naming.security.authentication</value>
               </key>
               <value>simple</value>
            </entry>
         </map>
      </property>
   </bean>

   <bean id="authenticationManager" class="org.jasig.cas.authentication.AuthenticationManagerImpl">
      <property name="credentialsToPrincipalResolvers">
         <list>
            <bean
               class="org.jasig.cas.adaptors.x509.authentication.principal.X509CertificateCredentialsToIdentifierPrincipalResolver">
               <property name="identifier" value="$CN" />
            </bean>
            <bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" />
            <bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
         </list>
      </property>

      <property name="authenticationHandlers">
         <list>
            <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler">
               <property name="httpClient" ref="httpClient" />
            </bean>
            <bean class="org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler">
               <property name="trustedIssuerDnPattern" value="^.*CN=Dave's Certificate Authority, O=Alfresco Software Inc\., L=Maidenhead, ST=Berkshire, C=GB$" />
            </bean>
            <bean class="org.jasig.cas.adaptors.ldap.FastBindLdapAuthenticationHandler">
               <property name="filter" value="%u@domain" />
               <property name="contextSource" ref="contextSource" />
            </bean>
         </list>
      </property>
   </bean>

   <bean id="userDetailsService" class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
      <property name="userMap">
         <value>

         </value>
      </property>
   </bean>

   <bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao">
      <property name="backingMap">
         <map>
            <entry key="uid" value="uid" />
            <entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
            <entry key="groupMembership" value="groupMembership" />
         </map>
      </property>
   </bean>

   <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl" />

   <!-- Extra x509 bean -->
   <bean id="x509Check" p:centralAuthenticationService-ref="centralAuthenticationService"
      class="org.jasig.cas.adaptors.x509.web.flow.X509CertificateCredentialsNonInteractiveAction">
      <property name="centralAuthenticationService" ref="centralAuthenticationService" />
   </bean>
</beans>

Now, add your logging configuration to src/main/webapp/WEB-INF/classes/log4j.properties

Note we have added one line to the standard file in order to get debugging information from the X509CredentialsAuthenticationHandler

log4j.logger.org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler=DEBUG

For maximum debug information, you may even go as far as changing this line

log4j.logger.org.jasig=INFO

to

log4j.logger.org.jasig=DEBUG

The full file is below

# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
log4j.rootLogger=ERROR, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=cas.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

# WARNING: Setting the org.springframework logger to DEBUG displays debug information about
# the request parameter values being bound to the command objects.  This could expose your
# password in the log file.  If you are sharing your log files, it is recommend you selectively
# apply DEBUG level logging on a an org.springframework.* package level (i.e. org.springframework.dao)
log4j.logger.org.springframework=WARN
#log4j.logger.org.springframework.web.servlet.i18n=DEBUG
#log4j.logger.org.springframework.web.servlet.view=DEBUG
#log4j.logger.org.quartz=DEBUG

log4j.logger.org.jasig=INFO
# WARNING: Setting the flow package to DEBUG will display
# the parameters posted to the login servlet including
# cleartext authentication credentials
log4j.logger.org.jasig.cas.web.flow=INFO
#log4j.logger.org.jasig.cas.authentication=DEBUG
#log4j.logger.org.jasig.cas.web.flow.TicketGrantingTicketCheckAction=DEBUG
#log4j.logger.org.jasig.cas.services.DefaultServiceRegistry=DEBUG
#log4j.logger.org.jasig.cas.services=DEBUG 
log4j.logger.org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler=DEBUG

Finally, create a file src/main/webapp/WEB-INF/login-webflow.xml with the customizations described here to ensure auto-redirect through the login screen with the client certificate we exported to alfresco-system.p12. It should be fine to copy the version below without making any edits.

<?xml version="1.0" encoding="UTF-8"?>
    <flow xmlns="http://www.springframework.org/schema/webflow"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="
              http://www.springframework.org/schema/webflow
              http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">

	<start-state idref="initialFlowSetup"/>

	<action-state id="initialFlowSetup">
		<action bean="initialFlowSetupAction" />
		<transition on="success" to="ticketGrantingTicketExistsCheck" />
	</action-state>
	
	<decision-state id="ticketGrantingTicketExistsCheck">
		<if test="${flowScope.ticketGrantingTicketId != null}" then="hasServiceCheck" else="gatewayRequestCheck" />
	</decision-state>
    
	<decision-state id="gatewayRequestCheck">
		<if test="${externalContext.requestParameterMap['gateway'] != '' &amp;&amp; externalContext.requestParameterMap['gateway'] != null &amp;&amp; flowScope.service != null}" then="redirect" else="startAuthenticate" />
	</decision-state>
	
	<decision-state id="hasServiceCheck">
		<if test="${flowScope.service != null}" then="renewRequestCheck" else="viewGenericLoginSuccess" />
	</decision-state>
	
	<decision-state id="renewRequestCheck">
		<if test="${externalContext.requestParameterMap['renew'] != '' &amp;&amp; externalContext.requestParameterMap['renew'] != null}" then="startAuthenticate" else="generateServiceTicket" />
	</decision-state>
	
	<!-- 
		The "warn" action makes the determination of whether to redirect directly to the requested
		service or display the "confirmation" page to go back to the server.
	-->
	<decision-state id="warn">
		<if test="${flowScope.warnCookieValue}" then="showWarningView" else="redirect" />
	</decision-state>
	
	<action-state id="startAuthenticate">
		<action bean="x509Check" />
		<transition on="success" to="sendTicketGrantingTicket" />
		<transition on="error" to="viewLoginForm" />
	</action-state>
   
	<view-state id="viewLoginForm" view="casLoginView">
			<render-actions>
			<action bean="authenticationViaFormAction" method="setupForm"/>
			<action bean="authenticationViaFormAction" method="referenceData"/>
		</render-actions>
		<transition on="submit" to="bindAndValidate" />
	</view-state>
	
	<action-state id="bindAndValidate">
		<action bean="authenticationViaFormAction" />
		<transition on="success" to="submit" />
		<transition on="error" to="viewLoginForm" />
	</action-state>
	
	<action-state id="submit">
		<action bean="authenticationViaFormAction" method="submit" />
		<transition on="warn" to="warn" />
		<transition on="success" to="sendTicketGrantingTicket" />
		<transition on="error" to="viewLoginForm" />
	</action-state>
	
	<action-state id="sendTicketGrantingTicket">
		<action bean="sendTicketGrantingTicketAction" />
		<transition on="success" to="serviceCheck" />
	</action-state>

	<decision-state id="serviceCheck">
		<if test="${flowScope.service != null}" then="generateServiceTicket" else="viewGenericLoginSuccess" />
	</decision-state>
	
	<action-state id="generateServiceTicket">
		<action bean="generateServiceTicketAction" />
		<transition on="success" to ="warn" />
		<transition on="error" to="viewLoginForm" />
		<transition on="gateway" to="redirect" />
	</action-state>

	<!-- 
		the "viewGenericLogin" is the end state for when a user attempts to login without coming directly from a service.
		They have only initialized their single-sign on session.
	-->
	<end-state id="viewGenericLoginSuccess" view="casLoginGenericSuccessView" />

	<!-- 
		The "showWarningView" end state is the end state for when the user has requested privacy settings (to be "warned") to be turned on.  It delegates to a 
		view defines in default_views.properties that display the "Please click here to go to the service." message.
	-->
	<end-state id="showWarningView" view="casLoginConfirmView" />

	<!-- 
		The "redirect" end state allows CAS to properly end the workflow while still redirecting
		the user back to the service required.
	-->
	<end-state id="redirect" view="bean:dynamicRedirectViewSelector" />
	
	<end-state id="viewServiceErrorView" view="viewServiceErrorView" />
    
    <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" />

	<global-transitions>
		<transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" />
        <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" />
		<transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" />
	</global-transitions>
</flow>

We're now ready to build our configured version of the CAS war file and plug it into Tomcat:

cd /root/custom-cas-server
mvn -Dmaven.test.skip=true package install
cp target/cas.war /opt/apache-tomcat-6.0.20/webapps/

Step 5: Build, Install and Configure Apache Modules

Ensure Apache HTTP Server is not running

/etc/init.d/httpd stop

Download and extract the following under /root

Now build and install with the following commands

cd /root/tomcat-connectors-1.2.28-src/native
./configure --with-apxs=/usr/sbin/apxs; make; make install
mkdir /opt/apache-tomcat-6.0.20/conf/jk/
cp ../conf/workers.properties /opt/apache-tomcat-6.0.20/conf/jk/

Use subversion to check out the latest mod_auth_cas source (note we require the latest code from trunk - v1.0.8 will not work).

cd /root
svn co https://source.jasig.org/cas-clients/mod_auth_cas/trunk mod_auth_cas_trunk
cd mod_auth_cas_trunk
./configure; make; make install

NOTE: It might be a better idea to check out a release version, one that is tagged, as trunk might not be in a consistent way or might not even compile. For example tag "1.0.9.1" in " https://source.jasig.org/cas-clients/mod_auth_cas/tags/mod_auth_cas-1.0.9.1/ " seems to work well.

We create a directory for mod_auth_cas to store cookie data. Note that this must be writeable by the apache user.

mkdir /tmp/cas
chown apache:apache /tmp/cas
chmod 0700 /tmp/cas

Now let's configure our modules.

In /etc/httpd/conf/httpd.conf make sure that ServerName is set to the full DNS name and port number through which your Apache HTTP Server will be reached by the outside world. This is important, because mod_auth_cas embeds this name into the URLs it generates.

ServerName your.server.com:80

Configure mod_jk by creating the file /etc/httpd/conf.d/mod_jk.conf with the following contents. This will mount the alfresco, share and examples applications on port 80 of the HTTP server. We mount the examples application, as it is very useful for diagnostics, as we shall see later.

<IfModule !mod_jk.c>
  LoadModule jk_module "/usr/lib/httpd/modules/mod_jk.so"
</IfModule>

JkWorkersFile "/opt/apache-tomcat-6.0.20/conf/jk/workers.properties"
JkLogFile "/opt/apache-tomcat-6.0.20/logs/mod_jk.log"

JkLogLevel emerg

JkMount /alfresco ajp13
JkMount /alfresco/* ajp13

JkMount /share ajp13
JkMount /share/* ajp13

JkMount /examples ajp13
JkMount /examples/* ajp13

Now configure mod_ssl by editing /etc/httpd/conf.d/ssl.conf by adding the following lines near the end, just before the closing </VirtualHost> tag in order to mount the cas and examples applications on the HTTPS port. Note that we also switch on client certificate verification and set the SSLCACertificatePath so that our CA certificate will be trusted by Apache. We set the SSLOptions necessary to ensure that Apache will forward client certificate information to Tomcat.

JkMount /cas ajp13
JkMount /cas/* ajp13

JkMount /examples ajp13
JkMount /examples/* ajp13

SSLVerifyClient optional
SSLCACertificatePath /etc/pki/tls/certs/
SSLOptions +StdEnvVars +ExportCertData

Finally, configure mod_auth_cas by creating a file /etc/httpd/conf.d/mod_auth_cas.conf with the following contents. This will ensure the alfresco, share and examples applications are protected by CAS. Remember to edit the host name in CASLoginURL and CASValidateURL appropriately.

LoadModule auth_cas_module modules/mod_auth_cas.so

CASCookiePath /tmp/cas/
CASLoginURL https://your.server.com/cas/login
CASValidateURL https://your.server.com/cas/serviceValidate
CASValidateServer Off
CASDebug On

<LocationMatch ^/alfresco/(?!service/|service$|webdav/|webdav$|s/|s$|scripts/|css/|images/).*>
AuthType CAS
AuthName "CAS"
require valid-user
CASScope /alfresco
</LocationMatch>

<Location /share>
AuthType CAS
AuthName "CAS"
require valid-user
CASScope /share
</Location>

<Location /examples>
AuthType CAS
AuthName "CAS"
require valid-user
CASScope /examples
</Location>

Step 6: Build, Install and Configure Alfresco Explorer and Share

This will get the very latest Alfresco code and build and install the alfresco and share applications in to Tomcat.

cd /root
svn co http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD/ alfresco_head
cd alfresco_head/root
ant incremental-tomcat


Now let's configure the alfresco app. Create a file /opt/apache-tomcat-6.0.20/shared/classes/alfresco-global.properties with the following lines. Note that we are using the new External authentication subsystem in our authentication chain, which will use the username passed by mod_auth_cas as request.getRemoteUser() as the Alfresco user.

dir.root=/opt/alfresco/alf_data
authentication.chain=external1:external

Make a root directory for the Alfresco application

mkdir -p /opt/alfresco/alf_data

Create a database for the Alfresco application by issuing the following commands to mysql:

drop database if exists alfresco;
create database alfresco default character set utf8 collate utf8_bin;
grant all on alfresco.* to alfresco identified by 'alfresco';

To configure the share app (See Alfresco_Share_SSO_using_NTLM or Alfresco_Share_using_external_SSO)

on 3.2:

cp projects/slingshot/config/alfresco/web-extension/webscript-framework-config-custom.xml.sample $CATALINA_HOME/shared/classes/alfresco/web-extension/webscript-framework-config-custom.xml

on 3.3:

tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml.sample


Now edit on 3.2:

/opt/apache-tomcat-6.0.20/shared/classes/alfresco/web-extension/webscript-framework-config-custom.xml 

or on 3.3:

tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml

and uncomment this section. Edit endpoint-url so that it points to the public address of the alfresco web app, through the Apache HTTP server.

   <!-- Overriding endpoints to reference an Alfresco server with external SSO or NTLM enabled -->
   <!-- NOTE: For NTLM, the NTLM Authentication Filter must also be enabled in share web.xml -->
   <!-- NOTE: if utilising a load balancer between web-tier and repository cluster, the "sticky -->
   <!--       sessions" feature of your load balancer must be used -->
   <config evaluator="string-compare" condition="Remote">
        <remote>
            <!-- SSL client certificate + trusted CAs. Optionally used to authenticate share to an external SSO system such as CAS -->
            <keystore>
                <path>alfresco/web-extension/alfresco-system.p12</path>
                <type>pkcs12</type>
                <password>alfresco-system</password>
            </keystore>
         
            <connector>
                <id>alfrescoCookie</id>
                <name>Alfresco Connector</name>
                <description>Connects to an Alfresco instance using cookie-based authentication</description>
                <class>org.alfresco.connector.AlfrescoConnector</class>
            </connector>

            <endpoint>
                <id>alfresco</id>
                <name>Alfresco - user access</name>
                <description>Access to Alfresco Repository WebScripts that require user authentication</description>
                <connector-id>alfrescoCookie</connector-id>
                <endpoint-url>http://your.server.com/alfresco/wcs</endpoint-url>
                <identity>user</identity>
                <external-auth>true</external-auth>
            </endpoint>
            
        </remote>
    </config>

Security concerns

Keep in mind that, as stated in this forum post, activating external authentication makes Alfresco accept external authentication tokens, so you must make sure that no other client than the authorized Apache HTTP server can access it through HTTP or AJP.

I guess binding the AJP connector to localhost and disabling HTTP tomcat connectors should be enough.

Step 7: Test It Out

First, start up the servers and check for errors in their logs.

catalina.sh start
/etc/init.d/httpd start

Now let's make sure CAS is authenticating us properly.

Browse to http://your.host.com/examples/jsp/snp/snoop.jsp

You should be redirected to the CAS login page. When you enter a valid LDAP username and password, you should be redirected back to the 'Snoop' JSP sample.

It should display, among other attributes, the name of the user you logged in as in the "Remote User" field.

You might also want to ensure that CAS is also authenticating Alfresco Share's client certificate correctly. To do this, install alfresco-system.p12 into your browser, delete all your cookies and then navigate to snoop.jsp again. This time, you should see alfresco-system as the "Remote User" without even being prompted for a username and password.

If this is all working then you are ready to try out the Alfresco Explorer and Share applications at

http://your.host.com/alfresco and http://your.host.com/share

Remember to remove the alfresco-system certificate from your browser before you do so

If not, then carefully check all Apache HTTP Server and Tomcat log files, and set DEBUG logging in your CAS log4j.properties if necessary.

Using Public vs Self-Signed Certificates

Troubleshooting

Ubuntu Specific Issues

The following were some experiences setting this up on Ubuntu 9.04, and may help diagnose problems.

There are a number of potential issues that can come up if things are not followed absolutely correctly for your platform. For instance, creating the SSL certificates and the PKCS12 keystore on Ubuntu is probably done better by following the instructions at the Ubuntu OpenSSL Community Page or using CA.pl (details below).

Setting up your CA and certificates properly is, of course, crucial to having this method work. This is easy to tell in Step 7:

  • Import alfresco-system.p12 into your browser. In Firefox 3.x, it's in Preferences->Advanced->Encryption Tab->View Certificates->Your Certificates Tab->Import...
  • Clear your cookies and navigate to the snoop.jsp example.
  • You should get a "User Identification Request" dialog, and a prompt to "Choose a certificate to present as identification".
  • If you do NOT get this prompt, there's something wrong in your certificate creation.
  • I found that the CA.pl script (which ships with OpenSSL) did all the work for me correctly - that way I avoided any typos or missed steps. The following can be copied to a script and run. Note that the default openssl.cnf file on Ubuntu 9.04 uses ./demoCA as the directory into which it stores stuff. YMMV depending on your distribution, so check the /etc/ssl/openssl.cnf file to ensure that paths are correct.
mkdir ~/newCA
cd ~/newCA
cp /usr/lib/ssl/misc/CA.pl .
chmod +x CA.pl
unset OPENSSL_CONF  # so that we use the default /etc/ssl/openssl.cnf file
# The Subject/Common-Name here should be your authority name. Eg. My Company Certificate Authority
./CA.pl -newca    # creates cacert.pem and cakey.pem

# The Subject/Common-Name here MUST be the host's external DNS name. Eg. my-host.myco.com
./CA.pl -newreq-nodes   # creates the server request, with the -nodes option so no apache password prompt
./CA.pl -signreq  # creates a signed certificate with the cacert created earlier
cp demoCA/cacert.pem /etc/ssl/certs/
cp demoCA/private/cakey.pem /etc/ssl/private/
mv newcert.pem /etc/ssl/certs/localhost.pem
mv newkey.pem /etc/ssl/private/localhost.key

# The Subject/Common-Name here MUST be alfresco-system
./CA.pl -newreq   # create the alfresco certificate request
./CA.pl -signreq  # create signed certificate
./CA.pl -pkcs12 "alfresco-system" # create the alfresco-system keystore/certificate.
                                  # Make sure that the export password is set and it's
                                  # the same as what you set in your webscript-framework-config-custom.xml
 mv newcert.pem /etc/ssl/certs/alfresco-system.crt
 mv newkey.pem  /etc/ssl/private/alfresco-system.key
 mv newcert.p12 <alfresco-path>/tomcat/shared/classes/alfresco/web-extension/alfresco-system.p12
  • Note that the PKCS12 certificate MUST have an export password. This is the password set in webscript-framework-config-custom.xml (if you set a blank password and try to use a blank <password> tag, things will fail).
  • If you get an error an "ArrayIndexOutOfBoundsException" error, this may mean that the certificate's DN parameters are incorrect. You must NOT have any commas in the Organization or Organization Unit fields.

Setting up Apache correctly is the other very important thing. Here are some instructions using the a2enmod scripts, and the associated configuration files.

  • Set the ssl-enabled virtual host (by default the default-ssl in sites-available) to have the following:
SSLCACertificateFile /etc/ssl/certs/cacert.pem
SSLCertificateFile /etc/ssl/certs/localhost.pem
SSLCertificateKey  /etc/ssl/private/localhost.key
# The SSL-enabled ajp worker nodes. casnode is the ajp13 cas-worker-node, defined in workers.properties
JkMount /cas*      casnode
JkMount /examples* casnode
  • Enable the ssl site using a2ensite default-ssl (or whatever your ssl-enabled vhost file is)
  • Enable the modules using a2enmod auth_cas jk ssl
  • The CAS-related JkMounts should be in the vhosts definition (default-ssl), and NOT in the jk.conf file. This seems to be because of the order in which configuration files and modules are loaded. The ssl.conf is loaded after jk.conf, and the sites-enabled/* is loaded last. Things don't work (you'll get a Not Found error) if the JkMount's for the ssl-enabled workers are defined outside the vhosts definition (like in ssl.conf).
  • ssl.conf should have:
SSLVerifyClient optional
SSLCACertificateFile /etc/ssl/certs/cacert.pem
# Keeping SSLCACertificatePath causes the browser to go into a loading loop,
# so use the above SSLCACertificateFile instead
# SSLCACertificatePath /etc/ssl/certs
SSLOptions +StdEnvVars +ExportCertData
  • jk.conf should have
JkWorkersFile "/etc/apache2/workers.properties" # Or whatever your path is
JkLogFile "/var/log/apache2/mod_jk.log"
JkOptions +ForwardKeySize -ForwardDirectories
JkLogLevel info # or debug, emerg, error

# Your non-SSL mounts can be here
# alfnode is the ajp13 worker node for alfresco, and must be defined in workers.properties
JkMount /alfresco* alfnode
JkMount /share*    alfnode
JkMount /examples* alfnode
  • auth_cas.conf is as defined is section 5 above

(Note that if this is done right everything works. You do NOT need JkMountCopy On anywhere.)

Also note that if you specify the SSLCACertificatePath option for mod_ssl.conf, you may find that when you navigate to your https site the browser just keeps loading. The solution is to add SSLCACertificateFile /etc/ssl/certs/cacert.pem instead as described above.


Fedoras Core 14 install notes - Dec 2010

Validated with 3.4 R 24232, Java 1.6.0_u21, Tomcat 6.0.29, Mysql 5.1 and optoins above

  • VMWare
    • access to FC14 hosted OS requires Windows hack for matching NAT to shared network in windows host
    • Fixed IP with name server support by adding to windows host file (you may want to do this correctly!)
  • disable SELinux (avoid file access and creation restrictions for httpd extensions)
  • domain name required (I used .local for auto discovery)
  • On 3.4 community you will need to change the name for or remove the "Kerberos" condition in share-config-custom.xml.
    • (If present share will try and use Kerberos in the Remote end point)
  • lots of dependencies to install
  • When generating tickets subject means CN :-)
  • Fixed the jk workers pool to the most basic one possible (seemed to generate errors looking for template definitoins - but this was before I took out SELinux rather then working "with" it)
  • Remember to change the trusted CN in the CAS config
  • the cas repo has moved

Any isues with Alfrecso or share not getting the correct user name - use the snoop.jsp and check the details as advised above.

Share wiring strategies

There are mainly two possible strategies to wire Share to Alfresco. In this wiki, the first strategy is presented. We introduce below the two strategies and their pros and cons.

Strategy 1: apache - share - apache - alfresco (Indirect)

External apache.png

The Share data flow indicated in blue goes through Apache twice:

  1. HTTP connection to the fornt end, then authentication
  2. if authentication is OK, then pass to Share using AJP
  3. The Share <endpoint-url>http://your.server.com/alfresco/wcs</endpoint-url> send back to apache, using HTTP+ a user header (defined by external.authentication.proxyHeader) + a Cert.
  4. Apache uses the certificate to check that the request is indeed coming from Share with the correct user (i.e the value of external.authentication.proxyUserName which is also the value of the cert user) and forwards to Alfresco

Strategy 2: apache - share -alfresco (Direct)

External direct.png

The Share data flow indicated in blue goes through Apache only once:

  1. HTTP connection to the front end, then authentication
  2. if authentication is OK, then pass to Share using AJP
  3. The Share <endpoint-url>http://localhost:8080/alfresco/wcs</endpoint-url> sends directly to the Alfresco layer using HTTP+a user header. In that case no Cert is used and the external.authentication.proxyUserName is blank. Alfresco trusts the header (defined by external.authentication.proxyHeader) sent by Share.

Comparison of the two strategies

The direct strategy is easier to set up and have one network hop less than the indirectstrategy (and that hop involves encryption using certs). It should in theory thus be a bit faster than the indirect strategy. However, to use the direct strategy you need to make sure that yoru alfresco server is well locked down, i.e. that no direct connection to port 8080 are possible. In other words, you have to enforce that all connections have to go through apache. This can be achieved easily with a firewall rule (like iptables on Linux). If the setup is not well locked, then a malicious user could log as any user just forging the auth header with the user he wants to log in as.

Personal tools
© 2014 Alfresco Software, Inc. All Rights Reserved. Legal | Privacy | Accessibility