Empezando con Liferay: una guía rápida de instalación

Dado que reciéntemente he tenido que instalar un Liferay limpito en un servidor con Apache, dejo aquí una guía con los pasos que seguí:

  1. ¿Qué es Liferay?
  2. Instalación básica
  3. Usando Liferay con MySQL
  4. Usando Liferay con Apache
  5. Usuario por defecto y un par de ajustes más

¿Qué es Liferay?

Liferay es un gestor de portales web con una gran funcionalidad integrada (gestión de comunidades y usuarios, CMS, wiki, blogs, y mucho más), y a su vez es un contenedor de portlets, lo que le permite ser extendido de manera bastante rápida y flexible (siguiendo la especificación JSR 286: Portlet Specification 2.0). Además, cuenta con una versión Community cuyo uso es gratuito y cuyo código es libre.

Instalación básica

Descargamos la última versión Community con el usuario que arrancará Liferay (en mi caso www-data) en el sitio que queramos (yo he escogido /opt/websites/liferay.deigote.com como directorio base de la instalación):

$ su - www-data
$ cd /opt/websites/
$ wget 'http://sourceforge.net/projects/lportal/files/Liferay%20Portal/liferay-portal-tomcat-6.0-5.2.3.zip'
$ unzip liferay-portal-tomcat-6.0-5.2.3.zip
$ mv liferay-portal-5.2.3/ liferay.deigote.com

Si ahora arrancamos Liferay, podremos ver cómo nos informa en los log de que se usará una base de datos para testing (Hipersonic), y si navegamos por la web, podremos ver una serie de portlets a modo de demo:

$ /opt/websites/liferay.deigote.com/tomcat-6.0.18/bin/startup.sh
$ tail -f logs/catalina.out &
$ firefox http://localhost:8080

Este entorno de demo está bien para cacharrear un poco y ver cómo la gente de Liferay quiere vendernos sus capacidades para hacer un clon de Facebook :D (vienen instanciados portlets de chat, de muro, de añadir usuarios como amigo, de actividad reciente…), pero los datos no persisten, por lo que no podréis pasar de ahí. Además, Liferay viene por defecto con usa serie de portlets (en forma de plugins) preparados para hacer la demostración antes mencionada. Yo normalmente borro dichos portlets antes de continuar (de hecho, en mi caso borro todos los plugins excepto el de web-form-portlet, que es el único que encuentro útil):

$ rm -rf `ls /opt/websites/liferay.deigote.com/tomcat-6.0.18/webapps | grep -v ROOT | grep -v web-form-portlet`

Para la persistencia de los datos, tenemos que conectar Liferay con un viejo conocido :D .

Usando Liferay con MySQL

Para conectar Liferay con MySQL existen varias formas. De momento anotaré aquí la más sencilla (aunque para mi gusto un poco “fea”), puesto que no recuerdo exactamente cómo es la otra :D . Liferay incluye en su core un fichero de propiedades (portal.properties) que configuran prácticamente todos los componentes del portal, base de datos incluida. Ese fichero puede ser extendido mediante el fichero portal-ext.properties, que por defecto no existe. Así que escribimos en él la configuración de la base de datos:

$ echo "# Database connection
jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.url=jdbc:mysql://localhost/liferay_database?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.default.username=mysql-user
jdbc.default.password=mysql-password" > /opt/websites/liferay.deigote.com/tomcat-6.0.18/webapps/ROOT/WEB-INF/classes/portal-ext.properties

A continuación nos conectamos al servidor de mysql (en mi caso, localhost) y creamos la base de datos y un usuario con permisos para la misma:

$ mysql -h localhost -u root -p
$ create database liferay_database
$ grant all privileges on liferay_database.* to "mysql-user"@"localhost" identified by "mysql-password";

Si en este momento arrancamos Liferay de nuevo, deberíamos ver algunos mensajes haciendo mención a la base de datos utilizada (MySQL), y otros que indican que se están creando las tablas..

Usando Liferay con Apache

Para usar Apache como servidor web, creamos un host virtual y lo conectamos al servidor de aplicaciones de Liferay (en mi caso Tomcat) usando un módulo de proxy. Podemos usar el módulo proxy_http, que funcionaría con cualquier servidor de aplicaciones, o el módulo proxy_ajp, específico de Apache, y que presenta algunas ventajas sobre http, aunque yo no las recuerde :D . Dado que Tomcat soporta AJP, será el que usemos. Dado que yo uso Debian, necesito activar el módulo de proxy_ajp y crear un host virtual que use dicho módulo:

$ su -
# a2enmod proxy_ajp
# nano /etc/apache2/sites-available/liferay.deigote.com
# a2ensite liferay.deigote.com
# /etc/init.d/apache2 restart
# exit

El contenido del fichero /etc/apache2/sites-available/liferay.deigote.com será el siguiente:

<VirtualHost *:80>
        ServerName liferay.deigote.com
        ServerAdmin webmaster@localhost
        ErrorLog /var/log/apache2/liferay.deigote.com_error.log
        LogLevel warn
        CustomLog /var/log/apache2/liferay.deigote.com_access.log combined
        # Proxy to Tomcat
        <Proxy *>
                Order deny,allow
                Allow from all
        </Proxy>
        ProxyPass / ajp://liferay.deigote.com:8009/
        ProxyPassReverse / ajp://liferay.deigote.com:8009/
</VirtualHost>

Antes de echar a andar con esta configuración, debemos añadir un par de líneas al fichero de propiedades de Liferay, ya que si no éste dará por hecho que estamos atacando al puerto 8080 (el puerto por defecto de Tomcat) y escribirá las URL’s con dicho puerto. Una vez realizado este paso, deberíamos poder acceder a Liferay a través del host virtual que hemos usado (siempre y cuando nuestro servidor DNS sepa resolver dicho host, claro):

$ /opt/websites/liferay.deigote.com/tomcat-6.0.18/bin/shutdown.sh
$ echo "
# Webserver configuration
web.server.http.port=80
web.server.https.port=443" >> /opt/websites/liferay.deigote.com/tomcat-6.0.18/webapps/ROOT/WEB-INF/classes/portal-ext.properties
$ /opt/websites/liferay.deigote.com/tomcat-6.0.18/bin/startup.sh
$ firefox http://liferay.deigote.com

Usuario por defecto y un par de ajustes más

Una vez estemos navegando por Liferay, podremos acceder usando el usuario test@liferay.com con la contraseña test, que es administrador de la comunidad por defecto (guest) Yo recomiendo un par de ajustes más:

  • Cambiar la dirección de correo (y por tanto el login) y la contraseña del usuario administrador. Esto lo podéis hacer en el Panel de control, en el apartado de Usuarios.
  • Modificar el host virtual de la comunidad por defecto (o la que vayáis a usar) para que coincida con el que estéis usando para acceder a través de Apache. Esto se puede hacer en el panel de control, en el apartado Communities – Guest – Manage pages – Settings – Virtual host, usando el campo Public virtual host. Esto permitirá que las URL’s del tipo http://virtual_host/web/nombre_de_la_comunidad/pagina pasen a ser http://virtual_host/pagina, lo cual es más cómodo. Por ejemplo, la URL de la página por defecto (home) en la comunidad por defecto (guest) en mi caso pasaría de http://liferay.deigote.com/web/guest/home a http://liferay.deigote.com/home

Una vez finalizados estos pasos, ya podemos empezar a trabajar con Liferay en un entorno de producción (a falta, por supuesto, de configuraciones y optimizaciones de Tomcat, Apache y MySQL que no vienen al caso :D ).

Contestadores automáticos: al final, alguien te está escuchando

Conversación hipotética entre un contestador automático de atención al cliente y yo:

  • Contestador: Bienvenido al servicio de atención al cliente de Filostros Asociados. Si desea hacer una consulta general sobre filostros, por favor, pulse 1. Si desea información sobre nuestros puntos de venta, por favor, pulse 2. Si desea consultar el estado de un pedido de filostros, por favor, pulse…
  • Diego: Operador
  • Contestador: Perdone, no hemos entendido su opción. Si desea hacer una consulta general sobre filostros, por favor, pulse 1…
  • Diego: Quiero cambiar la dirección de entrega de mi pedido de filosotros, a ver si me podéis pasar con un operador
  • Operador: Buenas tardes, le atiende Gloria Aleluya, ¿en qué puedo ayudarle?
  • Un amigo que trabajaba en atención al cliente de Vomistar Movistar me dijo hace unos cuantos años que en realidad casi siempre hay un operador que te redirije manualmente a la opción escogida, en caso de que sea por voz, y en caso de que sea por número, siempre hay alguien “vigilando”. Por lo visto tenía razón, pues este truco ya lo he usado con éxito unas cuantas veces.

Registros NS, A, CNAME, MX y SPF de un DNS: ¿y eso qué es lo que es?

Tras enfrentarme por primera vez a la gestión de un registro DNS (uooooh :cool: :lol: ) voy a hacer un pequeño resumen de los principales campos que lo componen de cara a un usuario (tenéis una referencia rápida de todos los posibles en, como no, la wikipedia).

  • Registro NS (name server): indica los servidores de DNS autorizados para el dominio, es decir, a quién tengo que preguntar para saber acerca de los registros de dominio.com.
    Ejemplo: toharia.com NS ns15.ovh.net
  • Registro A (address): indica la dirección IP a la que se debe traducir ese nombre de dominio.
    Ejemplo: toharia.com A 94.23.62.64
  • Registro CNAME (canonical name): permite definir alias o nombres de domino equivalentes. Normalmente, se usa para definir subdominios (que casi siempre apuntarán al dominio principal, aunque no es obligatorio).
    Ejemplo: mail.toharia.com CNAME toharia.com, pero también diego.toharia.com CNAME deigote.com
  • Registro MX (mail exchange): permite definir a qué servidor se envía el correo electrónico del dominio en cuestión. Cuenta con un campo prioridad, por lo que para un mismo dominio se pueden definir varios registros MX con distinta prioridad, escogiéndose el primero que esté disponible en cada momento.
    Ejemplo: toharia.com MX 1 aspmx.l.google.com, toharia.com MX 5 alt1.aspmx.l.google.com. En la práctica, cuando un servidor SMTP (es decir, que envía correo) tiene que enviar un correo a diego@toharia.com, inspecciona los registros MX para toharia.com, y envía el correo al de mayor prioridad que encuentre disponible (en este caso, empezaría por alt1.aspmx.l.google.com, y luego seguiría con el resto).
  • Registro SPF (sender policy framework): permite definir qué servidores están autorizados para enviar correo electrónico del dominio. Su uso surgió como propuesta para acabar o al menos dificultar el spam (correos no deseados con publicidad, phishing, etcétera). Normalmente, los spammers envían correo electrónico desde direcciones recopiladas en internet (y a direcciones recopiladas en internet :D ), para que sea más difícil indentificar el correo como spam. Sin embargo, si el filtro antispam está bien implementado, mirará el registro SPF del dominio del remitente de un correo entrante, y si la dirección IP origen del correo no está entre las adminitidas en dicho registro, lo marcará como spam. Podéis aprender más sobre SPF en el sitio web oficial.
    Ejemplo: toharia.com SPF v=spf1 a mx include:aspmx.googlemail.com ~all. Si os fijáis, lo que viene a decir es que el campo spf incluye el registro A, el registro MX y el servidor aspmx.googlemail.com. Esto es porque los correos los enviaré desde el propio servidor que aloja toharia.com, desde los servidores de recepción de correo MX, y desde aspmx.googlemail.com (en mi caso sería redundante ya que los registros MX ya incluyen este servidor).

Nota: mis registros MX y SPF incluyen direcciones MX de Google porque el correo de ese dominio lo tengo gestionado con Google Apps. Por tanto, la recepción del correo siempre la hago en sus servidores, mientras que el envío la hago tanto desde sus servidores (cuando lo hago desde la aplicación web de correo electrónico de Google) como desde el servidor que aloja ese dominio (cuando lo hace alguna aplicación que corre en el mismo).

Subversion desde Apache usando virtual hosts y locations

Aunque hay bastantes guías que cubren cómo configurar un WebDAV para acceder a un repositorio Subversion mediante HTTP usando el servidor Apache, aquí os presento la mía, en la que configuro el acceso a un conjunto de repositorios usando un virtual host y distintas locations (es decir, es una guía más con la configuración que a mi más me gusta :D ).

Lo primero que debemos hacer es asegurarnos de que tenemos el módulo adecuado. En Debian, por ejemplo, podemos buscarlo e instalarlo si es necesario usando los siguientes mandatos:

$ aptitude search apache subversion | grep svn
# aptitude install libapache2-svn

Además, deberemos asegurarnos de que Apache habilita el módulo, lo cual, nuevamente en Debian, es trivial gracias a la buena organización que tiene de la configuración de Apache:

$ a2enmod dav_svn
# /etc/init.d/apache2 restart

El siguiente paso es crear un repositorio Subversion. Lo habitual es crear uno por proyecto, aunque en mi caso no lo hago así exactamente. Por ejemplo, tengo un repositorio al que llamo personal en el que tengo mi CV, algunas prácticas y cosas pequeñas en las que es más que improbable que participe nadie más que yo. Depende del criterio de cada uno (por ejemplo, a mi siempre me resulta tentador tener un único respositorio para todo, y a la hora de hacer el checkout, hacerlo con ruta relativa al proyecto que necesito), pero recordad que los usuarios, contraseñas y permisos serán los mismos para todo un repositorio. La creación del repositorio la hacemos con los mandatos:

$ mkdir -p /opt/svn/nombre_del_repositorio/
$ svnadmin create /opt/svn/nombre_del_repositorio/repo

Podemos verificar que funciona intentando hacer un checkout:

$ svn co file:////opt/svn/nombre_del_repositorio/repo /tmp/

Por último, creamos los usuarios y passwords del repositorio:

$ mkdir -p /opt/svn/nombre_del_repositorio/passwords/
$ htpasswd -c /opt/svn/nombre_del_repositorio/passwords/.htpasswd usuario

Y procedemos a crear el fichero de configuración del virtual host de Apache. En el ejemplo, lo voy a hacer para dos repositorios, uno llamado personal y otro llamado público, suponiendo que quiera acceder a ambos a través del mismo dominio (svn.deigote.com) pero con distintas localizaciones (/personal y /publico):

<VirtualHost *:80>
   ServerName svn.deigote.com
   DocumentRoot /opt/websites/svn.deigote.com

Como véis, definimos un virtual host para el dominio elegido, y le asignamos un docroot, en el que podríamos poner una página de inicio o incluso vacía (para que si alguien entra directamente en el dominio, no vea el clásico It works! situado en /var/www :D )(1).

A partir de aquí, para cada localización (personal y publico) establecemos que es un WebDAV de tipo Subversion, e indicamos la ruta del repositorio y del fichero de passwords, tanto para el repositorio personal:

   <Location "/personal" >
     DAV svn
     SVNPath /opt/svn/personal/repo

     AuthType Basic
     AuthName "SVN Deigote - Personal"
     AuthUserFile /opt/svn/personal/passwords/.htpasswd
     Require valid-user

     Order deny,allow
     Deny from all
     Allow from unaipdeconfianza.com
   </Location>

como para el repositorio público:


   <Location "/publico" >
     DAV svn
     SVNPath /opt/svn/publico/repo

     AuthType Basic
     AuthName "SVN Deigote - Publico"
     AuthUserFile /opt/svn/publico/passwords/.htpasswd
     <LimitExcept GET PROPFIND OPTIONS REPORT>
       Require valid-user
     </LimitExcept>
   </Location>

Podemos ver un par de diferencias. Mientras que el repositorio personal pide un usuario válido para todos los casos (es decir, un usuario definido en el fichero de passwords), el repositorio público especifica que necesita un usuario válido excepto para algunas acciones. Básicamente, son el conjunto de acciones que permiten lectura y navegación por el repositorio. De esta manera, todo el mundo podrá ver (acciones checkout, update, etcétera) pero sólo los usuarios válidos podrán escribir (acción commit, add, etcétera) (2)

La otra diferencia es que el repositorio personal tiene una sección que especifica que el acceso es denegado para todos, y admitido para una ip o nombre de dominio. Esto te garantiza que la persona que accede a tu repositorio lo está haciendo desde una IP de tu confianza, aumentando ligeramente la paranoia seguridad.

Añadir que el directorio raiz (/) es un location válido, por lo que podemos configurar un único repositorio para el dominio, aunque sería equivalente a poner dicha configuración directamente en el contexto del virtual host en vez de en el del location.

Añadiendo la información de logs y demás, el fichero de virtual host de ejemplo (/etc/apache2/sites-available/svn.deigote.com) queda como se ve a continuación:

<VirtualHost *:80>
   ServerName svn.deigote.com
   DocumentRoot /opt/websites/svn.deigote.com
   <Location "/personal" >
     DAV svn
     SVNPath /opt/svn/personal/repo

     AuthType Basic
     AuthName "SVN Deigote - Personal"
     AuthUserFile /opt/svn/personal/passwords/.htpasswd
     Require valid-user

     Order deny,allow
     Deny from all
     Allow from unaipdeconfianza.com
   </Location>

   <Location "/publico" >
     DAV svn
     SVNPath /opt/svn/publico/repo

     AuthType Basic
     AuthName "SVN Deigote - Publico"
     AuthUserFile /opt/svn/publico/passwords/.htpasswd
     <LimitExcept GET PROPFIND OPTIONS REPORT>
       Require valid-user
     </LimitExcept>
   </Location>

   ErrorLog /var/log/apache2/error_svn.deigote.com.log
   LogLevel warn
   CustomLog /var/log/apache2/access_svn.deigote.com.log combined
</VirtualHost>

Lo añadimos a la lista de sites y reiniciamos Apache, y ya estamos listos para jugar:

$ a2ensite svn.deigote.com
$ /etc/init.d/apache restart
$ svn co http://svn.deigote.com/personal
$ svn co http://svn.deigote.com/publico

Como nota final, ojo con los passwords. Tened en cuenta que estamos configurando un acceso a través de HTTP, por lo que la información no va encriptada. Para que sí lo fuera tendríamos que configurar el virtual host para ir a través de HTTPS, pero eso lo dejamos para un próximo capítulo (más que nada porque todavía no he aprendido a hacerlo :) ).

(1) Tened cuidado de no crear directorios en el document root con el mismo nombre que los location. Os encontraréis con un error de este estilo al hacer el checkout:

$ ls /opt/websites/svn.deigote.com # Docroot
personal
$ svn co http://svn.deigote.com/personal/ /tmp/personal
Authentication realm: SVN Deigote - Personal
Password for 'deigote':
svn: PROPFIND request failed on '/personal'
svn: PROPFIND of '/personal': 301 Moved Permanently (http://svn.deigote.com)
$ rmdir /opt/websites/svn.deigote.com/personal
$ svn co http://svn.deigote.com/personal/ /tmp/personal
...
Checked out revision 1

(2) Se puede afinar más usando mod_authz_svn, que permite establecer qué usuarios pueden escribir y cuáles pueden leer sin complicarse mucho, pero yo de momento no lo he necesitado, así que queda para el siguiente capítulo :-)