jueves, 2 de junio de 2016

SSO con Redmine 3.2.1 en Debian 8.4

En mi empresa han decidido usar Redmine para la gestion de proyectos y helpdesk, y me ha tocado instalarlo y configurarlo con SSO para que el acceso no requiera de usuario y contraseña cogiendo esta información del usuario que está logado en windows.

Para ello hemos usado el plugin single auth no sin una serie de problemas para conseguir que el sistema funcione correctamente. He tenido que recoger información que estaba diseminada por Internet sin encontrar un único sitio en el que ver la configuración de principio a fin y funcionando, motivo por el que lo anoto aquí para referencias posteriores.

Partimos de la suposición de que tenemos una máquina con Redmine corriendo en la que hemos instalado el plugin en cuestión y el plugin del que depende (a_common_libs).

Los pasos a seguir son (voy a hacer todo el proceso desde root, por lo que no necesitaré usar sudo):
  • Instalar los paquetes que necesitamos (si no los tenemos ya instalados)
root@Mars:~# apt-get install samba winbind libapache2-mod-auth-ntlm-winbind heimdal-clients libpam-heimdal
La instalación de kerberos (heimdal) nos puede solicitar el dominio predeterminado, introduciremos el nuesto que en este ejemplo será NUESTRODOMINIO.ES.
Los paquetes que ha instalado el comando anterior son en mi caso samba y winbind 4.2.10 así como heimdal-clients 1.6.
  • Una vez instalados pasaremos a configurar los distintos componentes (kerberos, samba, y apache).

    1. configurar kerberos, para ello necesitaremos modificar el archivo /etc/krb5.conf. En nuestro caso queda de la siguiente forma:

    2. [libdefaults]
          default_realm = NUESTRODOMINIO.ES

      # The following krb5.conf variables are only for MIT Kerberos.
          krb4_config = /etc/krb.conf
          krb4_realms = /etc/krb.realms
          kdc_timesync = 1
          ccache_type = 4
          forwardable = true
          proxiable = true

      # The following libdefaults parameters are only for Heimdal Kerberos.
          v4_instance_resolve = false
          v4_name_convert = {
              host = {
                  rcmd = host
                  ftp = ftp
              }
              plain = {
                  something = something-else
              }
          }
          fcc-mit-ticketflags = true

      [realms]
          NUESTRODOMINIO.ES = {
              kdc = nuestro_servidor.nuestrodominio.es
              admin_server = nuestro_servidor.nuestrodominio.es
              default_domain = nuestrodominio
          }

      [domain_realm]
          .nuestrodominio.es = NUESTRODOMINIO.ES
          nuestrodominio.es = NUESTRODOMINIO.ES

      [login]
          krb4_convert = true
          krb4_get_tickets = false

      En la configuración anterior son MUY IMPORTANTES las mayúsculas y minúsculas. Si hemos introducido los datos correctos deberíamos ser capaces de obtener un ticket de kerberos con un usuario del dominio, para ello usamos kinit para obtener el ticket y klist para comprobar que ha salido todo correcto:
      root@Mars:~# kinit mi_usuario
      mi_usuario@NUESTRODOMINIO.ES's Password:
      root@Mars:~# klist
      Credentials cache: FILE:/tmp/krb5cc_0
          Principal: mi_usuario@NUESTRODOMINIO.ES

         Issued Expires Principal
      Jun 1 11:58:35 2016 Jun 1 21:58:31 2016 krbtgt/NUESTRODOMINIO.ES@NUESTRODOMINIO.ES


    3. Si todo ha ido correcto (habiendo introducido los datos correctos en krb5.conf) pasamos a configurar samba, en mi caso es una configuración básica, única y exclusivamente para realizar el SSO.

    4. [global]

          workgroup = NUESTRODOMINIO
          realm = NUESTRODOMINIO.ES
          preferred master = no
          password server = *
          security = domain
          encrypt passwords = yes
          winbind use default domain = yes
          winbind refresh tickets = true

          log file = /var/log/samba/log.%m
          max log size = 1000
          syslog = 0
          panic action = /usr/share/samba/panic-action %d

      Tras ésto reiniciaremos samba y después nos uniremos al dominio:
      root@Mars:~# service samba reload
      [ ok ] Reloading smbd configuration (via systemctl): smbd.service.
      root@Mars:~# net join -U usuario_dominio
      Enter usuario_dominio's password:
      Using short domain name -- NUESTRODOMINIO
      Joined 'MARS' to dns domain 'NUESTRODOMINIO.ES'
      Si al unirnos al dominio obtenemos el siguiente error:
      No DNS domain configured for mars. Unable to perform DNS Update.
      DNS update failed: NT_STATUS_INVALID_PARAMETER
      Será porque no tenemos bien definido el nombre de la máquina en el /etc/hosts. Deberemos tener como primera entrada tras la dirección IP de la máquina el nombre de ésta con el dominio, en el caso de ejemplo sería:
      a.b.c.d Mars.nuestrodominio.es
      Siendo a.b.c.d la IP de la máquina.   Tras esta modificación volvemos a conectar al dominio y ya no deberíamos tener ningún error, si es así reiniciamos winbind
      root@Mars:~# service winbind restart
      Ahora podremos comprobar que está todo correcto corriendo wbinfo -g y confirmar que nos devuelve los grupos del dominio. (wbinfo -u debería devolver los usuarios, pero en mi caso no funciona correctamente.)

      Si se nos olvidó reiniciar el servicio de winbind obtendremos el siguiente mensaje de error:
      could not obtain winbind interface details: WBC_ERR_WINBIND_NOT_AVAILABLE
      could not obtain winbind domain name!
      failed to call wbcListGroups: WBC_ERR_WINBIND_NOT_AVAILABLE
      Error looking up domain groups
    5. Ahora vamos con Apache.
    6. Lo primero de todo será enlazar el módulo de NTLM (en mi instalación apache 2.4.10 no lo hace automáticamente) ya que sino obtendremos el siguiente error al arrancar apache tras configurar NTLM:
      root@Mars:/# service apache2 restart
      Job for apache2.service failed. See 'systemctl status apache2.service' and 'journalctl -xn' for details.
      root@Mars:/# systemctl status apache2.service
      ● apache2.service - LSB: Apache2 web server
         Loaded: loaded (/etc/init.d/apache2)
         Active: failed (Result: exit-code) since mié 2016-06-01 12:13:42 CEST; 54s ago
         Process: 9945 ExecStop=/etc/init.d/apache2 stop (code=exited, status=0/SUCCESS)
         Process: 9972 ExecStart=/etc/init.d/apache2 start (code=exited, status=1/FAILURE)

      jun 01 12:13:42 Mars apache2[9972]: Starting web server: apache2 failed!
      jun 01 12:13:42 Mars apache2[9972]: The apache2 configtest failed. ... (war...).
      jun 01 12:13:42 Mars apache2[9972]: Output of config test was:
      jun 01 12:13:42 Mars apache2[9972]: AH00526: Syntax error on line 8 of /etc...f:
      jun 01 12:13:42 Mars apache2[9972]: Invalid command 'NTLMAuth', perhaps mis...on
      jun 01 12:13:42 Mars apache2[9972]: Action 'configtest' failed.
      jun 01 12:13:42 Mars apache2[9972]: The Apache error log may have more info...n.
      jun 01 12:13:42 Mars systemd[1]: apache2.service: control process exited, c...=1
      jun 01 12:13:42 Mars systemd[1]: Failed to start LSB: Apache2 web server.
      jun 01 12:13:42 Mars systemd[1]: Unit apache2.service entered failed state.
      Hint: Some lines were ellipsized, use -l to show in full.

      Como podemos ver en la parte de "invalid command" no reconoce NTLMAuth y eso es porque no tenemos el módulo en /etc/apache2/mods-enabled. 
       
      Para ello lanzamos el comando:
      root@Mars:/# a2enmod auth_ntlm_winbind
      Tras esto configuramos en /var/apache2/sites-enabled el archivo de nuestro Redmine. En mi caso queda de la siguiente forma:

      <VirtualHost *:80>

      ServerAdmin admin@example.com
      Servername Planea
      LogLevel debug
      DocumentRoot /var/www/html/redmine
              <Location "/login">
                      AuthType NTLM
                      AuthType Negotiate
                      NegotiateAuth on
                      NegotiateAuthHelper "/usr/bin/ntlm_auth --helper-protocol=gss-spnego"
                      NTLMAuth on
                      AuthName "Autentificación Redmine"
                      NTLMAuthHelper "/usr/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp"
                      NTLMBasicAuthoritative on
                      require valid-user
              </Location>
              <Directory /var/www/html/redmine>
                      RailsEnv production
                      RailsBaseURI /
                      RackBaseURI /
                      PassengerResolveSymlinksInDocumentRoot on
                      Options -MultiViews
              </Directory>

      </VirtualHost>

         
      Tras esto reiniciamos apache
      root@Mars:/# service apache2 restart
      se supone que ya deberíamos poder a acceder a Redmine sin necesidad de que nos pidiese usuario y contraseña, pero...
  • Y la guinda del pastel
  •  
     ...en cambio obtendremos el error "Internal Server Error" al conectarnos a la página (o por lo menos en mi caso eso es lo que obtenía). Echando un vistazo a los logs de apache (/var/logs/apache2/error.log) podremos ver algo como lo siguiente:
    [Wed Jun 01 12:28:43.602147 2016] [auth_ntlm_winbind:error] [pid 10381:tid 140064302552832] (20014)Internal error: [client x.x.x.x:50260] ntlm_auth reports Broken Helper: BH NT_STATUS_UNSUCCESSFUL NT_STATUS_UNSUCCESSFUL
    Buscando en google, podremos comprobar en este enlace que hay algo que modificar.
    Debemos dar al usuario de apache (www-data) acceso al directorio /var/lib/samba/winbindd_privileged y crear un enlace al archivo pipe que hay dentro. Para ello añadimos el usuario de apache al grupo winbindd_priv y hacemos los siguientes cambios:
    root@Mars:/# usermod -a -G winbindd_priv www-data
    root@Mars:/# chgrp winbindd_priv /var/lib/samba/winbindd_privileged
    root@Mars:/# ln -s /var/lib/samba/winbindd_privileged/pipe /var/run/samba/winbindd_privileged/pipe
    Tras los cambios deberemos reiniciar el servidor apache
    root@Mars:/# service apache2 restart
    Finalmente ya deberíamos tener Redmine funcionando con SSO para los usuarios. Si queremos que acepte los usuarios de nuestro dominio sin tener que darlos de alta en Redmine deberemos configurarlo con ldap, pero eso es otro tema...

viernes, 7 de mayo de 2010

Error en apache2

He aquí un error del que no he econtrado respuesta por ahí, pero he descubierto a que se debe.

El error en cuestion es:

Syntax error on line 1 of /etc/apache2/conf.d/pwd-apache:
Invalid command 'Paco:4d4.2dgJftZh2', perhaps misspelled or defined by a module not included in the server configuration
...fail!


Todas las búsquedas me llevan a que el error se produce porque no está cargado el módulo
authn_file
o alguno de los de seguridad. Pero en mi caso el error es muchísimo más tonto: En el archivo de configuración apache2.conf hay la siguiente entrada:
Include /etc/apache2/conf.d/

Eso hace que se intenten cargar el archivo pwd-apache como si fuese de configuración, y ese es el error. Si subimos el archivo a /etc/apache2 o lo ponemos en cualquier lado (que no esté en un Include de los archivos de configuración) se resuelve el problema.

jueves, 3 de diciembre de 2009

"unable to load module" al cargar el módulo del dnie en linux

Bueno si has llegado aquí buscando el error del título y no había muchas más páginas en tu busqueda en google..... creo que se cual es tu problema: El mismo que el mio :-)

Esto no es una guía para instalar el dni electrónico en linux, de esas hay un montón y entre ellas la de la dirección general de la policía, que está bastante bien, aunque falta alguna cosilla como este error.

Voy a arriesgarme a adivinar la arquitectura de tu procesador.... mmmmmmmmmmmmmmmmm 64bit :-)))))) y me voy a arriesgar a adivinar tu navegador... MMMMMMMM swiftfox??????? y arriesgaré más, tienes desinstalado firefox :-P

Este error sale cuando intentas cargar el módulo /usr/lib/opensc/opensc-pkcs11.so en los "security devices" de swiftfox.

Bueno, pues después de todo el rollo que he soltado aquí va la solución:

Está dando el error porque estás intentando cargar un módulo de 64 bits en el swiftfox de 32, ¿recuerdas para que instalaste swiftfox? ¿no sería para que funcionase el flash sin tener que utilizar el wrapper ya que no hay flash para 64 bits todavía?

La solución es muy simple, vuelve a instalar el firefox (apt-get install firefox-3.5) y carga el módulo desde éste. No habrá ningún error y ya podrás usar tu lector de dni electrónico en internet.

miércoles, 9 de septiembre de 2009

Campos spare en Witness CSCM de Avaya sin Viewer.

No me gusta el Viewer.
Y quiero usar los campos spare para añadir datos.

Veamos, queremos hacer grabación bajo demanda en Witness (de eso hablaré en otra entrada) y ya que hacemos la grabación, queremos aprovechar para guardar datos junto a ésta (como por ejemplo el número de expediente asociado a la llamada). Para esto existen unos campos "spare" en los que podemos meter la información que queramos (en realidad podemos crear los nombres que nos de la gana, no tienen por que llamarse spare). Pero queremos ver los campos spare en el CSCM sin necesidad de usar el Viewer que no me gusta nada.

Como hemos encontrado la solución voy a ponerla aquí para el que desee hacer lo mismo.

Antes de nada supongo que todos sabemos que el Contact Store corre sobre una máquina Linux, en concreto Avaya se empeña en instalarlo sobre una Red Hat. Supongo que para automatizar instalaciones y quitarse problemas.

La base de datos es Postgress, así que si sabemos lo que hay que modificar podremos ver en nuestras consultas en el CSCM los campos spare (o los nombres que nos de la gana no tienen porqué ser spare como dije antes) que queramos, para ello lo que tenemos que hacer es lo siguiente:

En la base de datos hay creada una vista que se llama browse, esta vista está construida de la siguiente manera:

SELECT inum, startedat, duration, recmodeid, callid, direction, svcnum, svcname, tarid, previnum, callparty,
"owner", parties_name_number(inum) AS partynames, parties_agent_name(inum) AS partyagentnames,
party_name_number(svcname::text, svcnum::text) AS vdn, callset_names(inum) AS csnames
FROM calls
NATURAL JOIN owners
WHERE callparty <> 2;

Contiene los campos que son accesibles desde el layout, por lo tanto deberemos crear campos en la vista para poder acceder a ellos desde éste.

Los campos spare (o los que sean, podemos poner el nombre que queramos como dije antes) están en las tablas udfs y udfnames, que se componen de los siguientes campos:

udfs: inum, udfid, udfval
udfnames: udfid, udfname

Los nombres de los spares estan en udfnames y están relacionados con la tabla udfs a través del campo udfid, por lo que para poder seleccionar los campos que nos interesan deberemos crear una función de la siguiente manera:

CREATE FUNCTION RecogeSpares(bigint, text) RETURNS text
AS 'select udfval from udfs where inum=$1 and udfid=(select udfid from udfnames where udfname=$2)'
LANGUAGE 'SQL';

Tras esto deberemos modificar la vista. Para no tocar la original lo que he hecho es crear una nueva y luego cambiar los nombres de la siguiente manera:

CREATE VIEW browsetmp AS
SELECT inum, startedat, duration, recmodeid, callid, direction, svcnum, svcname, tarid, previnum, callparty, "owner", parties_name_number(inum) AS partynames, parties_agent_name(inum) AS partyagentnames, party_name_number(svcname::text, svcnum::text) AS vdn, callset_names(inum) AS csnames, RecogeSpares(inum,'spare1') as Spare1, RecogeSpares(inum,'spare2') as Spare2, RecogeSpares(inum,'spare3') as Spare3, RecogeSpares(inum,'spare4') as Spare4, RecogeSpares(inum,'spare5') as Spare5, RecogeSpares(inum,'spare6') as Spare6, RecogeSpares(inum,'spare7') as Spare7, RecogeSpares(inum,'spare8') as Spare8
FROM calls
NATURAL JOIN owners
WHERE callparty <> 2;

Acabamos de crear la vista, podemos comprobar que funciona haciendo un

SELECT * from browsetmp where inum= 'numero que tiene spares para prueba';

Tras ver que todo funciona correctamente hacemos el cambio.

ALTER TABLE browse RENAME TO browse_original;
ALTER TABLE browsetmp RENAME TO browse;

Ahora ya tenemos la vista enviandonos las columnas que queremos, solo nos resta modificar el layout para que CSCM nos muestre las nuevas columnas. Para ello haremos lo siguiente:

Deberemos ir al directorio en el que se encuentran los layouts, es decir:
/opt/witness/properties/layouts
Modificamos el archivo lcscm.xml para incluir la columna, en mi caso queda de la siguiente manera (en negrita la linea que hay que añadir):

<?xml version="1.0" encoding="UTF-8"?>

<layout name="cscm" display="ldefault">

<view>browse</view>

<filters>
<filter type="date" dbName="startedat" display="fdate"/>
<filter type="string" dbName="partynames" display="fparty"/>
<filter type="string" dbName="partyagentnames" display="fagent"/>
<filter type="duration" dbName="duration" display="fduration"/>
<filter type="string" dbName="vdn" display="fvdn"/>
<filter type="ucid" dbName="callid" display="fcallid"/>
<filter type="callset" dbName="csnames" display="fcallset"/>
</filters>

<columns>
<column dbName="startedat" columnWidth="" formatType="DATE" display="cdate"/>
<column dbName="duration" columnWidth="" formatType="TIME" display="cduration"/>
<column dbName="partyagentnames" columnWidth="" formatType="" display="cagent"/>
<column dbName="partynames" columnWidth="" formatType="" display="cparty"/>
<column dbName="vdn" columnWidth="" formatType="" display="cvdn"/>
<column dbName="Spare1" columnWidth="" formatType="UCID" display="cspare1"/>
<column dbName="callid" columnWidth="" formatType="UCID" display="ccallid" enableLikeSearch="true"/>
</columns>

</layout>

Si añadiesemos otra linea en filters podríamos hacer búsquedas por número de expediente que en mi caso es el spare1.
Tras esto modificamos el archivo idefault.xml para poner el nombre de columan que queremos que nos aparezca en la página web. (solo muestro el trozo de código para español que es el que nos interesa y en negrita la linea a añadir).

<language lang="es">
<token tid="ldefault">Diseño predeterminado</token>
<token tid="fdate">Intervalo de inicio de llamada</token>
<token tid="fparty">Interlocutores</token>
<token tid="fagent">Agente</token>
<token tid="fduration">Longitud</token>
<token tid="fvdn">Servicio</token>
<token tid="fcallid">ID de llamada</token>
<token tid="fcallset">Conjunto de llamadas</token>
<token tid="cdate">Inicio de llamada</token>
<token tid="cduration">Long.</token>
<token tid="cagent">Agente</token>
<token tid="cparty">Interlocutores</token>
<token tid="cvdn">Servicio</token>
<token tid="cspare1">Expediente</token>
<token tid="ccallid">ID de llamada</token>
</language>


Ya tendremos en nuestras consultas de grabaciones un nuevo campo con el nombre Expediente que nos muestra los valores del campo spare1.

Bueno, espero que os sirva.

jueves, 26 de febrero de 2009

Notas sobre la alta disponibilidad

Bueno, en esta entrada voy a poner cosas a tener en cuenta sobre la configuración de la alta disponibilidad con Heartbeat/Pacemaker. Me han ido surgiendo a medida que hacía la configuración y he ido viendo el porque se me producían.

Primero y antes de nada, no modificar directamente cib.xml, ya lo puse en el otro post, pero en la documentación se insite y de hecho hay tres pasos fundamentales a la hora de modificarlo que son los siguientes:

1º) No modificar directamente el cib.xml.
2º) Volver a leer el punto 1.
3º) El cluster se enterará si no se siguen los pasos 1 y 2, y por tanto rechazará usar esa configuración.

Para modificar el cib podremos usar las herramientas que nos proporciona Pacemaker, como son cibadmin y crm_*.

Para modificar directamente el paso sería el siguietne:

cibadmin -Q > cib_aux
modificar cib_aux
cibadmin -R --xml-file cib_aux

Aunque personalmente prefiero ir por partes, así, si quisies modificar solo los recurosos haría
cibadmin -Q -o resources > recursos
modificaría y después haría
cibadmin -R --xml-file recursos

Hay una herramienta para modificar attributos como crm_attribute, otra que nos permite mover los recursos de una máquina a otra crm_resource o modificarlos, etc...
ver la documentación para entender todos y cada uno de los crm_*

Recursos prohibidos en nodos:
Aquí he tenido un problemilla, obtenía errores en el nodo en el que no se debía arrancar el servicio y esto es debido a que, aunque no vaya a correr nunca el recurso en ese nodo (-INFINITE) el script lsb debe existir, aunque se suponga que nunca va a correr en ese nodo. :(

Errores "curiosos":
Errores en el log del estilo

"ERROR: unpack_rsc_op: Remapping NTP_monitor_0 (rc=1) on musicas to an ERROR"

que el crm_mon -i5 mostrará como


Failed actions:
NTP_monitor_0 (node=musicas, call=5, rc=1): complete


pueden surgir y dejarnos KO al no saber porque se producen, bueno pues tras leer y leer.... la causa se supone que es simplemente que al realizar un /etc/init.d/ntp status el resultado no devuelve un OK entre el texto mostrado. Esto parece que se debe a un tema de comptatibilidad con versiones anterios de Heartbeat/Pacemaker, pero para encontrar el porque nos surge el error si no sabemos ese "pequeño" tema de compatibilidad nos podemos romper la cabeza durante unos días. En fin, bastará con editar el archivo /etc/init.d/ntp y añadir un simple [OK] al final de la linea

* NTP server is running.

No se muy bien porque, pero no he conseguido que me funcione, así que lo que me ha resuelto el problema ha sido lo siguiente: Al definir el recurso indicamos que la clase es ocf y el provider yo.

primitive id="NTP" class="ocf" type="ntp" provider="yo"

Tras esto nos creamos el directorio /usr/lib/ocf/resources.d/yo y copiamos en él el script /etc/init.d/ntp. Una vez hecho esto sustituimos en el script status) por monitor) y modificamos el código para que devuelva 0 cuando está corriendo, 7 si está parado y cualquier otra cosa si es algún error. Con esto evitaremos que nos muestre el error que nos estaba dando. Si en algún momento consigo hacerlo funcionar con lsb abriré otra entrada.

Bueno, no he tardado mucho en encontrarlo así que no abriré otra entrada. La respuesta estaba en la propia página de Heartbeat: http://www.linux-ha.org/LSBResourceAgent. El /etc/init.d/ntp status devuelave un 4 en lugar de un 3 cuando el servicio está parado. Tema resuelto.

Otro tipo de error es el de (unmanaged) FAILED. Supongo que podrá deverse a varias cosas, pero una de ellas es la siguiente. Al hacer los scripts de los recursos, hay que tener en cuenta que éstos se ejecutarán de la misma forma que los que ponemos en el cron, por lo que deberemos poner el shell con el que queremos que se ejecuten y las variables de entorno que necesitemos, como PATH y demás. Digo esto porque tras comprobar de todas las maneras posibles los resultados de un script y ver que se ajustaban al estandar ocf me daba el error arriba descrito y es por la "tonteria" de no indicar éstas cosas.

Bueno por ahora esto es todo, espero ahorrar unos días de busquedas y roturas de cabeza buscando por ahí a que se deben estos comportamientos.

martes, 24 de febrero de 2009

Ejemplo alta disponibilidad con Heartbeat activo/activo

Vamos a crear un sistema de alta disponibilidad activo/activo con linux y Heartbeat/Pacemaker. Para ello contamos con dos máquinas (Caronte y Musicas ) y vamos a montar un único recurso que nos va a permitir que si una máquina se cae la otra responda con las dos direcciones IP (la suya y la de la máquina caida). Una vez el cluster cambie de una máquina a otra el añadir más servicios será coser y cantar, así que por ahora solo vamos a explicar esto.

Lo primero es preparar el sistema. En mi caso he cogido un par de máquinas no muy antiguas que había por la empresa (esas que a los de windows ya no les sirven y con las que Linux va de maravilla) y ponerles un par de tarjetas de red a cada una (una para la red y otra para el heartbeat). El heartbeat puede enviarse a través de un puerto serie con un modem nulo, pero mi experiencia no ha sido positiva, no se si por mi culpa que no he configurado bien el asunto pero al poco de comenzar se perdía la comunicación en un sentido y se me iba todo al carajo, así que me decanté por una tarjeta de red (hay un montón tiradas en la empresa muertas de risa).

Una vez tengamos las maquinas preparadas deberemos establecer las direcciones IP de cada una de ellas. En mi caso utilicé dos redes, una para el heartbeat y otra de servicio, esta última tendrá dos direcciones IP, una la del equipo como tal y otra la del nodo del cluster.

En mi caso quedaría de la siguiente manera:

Caronte:

- eth0 10.50.1.202/20 (servicio y cluster, la IP del cluster no se configura en el sistema operativo, se hará en la configuración del cib.xml)
- eth1 172.16.0.1/30 (heartbeat)


Musicas:

-eth0 10.50.1.203/20 (servicio y cluster)
-eth1 172.16.0.2/30


Una vez tengamos esto, estaremos ya en disposición de comenzar la configuración de heartbeat como tal.

Para el que todavía no lo tenga instalado, en ubuntu basta con hacer

sudo apt-get install heartbeat

El que no tenga ubuntu podrá ver en el siguiente enlace http://www.linux-ha.org/DownloadSoftware las últimas versiones y descargarselas.

Una vez instalado Heartbeat deberemos empezar configurando el archivo /etc/ha.d/ha.cf. La documentación está en http://www.linux-ha.org/ha.cf y en mi caso el archivo a quedado de la siguiente manera:


logfacility local6 #Los log seran enviados como local6 para poder redireccionarlos a un archivo de ha.log con syslog-ng
keepalive 500ms
warntime 2
deadtime 4
initdead 10
bcast eth2
udpport 694
node Caronte Musicas
auto_failback on #cuidado con esto
ping 10.50.1.1 #podemos usar ping_group para hacerlo contra varias máquinas
compression bz2
compression_threshold 2
crm respawn
autojoin any #Permitir que se unan nuevos nodos automáticamente


El archivo /etc/ha.d/authkeys queda en mi caso de la siguiente manera


auth 2
#1 crc
2 sha1 'contraseña que queramos`
#3 md5 Hello!


Tras configurar los dos archivos los copiamos al mismo directorio del otro nodo, y a partir de ahora es cuando empieza el meollo de la cuestión ya que configuramos los recursos de nuestro cluster. La documentación de como configurar el /var/lib/heartbeat/crm/cib.xml (OJO. No modificar nunca el archivo cib.xml directamente, hacerlo a través de las herramientas del crm que explicaré más adelante)está en el siguiente enlace http://clusterlabs.org/wiki/Documentation. Si por cualquier motivo no os reconoce los cambios en el archivo, puede ser que tengáis una versión y la documentación que estéis viendo sea de otra, así que para ver los campos de la vuestra, basta con consultar en el archivo /usr/share/heartbeat/crm.dtd, donde podemos encontrar éstos y valores que reconoce nuestra versión.

Los pasos que he seguido yo son los siguientes:

1º) Arrancamos el cluster en las dos máquinas, ejecutando en cada una de ellas /etc/init.d/heartbeat start
2º) Comprobamos que el cluster arranca y tenemos las dos máquinas levantadas con el comando
crm_mon -i5
Esperamos a que los dos nodos esten online como en mi caso:


Refresh in 1s...

============
Last updated: Wed Feb 25 10:27:21 2009
Current DC: caronte (871ed0db-b56c-467f-a31a-807fa717dccb)
2 Nodes configured.
0 Resources configured.
============

Node: caronte (871ed0db-b56c-467f-a31a-807fa717dccb): online
Node: musicas (6f0bbea5-947c-42ee-a5ba-9cb5b5ba9fef): online


3º) Llegados a este punto tenemos los dos nodos levantados, uno de ellos se ha erigido como DC (Designated Coordinator osea el coordinador designado para gestionar el cluster) en mi caso caronte y como se puede observar sin ningún recurso en ninguno de los nodos. Es ahora cuando empieza el trabajo para convertir este cluster en activo/activo. Antes de nada ejecutaremos crm_attribute -v false --attr-name symmetric-cluster que evitará ejecutar cualquier recurso en cualquier nodo que no hayamos espeificado. Después creamos un archivo llamado recursos que contenga lo siguiente:



<resources>
<primitive id="IPMusicas" class="ocf" type="IPaddr" provider="heartbeat">
<operations>
<op id="IPMusicas-startup" name="monitor" interval="0" timeout="90s"/>
<op id="IPMusicas-start" name="start" interval="0" timeout="180s"/>
<op id="IPMusicas-stop" name="stop" interval="0" timeout="180s"/>
</operations>
<instance_attributes id="IPMusicas">
<attributes>
<nvpair id="IPMusicas-ip" name="ip" value="10.50.2.230"/>
<nvpair id="IPMusicas-mask" name="netmask" value="255.255.240.0"/>
</attributes>
</instance_attributes>
</primitive>
<primitive id="IPCaronte" class="ocf" type="IPaddr" provider="heartbeat">
<operations>
<op id="IPCaronte-startup" name="monitor" interval="0" timeout="90s"/>
<op id="IPCaronte-start" name="start" interval="0" timeout="180s"/>
<op id="IPCaronte-stop" name="stop" interval="0" timeout="180s"/>
</operations>
<instance_attributes id="IPCaronte">
<attributes>
<nvpair id="IPCaronte-ip" name="ip" value="10.50.1.200"/>
<nvpair id="IPCaronte-mask" name="netmask" value="255.255.240.0"/>
</attributes>
</instance_attributes>
</primitive>
</resources>
<constraints>
<rsc_location id="MusicasEnCaronte" rsc="IPMusicas" node="caronte" score="0"/>
<rsc_location id="MusicasEnMusicas" rsc="IPMusicas" node="Musicas" score="200"/>
<rsc_location id="CaronteEnCaronte" rsc="IPCaronte" node="caronte" score="200"/>
<rsc_location id="CaronteEnMusicas" rsc="IPCaronte" node="Musicas" score="0"/>
</constraints>



Seguidamente ejecutaremos el comando

cibadmin -R --xml-file recursos

En ese momento el cluster cambiará, arrancará los dos servicio y se replicará el archivo cib.xml entre los nodos automáticamente. Pasaremos a tener en la pantalla en la que demajos el crm_mon -i3 los siguiente:


Refresh in 5s...

============
Last updated: Wed Feb 25 13:15:55 2009
Current DC: caronte (871ed0db-b56c-467f-a31a-807fa717dccb)
2 Nodes configured.
2 Resources configured.
============

Node: caronte (871ed0db-b56c-467f-a31a-807fa717dccb): online
Node: musicas (6f0bbea5-947c-42ee-a5ba-9cb5b5ba9fef): online

IPMusicas (heartbeat::ocf:IPaddr): Started musicas
IPCaronte (heartbeat::ocf:IPaddr): Started caronte



Como se puede observar se ha iniciado el servicio IPMusicas en musicas y el IPCaronte en caronte. En los recursos se definieron éstos y cada uno lo que hacía era asignar una dirección IP. Un ifconfig en Caronte mostraría:


eth0 Link encap:Ethernet direcciónHW 00:c0:9f:1d:75:62
inet dirección:10.50.1.202 Difusión:10.50.255.255 Máscara:255.255.240.0
dirección inet6: fe80::2c0:9fff:fe1d:7562/64 Alcance:Vínculo
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
RX packets:24880 errors:0 dropped:0 overruns:0 frame:0
TX packets:5018 errors:0 dropped:0 overruns:0 carrier:0
colisiones:0 txqueuelen:1000
RX bytes:2016368 (1.9 MB) TX bytes:690645 (674.4 KB)
Dirección base: 0xecc0 Memoria:fe100000-fe120000

eth0:0 Link encap:Ethernet direcciónHW 00:c0:9f:1d:75:62
inet dirección:10.50.1.200 Difusión:10.50.15.255 Máscara:255.255.240.0

eth1 Link encap:Ethernet direcciónHW 00:00:d1:f0:01:44
inet dirección:172.16.0.1 Difusión:172.16.0.4 Máscara:255.255.255.252
dirección inet6: fe80::200:d1ff:fef0:144/64 Alcance:Vínculo
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
RX packets:2867 errors:0 dropped:0 overruns:0 frame:0
TX packets:2829 errors:0 dropped:0 overruns:0 carrier:0
colisiones:0 txqueuelen:1000
RX bytes:823661 (804.3 KB) TX bytes:847457 (827.5 KB)
Interrupción:20


Y en Musicas:


eth0 Link encap:Ethernet HWaddr 00:c0:9f:1f:79:10
inet addr:10.50.1.203 Bcast:10.50.15.255 Mask:255.255.240.0
inet6 addr: fe80::2c0:9fff:fe1f:7910/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:82959 errors:0 dropped:0 overruns:0 frame:0
TX packets:12876 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:6484769 (6.4 MB) TX bytes:1816187 (1.8 MB)

eth0:0 Link encap:Ethernet HWaddr 00:c0:9f:1f:79:10
inet addr:10.50.2.230 Bcast:10.50.15.255 Mask:255.255.240.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

eth1 Link encap:Ethernet HWaddr 00:00:d1:f0:00:0c
inet addr:172.16.0.2 Bcast:172.16.0.4 Mask:255.255.255.252
inet6 addr: fe80::200:d1ff:fef0:c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:9965 errors:0 dropped:0 overruns:0 frame:0
TX packets:10550 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2892141 (2.8 MB) TX bytes:2940213 (2.9 MB)
Interrupt:20


Como se puede observar nos ha levantado las dos direcciones de los nodos 10.50.2.230 y 10.50.1.200.

Si ahora tiramos una de las dos máquinas (o tiramos el heartbeat) por ejemplo Musicas, un ping mostraría los siguientes resultados:


Haciendo ping a 10.50.2.230 con 32 bytes de datos:

Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Tiempo de espera agotado para esta solicitud.
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64
Respuesta desde 10.50.2.230: bytes=32 tiempo<1m TTL=64

Estadísticas de ping para 10.50.2.230:
Paquetes: enviados = 11, recibidos = 10, perdidos = 1
(9% perdidos),


Es decir, hemos perdido un único paquete de datos y enseguida a pasado a respondernos Caronte con la dirección de Musicas. Si ejecutamos el comando crm_mon -i5 nos mostrará los siguiente:


Refresh in 1s...

============
Last updated: Wed Feb 25 13:23:36 2009
Current DC: caronte (871ed0db-b56c-467f-a31a-807fa717dccb)
2 Nodes configured.
2 Resources configured.
============

Node: caronte (871ed0db-b56c-467f-a31a-807fa717dccb): online
Node: musicas (6f0bbea5-947c-42ee-a5ba-9cb5b5ba9fef): OFFLINE

IPMusicas (heartbeat::ocf:IPaddr): Started caronte
IPCaronte (heartbeat::ocf:IPaddr): Started caronte



Como se puede ver el recurso IPMusicas se ha arrancado en Caronte, y los interfaces de red se encuentran de la siguiente manera:


eth0 Link encap:Ethernet direcciónHW 00:c0:9f:1d:75:62
inet dirección:10.50.1.202 Difusión:10.50.255.255 Máscara:255.255.240.0
dirección inet6: fe80::2c0:9fff:fe1d:7562/64 Alcance:Vínculo
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
RX packets:35096 errors:0 dropped:0 overruns:0 frame:0
TX packets:6982 errors:0 dropped:0 overruns:0 carrier:0
colisiones:0 txqueuelen:1000
RX bytes:2871409 (2.7 MB) TX bytes:955840 (933.4 KB)
Dirección base: 0xecc0 Memoria:fe100000-fe120000

eth0:0 Link encap:Ethernet direcciónHW 00:c0:9f:1d:75:62
inet dirección:10.50.1.200 Difusión:10.50.15.255 Máscara:255.255.240.0
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
Dirección base: 0xecc0 Memoria:fe100000-fe120000

eth0:1 Link encap:Ethernet direcciónHW 00:c0:9f:1d:75:62
inet dirección:10.50.2.230 Difusión:10.50.15.255 Máscara:255.255.240.0
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
Dirección base: 0xecc0 Memoria:fe100000-fe120000

eth1 Link encap:Ethernet direcciónHW 00:00:d1:f0:01:44
inet dirección:172.16.0.1 Difusión:172.16.0.4 Máscara:255.255.255.252
dirección inet6: fe80::200:d1ff:fef0:144/64 Alcance:Vínculo
ARRIBA DIFUSIÃN CORRIENDO MULTICAST MTU:1500 Métrica:1
RX packets:3578 errors:0 dropped:0 overruns:0 frame:0
TX packets:4111 errors:0 dropped:0 overruns:0 carrier:0
colisiones:0 txqueuelen:1000
RX bytes:1012689 (988.9 KB) TX bytes:1215957 (1.1 MB)
Interrupción:20



Tenemos la dirección de Caronte y estamos dando servicio.

Ya tenemos dos nodos en estado activo/activo dando servicio, para añadir recursos estudiar la forma en la que se configuraron en el archivo cib.xml y reproducirlo para cada recurso nuevo que se quiera introducir.

Los recursos deben confirmar el estandar LSB o el OCF, y el nodo los buscará en /etc/ha.d/resources.d, en /usr/lib/ocf/resources.d/ o en /etc/init.d.

Alta disponibilidad en Linux con Heartbeat

Esta información me ha costado bastante recopilarla, ya que hay poca documentación al respecto.

Lo que pretendía en un principio era montar dos máquinas en alta disponibilidad y en estado activo/activo, es decir, dos máquinas con una dirección IP cada una y con servicios diferentes corriendo en cada una de ellas. De tal forma que si cualquiera de las dos máquinas se viene abajo la otra toma posesión de la dirección IP de la máquina caida y de los recursos que estaban corriendo en ella. Conseguimos de esta forma tener uno de los sistemas más fiables ya que no se deja de dar servicio en ningún momento.

Hearbeat en su versión 2 creo que nos permite tener hasta 16 nodos, pero como a mi me sobraba con 2 no estoy seguro de eso. Permite tener un cluster de máquinas repartiendo la carga de trabajo entre varias o tener una maquina trabajando y otra en stand by para entrar en servicio en cuanto se caiga la de trabajo o tener varias máquinas trabajando a su bola y cuando se cae alguna repartir los recursos entre las otras. Todo esto está explicado en la página de Heartbeat.

Mi problema surge nada más empezar, en la página de Heartbeat los ejemplos son para activo/pasivo, y lo único que hay de activo/activo es un trozo de xml sin explicación.

Tras googlear y googlear me entero de que la versión 2 de Heartbeat integra otro sistema, o quizá sea al revés, pero el caso es que la información había que buscarla en otro lado, osease, en Pacemaker que está en el siguiente enlace: http://www.clusterlabs.org/wiki/Main_Page. Ahí encontraremos como configurar el archivo cib.xml que es donde está todo el meollo.

Básicamente lo que hay que hacer es lo siguiente:

Configurar el archivo ha.cf y el authkeys que se encuentran en /etc/ha.d. Una vez configurado al gusto (la siguiente entrada en el blog contendrá un ejemplo) se copiarán en todos los nodos del cluster. Y tras esto deberemos configurar el archivo cib.xml que se encuentra en /var/lib/heartbeat/crm. La información para configurarlo se encuentra en Pacemaker.

En la siguiente entrada del blog pondré un ejemplo de como crear nuestro cluster HA para dos máqinas en estado activo/activo.