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...