Fortaleciendo nuestras contraseñas

Si una de las promesas que tiene para este cierre de año es fortalecer las contraseñas en sus equipos personales, cambiarlas mensualmente y no repetir la misma contraseña en al menos doce cambios. En este artículo se le explicará como hacerlo sin tener que invertir una uva en ello, todo esto gracias al paquete libpam-cracklib en Debian, el procedimiento mostrado debe aplicarse a otras distribuciones derivadas de Debian.

Pareciese lógico que algunas de las mejores prácticas para el fortalecimiento de las contraseñas son las siguientes:

  • Cambiar las contraseñas periódicamente.
  • Establecer una longitud mínima en las contraseñas.
  • Establecer buenas reglas para las nuevas contraseñas, es decir, mezcla entre letras mayúsculas, minúsculas, dígitos y caracteres alfanuméricos.
  • Mantener un histórico de las contraseñas usadas previamente, de ese modo, alentamos a los usuarios establecer nuevas contraseñas.
  • Indicarle a los usuarios que es inaudito que se anoten las contraseñas en un post-it y se dejen pegadas en los monitores o incluso en las gavetas de sus archivadores.

El primer paso es instalar el paquete libpam-cracklib

# apt-get install libpam-cracklib

A partir de la versión 1.0.1-6 de PAM se recomienda manejar la configuración vía pam-auth-update. Por lo tanto, por favor tome un momento y lea la sección 8 del manual del comando pam-auth-update para aclarar su uso y ventajas.

$ man 8 pam-auth-update

Ahora establezca una configuración similar a la siguiente, vamos primero con la exigencia en la fortaleza de las contraseñas, para ello edite o cree el fichero /usr/share/pam-configs/cracklib.

Name: Cracklib password strength checking
Default: yes
Priority: 1024
Conflicts: unix-zany
Password-Type: Primary
Password:
	requisite	pam_cracklib.so retry=3 minlen=8 difok=3
Password-Initial:
	requisite	pam_cracklib.so retry=3 minlen=8 difok=3

NOTA: Le recomiendo leer la sección 8 del manual de pam_cracklib para encontrar un mayor numero de opciones de configuración. Esto es solo un ejemplo.

En versiones previas el modulo pam_cracklib hacia uso del fichero /etc/security/opasswd para conocer si la propuesta de cambio de contraseña no había sido utilizada previamente. Dicha funcionalidad ahora corresponde al nuevo modulo pam_pwhistory

Definamos el funcionamiento de pam_pwhistory a través del fichero /usr/share/pam-configs/history.

Name: PAM module to remember last passwords
Default: yes
Priority: 1023
Password-Type: Primary
Password:
	requisite	pam_pwhistory.so use_authtok enforce_for_root remember=12 retry=3
Password-Initial:
	requisite	pam_pwhistory.so use_authtok enforce_for_root remember=12 retry=3

NOTA: Para mayor detalle de las opciones puede revisar la sección 8 del manual de pam_pwhistory

Seguidamente proceda a actualizar la configuración de PAM vía pam-auth-update.

pam-auth-update

Una vez cubierta la fortaleza de las contraseñas nuevas y de evitar la reutilización de las ultimas 12, de acuerdo al ejemplo mostrado, resta cubrir la definición de los periodos de cambio de las contraseñas.

Para futuros usuarios debemos ajustar ciertos valores en el fichero /etc/login.defs

#
# Password aging controls:
#
#       PASS_MAX_DAYS   Maximum number of days a password may be used.
#       PASS_MIN_DAYS   Minimum number of days allowed between password changes.
#       PASS_WARN_AGE   Number of days warning given before a password expires.
#
PASS_MAX_DAYS   30
PASS_MIN_DAYS   0
PASS_WARN_AGE   5

Las reglas previas no aplicaran para los usuarios existentes, pero para este tipo de usuarios podremos hacer uso del comando chage de la siguiente manera:

# chage -m 0 -M 30 -W 5 ${user}

Donde el valor de ${user} debe ser reemplazo por el username.

2 min read

apt-get detrás de proxy con autenticación NTLM

Por motivos que no vienen al caso discutir en este artículo tuve que instalar Debian GNU/Linux detrás de un proxy que aún utiliza NTLM como medio de autenticación, aunque NTLM ya no es recomendado por Microsoft desde hace años en pro de usar Kerberos.

Una vez instalada la distribución quería utilizar apt-get para actualizarla e instalar nuevos paquetes, el resultado fue que apt-get no funciona de manera transparente detrás de un proxy con autenticación NTLM. La solución fue colocar un proxy interno que esté atento a peticiones en un puerto particular en el host, el proxy interno se encargará de proveer de manera correcta las credenciales al proxy externo.

La solución descrita previamente resulta sencilla al utilizar cntlm. En principio será necesario instalarlo vía dpkg, posteriormente deberá editar los campos apropiados en el fichero /etc/cntlm.conf

  • Username
  • Domain
  • Password
  • Proxy

Seguidamente reinicie el servicio:

# /etc/init.d/cntlm restart

Ahora solo resta configurar apt-get para que utilice nuestro proxy interno, para ello edite el fichero /etc/apt.conf.d/02proxy

Acquire::http::Proxy "http://127.0.0.1:3128";

NOTA: Se asume que el puerto de escucha de cntlm es el 3128.

Ahora puede hacer uso correcto de apt-get:

# apt-get update
# apt-get upgrade
...

NOTA FINAL: Es evidente que cualquier comando o herramienta que necesite autenticarse contra el proxy externo deberá configurarlo para que utilice el proxy interno, lo explicado en este artículo no solo aplica para el comando apt-get.

1 min read

Enviando correos con Perl

Regularmente los administradores de sistemas requieren notificar, vía correo electrónico, a sus usuarios de ciertos cambios o nuevos servicios disponibles. La experiencia me ha indicado que el usuario aprecia más un correo personalizado que uno general. Sin embargo, lograr lo primero de manera manual es bastante tedioso e ineficaz. Por lo tanto, es lógico pensar en la posibilidad de automatizar el proceso de envío de correos electrónicos personalizados, en este artículo, explicaré una de las tantas maneras de lograrlo haciendo uso del lenguaje de programación Perl.

En CPAN podrá encontrar muchas alternativas, recuerde el principio TIMTOWTDI. Sin embargo, la opción que más me atrajo fue MIME::Lite:TT, básicamente este módulo en Perl es un wrapper de MIME::Lite que le permite el uso de plantillas, vía Template::Toolkit, para el cuerpo del mensaje del correo electrónico. También puede encontrar MIME::Lite::TT::HTML que le permitirá enviar correos tanto en texto sin formato (MIME::Lite::TT) como en formato HTML. Sin embargo, estoy en contra de enviar correos en formato HTML, lo dejo a su criterio.

Una de las ventajas de utilizar Template::Toolkit para el cuerpo del mensaje es separar en capas nuestra script, si se observa desde una versión muy simplificada del patrón MVC, el control de la lógica de programación reside en el script en Perl, la plantilla basada en Template Toolkit ofrecería la vista de los datos, de modo tal que podríamos garantizar que la presentación está separada de los datos, los cuales pueden encontrarse desde una base de datos o un simple fichero CSV. Otra ventaja evidente es el posible reuso de componentes posteriormente.

Un primer ejemplo del uso de MIME::Lite:TT puede ser el siguiente:

#!/usr/bin/perl

use strict;
use warnings;
use MIME::Lite::TT;

my %options;
$options{INCLUDE_PATH} = '/home/jdoe/example';

my %params;
$params{first_name} = "Milton";
$params{last_name}  = "Mazzarri";
$params{username}   = "milmazz";
$params{groups}     = "sysadmin";

my $msg = MIME::Lite::TT->new(
    From        => '[email protected]',
    To          => '[email protected]',
    Charset     => 'utf8',
    TimeZone    => 'America/Caracas',
    Subject     => 'Example',
    Template    => 'example.txt.tt',
    TmplOptions => \%options,
    TmplParams  => \%params,
);

$msg->send();

Y el cuerpo del correo electrónico, lo que en realidad es una plantilla basada en Template::Toolkit, vendría definido en el fichero example.txt.tt de la siguiente manera:

Hola [% last_name %], [% first_name %].

Tu nombre de usuario es [% username %].

Un saludo, feliz día.

Su querido BOFH de siempre.

En el script en Perl mostrado previamente podemos percatarnos que los datos del destinario se encuentran inmersos en la lógica. Por lo tanto, el siguiente paso sería desacoplar esta parte de la siguiente manera:

#!/usr/bin/perl

use strict;
use warnings;
use MIME::Lite::TT;
use Class::CSV;

my %options;
$options{INCLUDE_PATH} = '/home/jdoe/example';

# Lectura del fichero CSV
my $csv = Class::CSV->parse(
    filename       => 'example.csv',
    fields         => [qw/last_name first_name username email/],
    csv_xs_options => { binary => 1, }
);

foreach my $line ( @{ $csv->lines() } ) {
    my %params;

    $params{first_name} = $line->first_name();
    $params{last_name}  = $line->last_name();
    $params{username}   = $line->username();

    my $msg = MIME::Lite::TT->new(
        From        => '[email protected]',
        To          => $line->email(),
        Charset     => 'utf8',
        TimeZone    => 'America/Caracas',
        Subject     => 'Example',
        Template    => 'example.txt.tt',
        TmplOptions => \%options,
        TmplParams  => \%params,
    );

    $msg->send();
}

Ahora los datos de los destinarios los extraemos de un fichero en formato CSV, en este ejemplo, el fichero en formato CSV lo hemos denominado example.csv.

Cabe aclarar que $msg->send() realiza el envío por medio de Net::SMTP y podrá usar las opciones que se describen en dicho módulo. Sin embargo, si necesita establecer una conexión SSL con el servidor SMTP es oportuno recurrir a Net::SMTP::SSL:

#!/usr/bin/perl

use strict;
use warnings;
use MIME::Lite::TT;
use Net::SMTP::SSL;
use Class::CSV;

my $from = '[email protected]';
my $host = 'mail.example.com';
my $user = 'jdoe';
my $pass = 'example';

my %options;
$options{INCLUDE_PATH} = '/home/jdoe/example';

# Lectura del fichero CSV
my $csv = Class::CSV->parse(
    filename       => 'example.csv',
    fields         => [qw/last_name first_name username email/],
    csv_xs_options => { binary => 1, }
);

foreach my $line ( @{ $csv->lines() } ) {
    my %params;

    $params{first_name} = $line->first_name();
    $params{last_name}  = $line->last_name();
    $params{username}   = $line->username();

    my $msg = MIME::Lite::TT->new(
        From        => $from,
        To          => $line->email(),
        Charset     => 'utf8',
        TimeZone    => 'America/Caracas',
        Subject     => 'Example',
        Template    => 'example.txt.tt',
        TmplOptions => \%options,
        TmplParams  => \%params,
    );

    my $smtp = Net::SMTP::SSL->new( $host, Port => 465 )
      or die "No pude conectarme";
    $smtp->auth( $user, $pass )
      or die "No pude autenticarme:" . $smtp->message();
    $smtp->mail($from)                 or die "Error:" . $smtp->message();
    $smtp->to( $line->email() )        or die "Error:" . $smtp->message();
    $smtp->data()                      or die "Error:" . $smtp->message();
    $smtp->datasend( $msg->as_string ) or die "Error:" . $smtp->message();
    $smtp->dataend()                   or die "Error:" . $smtp->message();
    $smtp->quit()                      or die "Error:" . $smtp->message();
}

Note en este último ejemplo que la representación en cadena de caracteres del cuerpo del correo electrónico viene dado por $msg->as_string.

Para finalizar, es importante mencionar que también podrá adjuntar ficheros de cualquier tipo a sus correos electrónicos, solo debe prestar especial atención en el tipo MIME de los ficheros que adjunta, es decir, si enviará un fichero adjunto PDF debe utilizar el tipo application/pdf, si envía una imagen en el formato GIF, debe usar el tipo image/gif. El método a seguir para adjuntar uno o más ficheros lo dejo para su investigación ;)

4 min read

Construyendo de manera efectiva y rápida imágenes ISO de Debian con jigdo

Si usted desea el conjunto de CD o DVD para instalar Debian, tiene muchas posibilidades, desde la compra de los mismos, muchos de los vendedores contribuyen con Debian. También puede realizar descargas vía HTTP/FTP, vía torrent o rsync. Pero en este artículo se discutirá sobre un método para construir las imágenes ISO de Debian de manera eficiente, sobretodo si cuenta con un repositorio local de paquetes, dicho método se conoce de manera abreviada como jigdo o Jigsaw Download.

Las ventajas que ofrece jigdo están bien claras en el portal de Debian, cito:

¿Por qué jigdo es mejor que una descarga directa?

¡Porque es más rápido! Por varias razones, hay muchas menos réplicas para imágenes de CDs que para el archivo «normal» de Debian. Consecuentemente, si descarga desde una réplica de imágenes de CD, esa réplica no sólo estará más lejos de su ubicación, además estará sobrecargada, especialmente justo después de una publicación.

Además, algunos tipos de imágenes no están disponibles para descarga completa como .iso porque no hay suficiente espacio en nuestros servidores para alojarlas.

Considero que la pregunta pertinente ahora es: ¿Cómo descargo la imagen con jigdo?.

En primer lugar, instalamos el paquete jigdo-file.

# aptitude install jigdo-file

Mi objetivo era generar los 2 primeros CD para Debian Lenny, para la fecha de publicación de este artículo la versión más reciente es la 5.0.7. La lista de imágenes oficiales para jigdo las puede encontrar acá.

milmazz@manaslu /tmp $ cat files
http://cdimage.debian.org/debian-cd/5.0.7/i386/jigdo-cd/debian-507-i386-CD-1.jigdo
http://cdimage.debian.org/debian-cd/5.0.7/i386/jigdo-cd/debian-507-i386-CD-1.template
http://cdimage.debian.org/debian-cd/5.0.7/i386/jigdo-cd/debian-507-i386-CD-2.jigdo
http://cdimage.debian.org/debian-cd/5.0.7/i386/jigdo-cd/debian-507-i386-CD-2.template
milmazz@manaslu /tmp $ wget -c -i files
--2010-12-02 12:39:52--  http://cdimage.debian.org/debian-cd/5.0.7/i386/jigdo-cd/debian-507-i386-CD-1.jigdo
Resolving cdimage.debian.org... 130.239.18.163, 130.239.18.173, 2001:6b0:e:2018::173, ...
Connecting to cdimage.debian.org|130.239.18.163|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 31737 (31K) [text/plain]
Saving to: `debian-507-i386-CD-1.jigdo'

100%[===================================================================================================================>] 31.737      44,7K/s   in 0,7s

...

FINISHED --2010-12-02 12:50:15--
Downloaded: 4 files, 27M in 10m 21s (44,7 KB/s)
milmazz@manaslu /tmp $ ls
debian-507-i386-CD-1.jigdo  debian-507-i386-CD-1.template  debian-507-i386-CD-2.jigdo  debian-507-i386-CD-2.template files

Una vez descargados los ficheros necesarios, es hora de ejecutar el comando jigdo-lite, siga las instrucciones del asistente.

milmazz@manaslu ~ $ jigdo-lite debian-507-i386-CD-2.jigdo

Jigsaw Download "lite"
Copyright (C) 2001-2005  |  jigdo@
Richard Atterer          |  atterer.net
Loading settings from `/home/milmazz/.jigdo-lite'

-----------------------------------------------------------------
Images offered by `debian-507-i386-CD-2.jigdo':
1: 'Debian GNU/Linux 5.0.7 "Lenny" - Official i386 CD Binary-2 20101127-16:55 (20101127)' (debian-507-i386-CD-2.iso)

Further information about `debian-507-i386-CD-2.iso':
Generated on Sat, 27 Nov 2010 17:02:14 +0000

-----------------------------------------------------------------
If you already have a previous version of the CD you are
downloading, jigdo can re-use files on the old CD that are also
present in the new image, and you do not need to download them
again. Mount the old CD ROM and enter the path it is mounted under
(e.g. `/mnt/cdrom').
Alternatively, just press enter if you want to start downloading
the remaining files.
Files to scan:

El comando despliega información acerca de la imagen ISO que generará, en este caso particular, debian-507-i386-CD-2.iso. Además, jigdo-lite puede reutilizar ficheros que se encuentren en CD viejos y así no tener que descargarlos de nuevo. Sin embargo, este no era mi caso así que presione la tecla ENTER.

-----------------------------------------------------------------
The jigdo file refers to files stored on Debian mirrors. Please
choose a Debian mirror as follows: Either enter a complete URL
pointing to a mirror (in the form
`ftp://ftp.debian.org/debian/'), or enter any regular expression
for searching through the list of mirrors: Try a two-letter
country code such as `de', or a country name like `United
States', or a server name like `sunsite'.
Debian mirror [http://debian.example.com/debian/]:

En esta fase jigdo-lite solicita la dirección URL completa de un repositorio, aproveche la oportunidad de utilizar su repositorio local si es que cuenta con uno. Luego de presionar la tecla ENTER es tiempo de relajarse y esperar que jigdo descargue todos y cada uno de los ficheros que componen la imagen ISO.

Luego de descargar los paquetes y realizar las operaciones necesarias para la construcción de la imagen ISO jigdo le informará los resultados.

FINISHED --2010-12-01 14:43:50--
Downloaded: 6 files, 2,5M in 1,8s (1,39 MB/s)
Found 6 of the 6 files required by the template
Successfully created `debian-507-i386-CD-2.iso'

-----------------------------------------------------------------
Finished!
The fact that you got this far is a strong indication that `debian-507-i386-CD-2.iso'
was generated correctly. I will perform an additional, final check,
which you can interrupt safely with Ctrl-C if you do not want to wait.

OK: Checksums match, image is good!

Ahora bien, haciendo uso de un repositorio local, es bueno preguntarse en cuanto tiempo aproximadamente puedes construir tu imagen ISO, en mi caso el tiempo de construcción de debian-507-i386-CD-2.iso fue de:

milmazz@manaslu ~ $ time jigdo-lite debian-507-i386-CD-2.jigdo

...

real	8m35.704s
user	0m13.101s
sys	0m16.569s

Nada mal, ¿no les parece?.

Ahora bien, haciendo uso de un repositorio local, es bueno preguntarse en cuanto tiempo aproximadamente puedes construir tu imagen ISO, en mi caso el tiempo de construcción de debian-507-i386-CD-2.iso fue de:

Referencias

4 min read

Generar reporte en formato CSV de tickets en Trac desde Perl

El día de hoy recibí una llamada telefónica de un compañero de labores en donde me solicitaba con cierta preocupación un “pequeño” reporte del estado de un listado de tickets que recién me había enviado vía correo electrónico puesto que no contaba con conexión a la intranet, al analizar un par de tickets me dije que no iba a ser fácil realizar la consulta desde el asistente que brinda el mismo Trac. Así que inmediatamente puse las manos sobre un pequeño script en Perl que hiciera el trabajo sucio por mí.

Es de hacer notar que total de tickets a revisar era el siguiente:

$ wc -l tickets
126 tickets

Tomando en cuenta el resultado previo, era inaceptable hacer dicha labor de manera manual. Por lo tanto, confirmaba que realizar un script era la vía correcta y a la final iba a ser más divertido.

Tomando en cuenta que el formato de entrada era el siguiente:

#3460
#3493
...

El formato de la salida que esperaba era similar a la siguiente:

3460,"No expira la sesión...",closed,user

Básicamente el formato implica el id, sumario, estado y responsable asociado al ticket.

Net::Trac le ofrece una manera sencilla de interactuar con una instancia remota de Trac, desde el manejo de credenciales, consultas, revisión de tickets, entre otros. A la vez, se hace uso del módulo Class::CSV el cual le ofrece análisis y escritura de documentos en formato CSV.

#!/usr/bin/perl

use warnings;
use strict;

use Net::Trac;
use Class::CSV;

# Estableciendo la conexion a la instancia remota de Trac
my $trac = Net::Trac::Connection->new(
    url      => 'http://trac.example.com/project',
    user     => 'user',
    password => 'password'
);

# Construccion del objecto CSV y definicion de opciones
my $csv = Class::CSV->new(
    fields         => [qw/ticket sumario estado responsable/],
    line_separator => "\r\n",
    csv_xs_options => { binary => 1, }    # Manejo de caracteres non-ASCII
);

# Nos aseguramos que el inicio de sesion haya sido exitoso
if ( $trac->ensure_logged_in ) {
    my $ticket = Net::Trac::Ticket->new( connection => $trac );

    # Consultamos cada uno de los tickets indicados en el fichero de entrada
    while ( my $line = <> ) {
        chomp($line);
        if ( $line =~ m/^#\d+$/ ) {
            $line =~ s/^#(\d+)$/$1/;
            $ticket->load($line);

            $csv->add_line(
                {
                    ticket      => $ticket->id,
                    sumario     => $ticket->summary,
                    estado      => $ticket->status,
                    responsable => $ticket->owner
                }
            );
        }
        else {
            print "[INFO] La linea no cumple el formato requerido: $line\n";
        }
    }
    $csv->print();
}
else {
    print "No se pudieron asegurar las credenciales";
}

La manera de ejecutar el script es la siguiente:

$ perl trac_query.pl tickets

En donde trac_query.pl es el nombre del script y tickets es el fichero de entrada.

Debo aclarar que el script carece de comentarios, mea culpa. Además, el manejo de opciones vía linea de comandos es inexistente, si desea mejorarlo puede hacer uso de Getopt::Long.

Cualquier comentario, sugerencia o corrección es bienvenida.

2 min read