CASifying Zimbra : an update documentation

Discuss your pilot or production implementation with other Zimbra admins or our engineers.
User avatar
DualBoot
Elite member
Elite member
Posts: 1326
Joined: Mon Apr 18, 2016 8:18 pm
Location: France - Earth
ZCS/ZD Version: ZCS FLOSS - 8.8.15 Mutli servers
Contact:

CASifying Zimbra : an update documentation

Post by DualBoot »

Hello the community,

Longtime I wanted to share SSO - CAS up to date documentation :

Zimbra - CAS Client
Works with ZCS 8.6, 8.7 RC
Important
* The following modifications are not preserved from Zimbra upgrade/update.
* multi servers installation case : each modification or command must have to be passed where the zimbra mailboxd is enabled.
* to ease the installation the domain you want to CASify must have a virtual hostname set in Zimbra
* most common problems are from ntpd which are not synchro, CAS self-signed certificate (see at the end), new dependancy needs with CAS client core, new filter need in the zimbra.web.xml.in

Java Client Installation

Put into ''/opt/zimbra/jetty/common/lib'' folder the following jar files :
* cas-client-core-3.4.1.jar
* slf4j-api-1.7.21.jar

zimbra.web.xml.in modifications

Add before the following piece of code :

Code: Select all

  <error-page>
    <error-code>404</error-code>
    <location>/public/error.jsp</location>
  </error-page>
The whole code :

Code: Select all

<!-- CAS filter start -->

  <filter>
    <filter-name>CasSingleSignOutFilter</filter-name>
    <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    <init-param>
        <param-name>casServerUrlPrefix</param-name>
        <param-value>https://cas.domain.tld:443/cas</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CasSingleSignOutFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
 
  <listener>
    <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
  </listener>
 
  <filter>
    <filter-name>CasAuthenticationFilter</filter-name>
    <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
    <init-param>
        <param-name>casServerLoginUrl</param-name>
        <param-value>https://cas.domain.tld:443/cas/login</param-value>
    </init-param>
    <init-param>
        <param-name>serverName</param-name>
        <param-value>https://mail.domain.tld:443</param-value>
    </init-param>
  </filter>
    
  <filter-mapping>
    <filter-name>CasAuthenticationFilter</filter-name>
    <url-pattern>/public/preauth.jsp</url-pattern>
  </filter-mapping>

  
  <filter>
    <filter-name>CasValidationFilter</filter-name>
    <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
    <init-param>
        <param-name>casServerUrlPrefix</param-name>
        <param-value>https://cas.domain.tld:443/cas</param-value>
    </init-param>
    <init-param>
        <param-name>serverName</param-name>
        <param-value>https://mail.domain.tld:443</param-value>
    </init-param>
    <init-param>
        <param-name>redirectAfterValidation</param-name>
        <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CasValidationFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>CasHttpServletRequestWrapperFilter</filter-name>
    <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>CasHttpServletRequestWrapperFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

<!-- CAS filter end -->
Setting the pre-authentication page

Put in the ''/opt/zimbra/jetty/webapps/zimbra/public'' folder the following jsp file :

Code: Select all

<%@ page import="java.security.InvalidKeyException" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>
<%@ page import="java.security.SecureRandom" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeSet" %>
<%@ page import="javax.crypto.Mac" %>
<%@ page import="javax.crypto.SecretKey" %>
<%!
 public static final String DOMAIN_KEY =
        "b8d15fdfe2a7890d37c7708026c388e0648b0d716c28b0beb5d8316caf95686b"; // generate by the foolowing command : zmprov gdpak domain.tld


 public static String generateRedirect(HttpServletRequest request, String name) {
     HashMap params = new HashMap();
     String ts = System.currentTimeMillis()+"";
     params.put("account", name);
     params.put("by", "name"); // needs to be part of hmac
     params.put("timestamp", ts);
     params.put("expires", "0"); // means use the default

     String preAuth = computePreAuth(params, DOMAIN_KEY);
     return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+"/service/preauth/?" +
           "account="+name+
           "&by=name"+
           "&timestamp="+ts+
           "&expires=0"+
           "&preauth="+preAuth;
  }

    public static  String computePreAuth(Map params, String key) {
        TreeSet names = new TreeSet(params.keySet());
        StringBuffer sb = new StringBuffer();
        for (Iterator it=names.iterator(); it.hasNext();) {
            if (sb.length() > 0) sb.append('|');
            sb.append(params.get(it.next()));
        }
        return getHmac(sb.toString(), key.getBytes());
    }

    private static String getHmac(String data, byte[] key) {
        try {
            ByteKey bk = new ByteKey(key);
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(bk);
            return toHex(mac.doFinal(data.getBytes()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("fatal error", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("fatal error", e);
        }
    }
    
    
    static class ByteKey implements SecretKey {
        private byte[] mKey;

        ByteKey(byte[] key) {
            mKey = (byte[]) key.clone();;
        }

        public byte[] getEncoded() {
            return mKey;
        }

        public String getAlgorithm() {
            return "HmacSHA1";
        }

        public String getFormat() {
            return "RAW";
        }
   }

    public static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);
        for (int i=0; i<data.length; i++ ) {
           sb.append(hex[(data[i] & 0xf0) >>> 4]);
           sb.append(hex[data[i] & 0x0f] );
        }
        return sb.toString();
    }

    private static final char[] hex =
       { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
         '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f'};


%><%
String casUser = request.getRemoteUser().toString().trim();
String redirect = generateRedirect(request, casUser);
response.sendRedirect(redirect);

%>
<html>
<head>
<title>Pre-auth redirect</title>
</head>
<body>

You should never see this page.

</body>
</html>
Magical command
If you do not own a certificate for you CAS server signed by a trusted CA you will need to add the certicate into the zimbra keystore.

Code: Select all

keytool -import -file /tmp/cas.crt -alias cas -trustcacerts -keystore /opt/zimbra/common/etc/java/cacerts -storepass changeit
The Guy - DualBoot

PostMaster - WikiMaster - SysAdmin
"Free Your Mind. Think Open Source"
april.org
Zetalliance Member - zetalliance.org
User avatar
jorgedlcruz
Zimbra Alumni
Zimbra Alumni
Posts: 2782
Joined: Thu May 22, 2014 4:47 pm

Re: CASifying Zimbra : an update documentation

Post by jorgedlcruz »

Hello
Thank you for this Post, it's awesome!
Jorge de la Cruz https://jorgedelacruz.es
Systems Engineer at Veeam Software https://www.veeam.com/
kornflex
Posts: 3
Joined: Wed Apr 13, 2016 2:41 pm

Re: CASifying Zimbra : an update documentation

Post by kornflex »

Hi,

If you want to use CAS for multidomain, I've wrote this article ( french) :http://www.babash.fr/2016/05/31/zimbra- ... on-de-cas/

Bye
User avatar
DualBoot
Elite member
Elite member
Posts: 1326
Joined: Mon Apr 18, 2016 8:18 pm
Location: France - Earth
ZCS/ZD Version: ZCS FLOSS - 8.8.15 Mutli servers
Contact:

Re: CASifying Zimbra : an update documentation

Post by DualBoot »

I know your article too.
The last part is a good workaround, and an other way for this, is to use a preauth.jsp which has the ability to execute ldap request.
The Guy - DualBoot

PostMaster - WikiMaster - SysAdmin
"Free Your Mind. Think Open Source"
april.org
Zetalliance Member - zetalliance.org
ctmanh
Posts: 3
Joined: Fri Apr 07, 2017 7:43 am

Re: CASifying Zimbra : an update documentation

Post by ctmanh »

I'm using Zimbra 8.7.4 Open source, CAS Server 4.0.0, cas-client-core-3.4.1, slf4j-api-1.7.21
But ticket not working
Last edited by ctmanh on Mon Apr 10, 2017 4:56 am, edited 2 times in total.
User avatar
DualBoot
Elite member
Elite member
Posts: 1326
Joined: Mon Apr 18, 2016 8:18 pm
Location: France - Earth
ZCS/ZD Version: ZCS FLOSS - 8.8.15 Mutli servers
Contact:

Re: CASifying Zimbra : an update documentation

Post by DualBoot »

Hello,

Do you have anything that could help ? like log messages or screen shot of the problem ?

Regards,
phoenix
Ambassador
Ambassador
Posts: 27278
Joined: Fri Sep 12, 2014 9:56 pm
Location: Liverpool, England

Re: CASifying Zimbra : an update documentation

Post by phoenix »

ctmanh wrote:I'm using Zimbra 8.7.4 Open source, CAS Server 4.0.0, cas-client-core-3.4.1, slf4j-api-1.7.21
But ticket not working
When you're asked to provide further information you should add it as a new post otherwise people are not notified about your addition.

You should also remove the bit of advertising you have placed on the bottom right corner of that image, this forum isn't the place for that and it's not acceptable.
Regards

Bill

Rspamd: A high performance spamassassin replacement

Per ardua ad astra
ctmanh
Posts: 3
Joined: Fri Apr 07, 2017 7:43 am

Re: CASifying Zimbra : an update documentation

Post by ctmanh »

DualBoot wrote:Hello,

Do you have anything that could help ? like log messages or screen shot of the problem ?

Regards,
screen shot of the problem
ticket not working?
Image
User avatar
DualBoot
Elite member
Elite member
Posts: 1326
Joined: Mon Apr 18, 2016 8:18 pm
Location: France - Earth
ZCS/ZD Version: ZCS FLOSS - 8.8.15 Mutli servers
Contact:

Re: CASifying Zimbra : an update documentation

Post by DualBoot »

And what about log ?
Check time between CAS server and Zimbra too.
minh281182
Posts: 4
Joined: Sat Sep 13, 2014 1:42 am

Re: CASifying Zimbra : an update documentation

Post by minh281182 »

Could you please give a help?
I got Internal Server Error 500, same as previous post.
The log in /opt/zimbra/log/mailbox.log said that:

[btpool0-7891://xxxxx/zimbra/public/preauth.jsp?ticket=ST-4-CvihcFcSVjnL9LGP7ZlY-cas] [] CommonUtils - No route to host
java.net.NoRouteToHostException: No route to host

Please instruct how to overcome this problem? Thank you!
Post Reply