Configurar PAM para utilizar kerberos
En artículos anteriores se explicó cómo funciona kerberos y la forma de utilizarlo en GNU/Linux. Las workstations con Linux instalado pueden autenticar sus usuarios contra servidores de este tipo, ya sea que los mismos utilicen Linux, Windows (Active Directory), o cualquier otro SO que implemente el estándar. La configuración descripta a continuación es la misma sin importar sobre qué plataforma se implemente el servicio kerberos. Es decir, se puede utilizar, por ejemplo, para autenticar clientes Linux contra el servicio Active Directory de Windows.

Para la autenticación con kerberos, hay que instalar el módulo de PAM correspondiente, en todos los clientes. En debian, el paquete se denomina libpam-krb5:
# apt-get install krb5-{config,user} libpam-krb5
Durante la instalación, debconf requerirá algunos datos para continuar:
- el nombre del realm: DEMASIADOVIVO.ORG
- servidores kerberos para el realm, separados con espacios.
- servidor administrativo para el realm, para administrar cambio de contraseña.
Estos son los mismos datos que configuramos al instalar el servidor kerberos. Toda esta configuración se puede cambiar en el archivo /etc/krb5.conf.
En este archivo, como se explicó en la instalación del servidor, pueden eliminar todos los realms que aparecen en la instalación default (athena, mit, etc), y deberán agregar el dominio de su realm en la sección domain_realm:
[domain_realm]
.demasiadovivo.org = DEMASIADOVIVO.ORG
demasiadovivo.org = DEMASIADOVIVO.ORG
Luego de que se instala el módulo de kerberos, debian automáticamente establece en PAM que la autenticación de usuarios debe utilizar este protocolo. Si no están utilizando debian, tal vez requieran una configuración adicional para empezar a utilizarlo.

Veamos entonces cómo deberían quedar cada uno de los archivos de PAM, y el significado de cada configuración:

/etc/pam.d/common-account
account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
account requisite pam_deny.so
account required pam_permit.so
account required pam_krb5.so minimum_uid=1000
Se puede observar que este archivo es muy similar a cuando se utiliza sólo autenticación Unix. Para agregar la autenticación de kerberos, se agrega una línea final que invoca el módulo pam_krb5 con el argumento minimum_uid=1000.
En este caso, el sistema invoca tanto el módulo pam_unix como pam_krb5 como required, lo cual implica que si alguno de estos falla, la operación falla. El argumento "minimum_uid=1000" hace que la autenticación kerberos se aplique a los usuarios cuyo user ID sea mayor a 1000. Esto permite tener cuentas locales como la de root (uid=0), la cual no autentica contra kerberos.

/etc/pam.d/common-auth
auth [success=2 default=ignore] pam_krb5.so minimum_uid=1000
auth [success=1 default=ignore] pam_unix.so nullok_secure try_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
En este archivo podemos ver como funciona la autenticación propiamente dicha. Primero se intenta autenticar con kerberos a los usuarios cuyo uid es mayor o igual a 1000. Si esto tiene éxito, se salta a la línea pam_permit (success=2) y el usuario puede entrar. Si la autenticación kerberos falla, se intenta con la autenticación unix tradicional, permitiendo password en blanco (nullok_secure), y utilizando el mismo password que se utilizó en la autenticación kerberos (try_first_pass). Esto último se hace para que el sistema no requiera el password dos veces cuando la autenticación kerberos falla. Si ninguna de las autenticaciones es exitosa, se niega el acceso (pam_deny).

/etc/pam.d/common-pasword
password requisite pam_krb5.so minimum_uid=1000
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass sha512
password requisite pam_deny.so
password required pam_permit.so
Para el cambio de password se utiliza como requisito pam_krb5 con uid mayor igual a 1000. Si esto falla, el password no se actualiza. Si el password kerberos se actualiza correctamente, se intenta actualizar el password local, utilizando el mismo pass ingresado para kerberos (use_authtok). El resto de los argumentos de pam_unix son los mismos que cuando se utiliza autenticación tradicional, donde obscure checkea fortaleza del pass, y sha512 es el algoritmo para hashear el pass.
Algo a tener en cuenta aquí es que si la actualización del password local falla, la operación falla aunque el password kerberos no haya tenido problemas.

/etc/pam.d/common-session
session [default=1] pam_permit.so
session requisite pam_deny.so
session required pam_permit.so
session optional pam_krb5.so minimum_uid=1000
session required pam_unix.so
Finalmente en el archivo de session encontramos una regla adicional y opcional que es la de kerberos. Algo interesante que podemos hacer en este archivo es agregar una regla para la creación del directorio home del usuario.
Si creamos un usuario en kerberos y no en la máquina cliente, cuando éste intente ingresar, el sistema arrojará error si el home no existe, porque este directorio no se crea al agregar el usuario.
Para esto, existe un módulo denominado pam_mkhomedir. Este módulo crea el directorio home del usuario cuando éste se loguea por primera vez. Si el directorio ya existe, no hace nada. Un argumento interesante para este módulo es skel, que permite especificar el esqueleto del directorio home.
Podemos modificar el archivo anterior para agregar esta funcionalidad, de la siguiente manera:
session [default=1] pam_permit.so
session requisite pam_deny.so
session required pam_permit.so
session required pam_mkhomedir.so skel=/etc/skel/
session optional pam_krb5.so minimum_uid=1000
session required pam_unix.so
Con esto, la próxima vez que se autentique un usuario en el cliente configurado, éste utilizará kerberos en lugar del tradicional sistema Unix. Claro que el usuario a utilizar debe estar previamente creado en el servidor kerberos.
Una vez autenticados, se puede ver el ticket generado ejecutando klist:
$ klist
Ticket cache: FILE:/tmp/krb5cc_1000_s6uumi
Default principal: demasiadovivo@DEMASIADOVIVO.ORG

Valid starting Expires Service principal
09/24/11 08:08:07 09/24/11 18:07:57 krbtgt/DEMASIADOVIVO.ORG@DEMASIADOVIVO.ORG
renew until 09/24/11 18:08:07
Bug en script perl al intentar actualizar debian testing
Al intentar actualizar la librería libc6 en debian testing (wheezy) encontré un problema, el cual ocasionó que todo el sistema quede inestable. apt ciclaba arrojando el error:
Use of uninitialized value $text in concatenation (.) or string at /usr/share/perl5/Text/WrapI18N.pm line 101, line 5. substr outside of string at /usr/share/perl5/Text/WrapI18N.pm line 130, line 5.
Como el proceso no se detenía, la solución fue matarlo, pero dejando varias inconsistencias en el camino.Al parecer el script en cuestión tiene un bug con una variable sin inicializar, lo que ocasiona que el proceso de actualización falle.
Al quedar libc6 roto, muchos paquetes del sistema quedaron rotos y no es posible instalar ni borrar nada hasta que el problema no se arregle. Para salvar el problema y poder terminar la instalación, mi solución fue comentar la línea problemática (101 del archivo /usr/share/perl5/Text/WrapI18N.pm):
$text = $top2 . $c . $text;
Claro que si la línea esta ahí, para algo es, pero preferí tener un problema menor a tener un sistema que posiblemente al reiniciar no arrancaría.
Una vez comentada la línea hay que reparar los paquetes rotos, lo cual se realiza con:
apt-get install --fix-broken
Al haber matado el proceso apt, puede que algún proceso haya retenido los archivos de lock y que apt se reuse a actualizar. Si este es el caso, ejecutar fuser para obtener el id de los procesos que quedaron colgados, y luego matarlos:
fuser -v /var/lib/dpkg/lock
fuser -v /var/cache/debconf/config.dat
Con esto pude terminar la instalación de libc6 sin problemas y el sistema volvió a un estado estable. Según lo que encontré en diferentes foros, este es un bug del paquete perl y varios que han intentado actualizar lo han sufrido:
http://www.linuxquestions.org/questions/debian-26/error-configuring-libc6-898757/
http://www.facepunch.com/threads/1116926-Error-when-trying-to-dist-upgrade
Por ello me pareció interesante postear mi solución, que si bien no es elegante, me salvó de una reinstalación completa. Ojalá los salve a ustedes también!
Armar servidor de logging con rsyslog y configurar dispositivos para logging remoto
Todo sistema operativo cuenta con funcionalidad para almacenar logs del sistema, aplicaciones, dispositivos, etc. Los sistemas tipo *nix cuentan con el demonio syslog, encargado de recoger los logs y almacenarlos en el lugar indicado. El servicio syslog es tan potente que, además de realizar la tarea de logging local, permite almacenar logs de dispositivos remotos a través de un protocolo propio llamado del mismo nombre y que funciona sobre UDP y TCP. La mayoría de los sistemas operativos poseen facilidades para almacenar logs en un servidor remoto.
Centralizar los logs en un servidor facilita la tarea de análisis de logs a administradores y auditores, ya que deben acceder a sólo un repositorio.
La obvia desventaja es que si hackean el servidor de logs, pueden ver los logs de toda la red. Por ello es muy importante que el servidor de logging posea una buena configuración de seguridad. Igualmente, como se detallará al final del artículo, existen varias desventajas de seguridad adicionales al utilizar este esquema.
Los sistemas *nix modernos, utilizan una versión mejorada del antiguo syslog denominada rsyslog, más confiable que el anterior y con capacidades extendidas. La siguiente explicación muestra cómo armar un servidor de logging con rsyslog, que puede aplicarse a cualquier sistema *nix. Además se incluyen los comandos para habilitar el logging remoto en dispositivos Cisco.
Toda la configuración de rsyslog se realiza a través del archivo /etc/rsyslog.conf.


Servidor rsyslog

Para convertir el demonio rsyslog en un servidor de loggeo de red debe utilizarse el plugin imudp (si se desea utilizar UDP) y/o imtcp (para TCP). Estos plugins reemplazan la opción -r de syslog ya obsoleta. El port utilizado por defecto por este servicio es el 514 para ambos protocolos.
Las líneas que se deben agregar (o descomentar) en /etc/rsyslog.conf para habilitar el logging remoto son las siguientes:
$ModLoad imudp
$UDPServerRun 514

$ModLoad imtcp
$InputTCPServerRun 514

Para que la nueva configuración entre en vigencia, debe recargarse el demonio. Una manera de no cortar el logging de forma abrupta matando el proceso (clásico restart), lo mejor es enviar una señal HungUp:
killall -HUP rsyslogd

Con esto, cualquier dispositivo de red puede enviar sus logs al servidor recién configurado, y este los añadirá a los locales del servidor. Claro que lo mejor es no mezclar logs locales con logs remotos, por lo cual se pueden configurar distintas entradas en la configuración que redirijan estos logs a otros archivos.
Las reglas para separar logs se arman en base al facility y al nivel de criticidad que va de 0 (emergencias) a 7 (debugging). Las facilities permiten indicar el origen del mensaje, y existe un número fijo de ellas. Algunos demonios y procesos del sistema operativo tienen asignados valores de facilities (por ej: kern, mail, daemon, user, etc), y aquellos que no, deben utilizar alguna de las definidas como "uso local" (local0 a local7). El usuario puede utilizar las facilities local0 a local7 para sus logs, y estas son las que se suelen utilizar para separar logs de dispositivos remotos.
El formato de las reglas es:
<facility>.<criticidad> <archivo log destino (ej /var/log/syslog)>
Tanto el facility como la criticidad pueden ser asterisco (*), indicando que abarque todo. Por ejemplo, "kern.*" envía todos los logs del kernel al archivo especificado. En el caso de utilizar asterisco en el facility, es posible utilizar la prioridad none para indicar qué facilities no incluir. Por ejemplo, existe la siguiente regla default en la configuración de rsyslog:
*.*;auth,authpriv.none -/var/log/syslog
la cual hace que todo se envíe al archivo syslog. En esa misma regla se puede observar que se agregó auth,authpriv.none para evitar que los logs de auth y authpriv se almacenen en dicho archivo. A la regla anterior se puede agregar local4.none, para luego utilizar la facility local4 en dispositivos remotos y así separar esos logs de los locales:
*.*;auth,authpriv.none;local4.none -/var/log/syslog

Además de las reglas "facility.priority", se pueden armar reglas en base a filtros, basados en distintas propiedades como IP origen, mensaje, hostname, y varias más que se pueden ver en la sección de propiedades del manual oficial de rsyslog.
Las reglas con filtros se arman de la siguiente manera:
:<propiedad>, <operación de comparación>, "valor" <archivo log destino (ej /var/log/syslog)>
donde la operación de comparación puede ser isempty, isequal, startswith, regex y eregex, como se puede observar en el manual oficial, en la sección de filtros.
Esto permite, por ejemplo, separar los logs provenientes de la dirección IP 192.168.1.15 en un archivo propio para el host con la siguiente regla:
:fromhost-ip, isequal, "192.168.1.15" /var/log/remote/192.168.1.15.log

Una vez completadas las reglas, debe recargarse el demonio rsyslogd para que tome la nueva configuración.


Separación de logs

Conociendo las bases para la configuración del servidor, es posible armar una que reciba logs de diferentes fuentes y los separe en distintos archivos.
Dado que por defecto todo va a parar a syslog, lo mejor es eliminar las facilities local* de dicha configuración y después utilizarlas en los dispositovs de red. El primer paso es editar la regla correspondiente (en /etc/rsyslog.conf) para que quede como sigue:
*.*;auth,authpriv.none;\
local0.none;\
local1.none;\
local2.none;\
local3.none;\
local4.none;\
local5.none;\
local6.none;\
local7.none -/var/log/syslog
También hay que evitar que info, notice y warning terminen en /var/log/messages
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none;
local0.none;\
local1.none;\
local2.none;\
local3.none;\
local4.none;\
local5.none;\
local6.none;\
local7.none -/var/log/messages
y que los mensajes de debugging terminen en /var/log/debug
*.=debug;\
auth,authpriv.none;\
news.none;mail.none;
local0.none;\
local1.none;\
local2.none;\
local3.none;\
local4.none;\
local5.none;\
local6.none;\
local7.none -/var/log/debug
La separación se puede realizar de varias maneras, una es basándose en las facilities. Para ello, a cada tipo de dispositivo se asigna una facility de las "local use" y luego se arma la configuración en base a esto, de la siguiente manera:
local3.* /var/log/remote/firewalls.log
local4.* /var/log/remote/routers.log
local5.* /var/log/remote/APs.log
local6.* /var/log/remote/switches.log
Como se ve, se utiliza local3 para firewalls, loca4 para routers, local5 para access points y local6 para switches. A esta separación se la puede mejorar utilizando distintos archivos para diferentes prioridades, pero sigue siendo muy simple y no demasiado agradable para quien revisa los logs.

Una mejor forma de separar logs es colocar los logs de cada host en un archivo separado. En la sección anterior se mostró que es posible ver de qué dirección IP viene la información y armar un archivo en base a esto, pero es muy ineficiente colocar una regla por cada host. Una forma más simple de hacerlo es utilizar templates. Con un template es posible definir el formato del nombre del archivo log a partir de alguna propiedad del mismo, como por ejemplo el hostname o IP. Para usar una propiedad es necesario colocarla entre signos de porcentaje "%".
La siguiente línea crea un template que luego se puede utilizar para especificar el nombre del archivo:
$template LogsRemotos,"/var/log/remote/%fromhost-ip%.log"
donde fromhost-ip especifica que se utilice la IP como nombre del archivo.
Para lograr que cada dispositivo loggee en un archivo separado, sólo debe agregarse lo siguiente:
:fromhost-ip, isequal, "127.0.0.1" ~
*.* -?LogsRemotos
y con ello rsyslog generará automáticamente los nombres de archivos y almacenará los mensajes en el lugar correcto. En la primer línea se descartan los logs locales, dado que los mismos se almacenan en otros archivos y no deseamos generar uno nuevo. El descarte se realiza utilizando caracter "~" en lugar de un nombre de archivo. El signo de pregunta "?" de la segunda línea indica que se utiliza un nombre de archivo dinámico, a partir del template LogsRemotos, y el guión "-" que no escriba en el archivo inmediatamente (buffering), para mejorar la performance.

Para no "ensuciar" el archivo de configuración global, es posible crear archivos dentro de /etc/rsyslog.d/ con las configuraciones deseadas. En el último ejemplo, sería posible tener un archivo denominado remote.conf con las líneas:
$template LogsRemotos,"/var/log/remote/%fromhost-ip%.log"

:fromhost-ip, isequal, "127.0.0.1" ~
*.* -?LogsRemotos


Configuracion en dispositivos Cisco con IOS

Todos los dispositivos Cisco poseen la capacidad de loggear en un servidor remoto a través de syslog. La configuración de los mismos es muy simple, sólo debe indicarse la dirección del servidor, el nivel de información a enviar y el facility a utilizar. Esto se lleva a cabo en el modo de configuración con privilegios de administrador, ejecutando los siguientes comandos (lo que se encuentra luego del // son comentarios):
(config)#logging on
(config)#logging host <IP syslog server>
(config)#logging trap informational // es el nivel de mensajes a loguear. Cada nivel incluye a todos los niveles inferiores (informational incluye error, critical, alerts, etc).
(config)#logging facility local5 // usar el facility local5 (local0... local7 son para códigos definidos por el usuario).


Configuración de clientes *nix

Lograr que un máquina que ejecuta algún sistema *nix loggee remotamente es muy simple. Sólo debe agregarse la línea mostrada a continuación en su archivo /etc/rsyslog.conf (o /etc/syslog.conf si no posee rsyslog):
*.* @<IP remota>
El @ indica que deben enviarse los mensajes a un host remoto en lugar de almacenarlos en un archivo local. Con un sólo @ rsyslog utiliza UDP como protocolo de transporte, y si se desea utilizar el más confiable TCP deben colocarse dos:
*.* @@<IP remota>


Restringir el acceso

El protocolo no posee un mecanismo de autenticación que restrinja qué dispositivo puede almacenar logs en el servidor, permitiendo que cualquiera pueda escribir en el mismo, con el riesgo de que un atacante genere un Denial of Service si el disco se llena o la cantidad de datos transmitida satura el ancho de banda.
Una forma de limitar el acceso es utilizando un firewall y rechazar toda conexión que no venga de una dirección de red autorizada. Esta mejora no es la panacea, dado que suplantar una dirección de red es muy simple, pero al menos es algo.


Conclusiones

rsyslog es un sistema muy flexible, simple y consolidado. El protocolo se encuentra en prácticamente todo dispositivo decente, por lo cual la compatibilidad está garantizada. Lo que se describió en este artículo muestra sólo una parte de la potencia de esta herramienta, por lo cual se recomienda una atenta lectura de su manual oficial.
Probablemente su problema más grave es la pobre seguridad que provee. Entre algunos de los problemas más graves se encuentran:
  • Posibilidad de DoS, como se explicó en la sección anterior.
  • UDP es poco confiable: los paquetes se pueden perder y nadie se enteraría. syslog por TCP mejora este aspecto, pero no todos los dispositivos lo implementan.
  • No hay autenticación, permitiendo que cualquiera envíe logs al servidor.
  • No existe confidencialidad de los datos enviados.
  • No hay integridad de los datos (alguien podría modificarlos en el camino - MITM)
Algunos de estos problemas pueden solucionarse si se utiliza IPSec en la capa inferior, pero no es una solución que forme parte del protocolo.


Referencias

- rsyslog doc - rsyslog.conf configuration file
- Syslog Configuration and /etc/syslog.conf
- How To Set Up A Debian Linux Syslog Server
- FreeBSD Handbook - 29.11 Remote Host Logging with syslogd
- Catalyst 2950 - Configuring System Message Logging
- Practical UNIX & Internet Security - 10.5 The UNIX System Log (syslog) Facility
- Softpanorama - Centralised Syslog Server
- PIX 501 Logging to Syslog Server
Interfaz wireless desactivada al utilizar power injector en APs Cisco
Cuando se utiliza el AP con power injector instalado, puede suceder que la interfaz wireless se encuentre deshabilitada. Esto se puede observar en los logs del dispositivo ejecutando el comando:
#show logging
El error que se encontrará será similar al siguiente:
*Feb 28 21:00:10.622 GMT-3: %LINEPROTO-5-UPDOWN: Line protocol on Interface Dot11Radio0, changed state to down
*Feb 28 21:01:09.497 GMT-3: %CDP_PD-2-POWER_LOW: All radios disabled - LOW_POWER_CLASSIC_NO_INJECTOR_CONFIGURED WS-C2924-XL (00d0.bbd6.1b4e)
*Feb 28 21:01:09.497 GMT-3: -Verify the required power-injector is installed on this port: WS-C2924-XL(Fas 0/13).
*Feb 28 21:01:09.497 GMT-3: -If a power-injector is installed, issue the command:"power inline negotiation injector installed"
En este caso, el AP indica que no cuenta con la energía necesaria y por eso deshabilita la interfaz wireless. El problema es que no detecta que se encuentra energizado mediante power injector.

Para solucionar el problema, el AP necesita que se explicite la MAC address del port del switch en el que se encuentra conectado el power injector. Dicha MAC se puede observar en el log anterior, en la línea:
%CDP_PD-2-POWER_LOW: All radios disabled - LOW_POWER_CLASSIC_NO_INJECTOR_CONFIGURED WS-C2924-XL (00d0.bbd6.1b4e)
La dirección en que el injector se encuentra conectado se debe ingresar a través de la configuración del switch, ejecutando el siguiente comando:
(config)#power inline negotiation injector 00d0.bbd6.1b4e

Luego de esperar un minuto o dos, la interfaz de radio debería encontrarse habilitada nuevamente. Para checkearlo se puede ejecutar el comando:
#show interfaces dot11Radio 0
y la respuesta debe ser:
Dot11Radio0 is up, line protocol is up