INND: Cómo montar tu propio servidor de news privado

Estas son mis peripecias sobre cómo instalar el servidor de “Usenet” más completo y eficiente: el INND. La instalación es bajo Linux en CentOS / RedHat.

Le dediqué unos días al tema después de observar que algunas empresas siguen utilizando el protocolo NNTP (para servidor de news o newsserver) como una alternativa sencilla a foros privados para ser utilizados en intranets y similares.

También resulta una alternativa a mantener una “lista de correo” con archivo de mensajes, ya que si configuramos adecuadamente el servidor de news, simplemente hacemos que los mensajes no caduquen nunca, y obtenemos como resultado un perfecto historial de todos los mensajes para cualquier nuevo usuario o empleado que se incorpore a la empresa, por ejemplo.

Innd tiene fama de ser difícil de instalar. El origen del programa es tan antiguo que desgraciadamente conserva las premisas de “lee el manual y sigue las instrucciones”, en lugar de incluir un script de instalación que nos ahorre el trabajo más mecánico.

El paquete se sigue actualizando, pero la documentación disponible en Internet añade la dificultad de variados cambios de la configuración entre las distintas versiones.

Destacar que aquí sólo voy a explicar los pasos necesarios para montar un newsserver privado con las siguientes características:

  1. Sólo montaremos “grupos de noticias locales”,
  2. en los que los mensajes no caduquen nunca,
  3. con autenticación de usuarios (sólo un grupo de usuarios determinado podrá conectar y por tanto participar).
  4. comprobaremos los usuarios usando un script Perl que accedera por MySQL a una tabla de usuarios nuestra.
  5. Nuestro servidor NO va a recoger news de servidores remotos, será totalmente “privado”.

Instalación y configuración

Instalamos el paquete RPM usando yum:

# yum install inn

Como el programa es muy estricto en cuanto a permisos, cambiamos al usuario “news”, y así siempre que tengamos que modificar algo de la configuración:

# su - news

Esto nos dejará en el directorio home del usuario, que es /etc/news -donde residen todos los archivos de configuración-.

Antes de ser poder arrancar el servidor por primera vez sin que nos aparezcan errores muy poco descriptivos -obligándonos a leer el manual de instalación- debemos hacer una serie de tareas de configuración.

Establecemos el método de almacenamiento en el archivo storage.conf, añadiendo las siguientes líneas:

method tradspool {
 newsgroups: *
 class: 0
}

Añadimos las reglas de “lectores” para grupos locales, o quién puede acceder a los grupos, en el archivo readers.conf:

auth "local" {
    hosts: "*"
    default: "@somewhere.com"
}
access "local" {
    users: "*"
    newsgroups: "*" 
}

Esto permite conexión desde cualquier host, en principio a todos los usuarios, a todos los grupos locales.

En realidad, donde hemos puesto newsgroups: “*” no deberíamos dejarlo así, sino poner algo como misgrupos.* si esa fuera la jerarquía que queremos para nuestros grupos locales. De lo contrario los usuarios verán los grupos internos de administración, lo cual no interesa.

Después creamos la “base de datos de historial” o database history, con estos comandos:

# cd /var/lib/news
# makedbz -i
# chmod 664 *n*
# mv history.n.dir history.dir
# mv history.n.hash history.hash
# mv history.n.index history.index

Ahora vamos a configurar que no caduque ningún mensaje, o que no expiren. Editamos el archivo /etc/news/expire.ctl para que contenga la regla:

*:A:never:never:never

Esta regla es “ningún mensaje expira en ningún grupo”. La A significa los grupos de cualquier tipo (moderados o no).

En el archivo motd.news podemos poner un mensaje de bienvenida tipo FTP. No pasa nada si el archivo no existe o está vacío.

En el archivo subscriptions podríamos poner la lista de grupos más habituales, utilizada por algunos lectores de noticias. De momento podemos dejarlo en blanco.

Los mensajes de control

Como nuestro servidor de news va a ser privado, no vamos a necesitar el uso de mensajes de control para borrar mensajes, solicitar nuevos grupos de noticias, etc. Todo eso puede hacerlo el administrador mediante línea de comandos.

Podemos editar el archivo control.ctl y dejar únicamente la línea:

all:*:*:drop

Esta regla tiene la sintaxis: message:from:newsgroups:action
De este modo *todos* los mensajes de control (administración) son ignorados (drop), ya que all es un código especial que compara cualquier mensaje de control.

El archivo principal de configuración

Vamos con el archivo principal de configuración: inn.conf en el que afinaremos varios asuntos.

nnrpdposthost: (EN BLANCO)
server: localhost

Dejando las opciones anteriores así, podemos acelerar la conexión de los lectores de noticias, ya que accederán directamente al modo lector (MODE READER) o módulo NNRP cuando se conecten a INND.

Es decir, aparecería algo parecido a esto, en una conexión telnet:

# telnet localhost 119
[...]
200 xxx InterNetNews NNRP server INN 2.3.5 ready (posting ok).

Si no ajustamos la configuración como hemos comentado antes, observaríamos que la conexión sería algo como:

# telnet localhost 119
[...]
200 xxx InterterNetNews server INN 2.3.5 ready.
MODE READER <--- tendríamos que pasar primero a "modo lector"
200 xxx InterNetNews NNRP server INN 2.3.5 ready (posting ok).

Seguimos con la configuración de inn.conf:

Las conexiones máximas que aceptará el servidor.

maxconnections:  250

Dejaremos en blanco el campo organization, que es para añadir una cabecera por defecto al mensaje cuando alguien envía un mensaje:

organization:

Deberíamos activar (poner a true) todas las líneas de la configuración donde aparezca la palabra “mmap“, en muchos sitios desactivado, para compatibilidad con muchos sitemas antiguos. Activarlo en sistemas modernos resulta en una optimización:

articlemmap:            true
tradindexedmmap:        true

Y ahora, varios settings para afinar (siempre buscar o añadir en el archivo, según corresponda):

clienttimeout:     180
nnrpdcheckart:     false
nnrpdloadlimit:    16
complaints:        email@domain.tld
nnrpdauthsender:   true
strippostcc:       true
datamovethreshold: 32768

Con la opción nnrpdauthsender los mensajes enviados generarán una cabecera Sender: que incluirá la identidad del usuario (por ejemplo el username si usamos autenticación). La opción nnrpdcheckart a false nos permite optimizar las respuestas del servidor, al ser privado y no necesitar esta comprobación. La opción nnrpdloadlimit permite rechazar conexiones si la carga del servidor o Load Average sube al valor indicado. La opción complaints es el email de la cabecera X-Complaints-To: para los mensajes “posteados”. Con strippostcc eliminamos los campos To: Cc: y Bcc: de todos los posts (ahorraremos espacio). Y finalmente el datamovethreshold es una optimización

Arrancando el servidor y creando grupos

Ahora ya podemos ver si todo arranca bien. Como root hacer:

# service innd start

Si vamos jugando con la configuración, debemos volver al usuario news y reiniciar el daemon de inn:

# su - news
# service innd restart

Si queremos dejar el servidor para inicio automático en el arranque:

# chkconfig innd on 

Para administrar el servidor usamos ctlinnd. Podemos crear nuestro primer grupo local “misgrupos.anuncios”, por ejemplo:

# /usr/lib/news/bin/ctlinnd newgroup misgrupos.anuncios

Y probamos el server con telnet (o nuestro lector de noticias). La sesión podría ser algo como:

# telnet localhost 119
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
200 xxxx.com InterNetNews server INN 2.3.5 ready
MODE READER
200 xxxx.com InterNetNews NNRP server INN 2.3.5 ready (posting ok).
LIST
215 Newsgroups in form "group high low flags".
misgrupos.anuncios 0000000000 0000000001 y

Y si todo va bien, en principio ya podemos postear, leer, etc. El problema es que “todo dios” en Internet va a poder acceder a tu servidor “privado”, y lo que es peor, enviar mensajes…

Activando la autenticación de usuarios

Cuando tengamos el server funcionando podemos trastear con la autenticación de usuarios en innd. Esto es lo más “complicado” debido a los cambios entre versiones.

En principio para algo simple, podemos activar la siguiente opción en el archivo inn.conf:

nnrpperlauth:      true

Esto básicamente activa la autenticación a través de un hook de Perl, a nivel global. Cuando el servidor de news arranque, enganchará el archivo /usr/lib/news/bin/filter/nnrpd_auth.pl y lo utilizará para autenticar cualquier uso del módulo NNRP (lectura y envío de noticias).

Ojo porque activar esto hace que el archivo readers.conf se ignore por completo en la versión 2.3.5 de INN. Sólo en las versiones de INN 2.4.x se puede combinar un perl_hook con otros scripts en readers.conf

Archivo nnrpd_auth.pl de ejemplo

Dejo aquí un script para realizar autenticación en innd con MySQL desde Perl. Lo escribí basado en otros ejemplos, y me ha funcionado bien. Lleva instrucciones tanto en inglés como en español y explica bastante todo el proceso de autenticación, realizando además correctamente la devolución del código de respuesta correspondiente a “Autenticación requerida” si el usuario y contraseña están en blanco.

Conviene realizar una copia de seguridad del archivo original:

# cd /usr/lib/news/bin/filter
# mv nnrpd_auth.pl nnrpd_auth.pl.orig

El código del script a continuación:

#! /usr/local/bin/perl
## save this as: /usr/lib/news/bin/filter/nnrpd_auth.pl
##
##  INN perl_auth example (MySQL) written for toma2tazas 2009
##  INN 2.3.5
##
##  Requires: DBI
##  Requires: nnrpperlauth: true (in inn.conf)
##  > does setting this to true totally make irrelevant the
##  > information in readers.conf file?
##  Yes.  If you would like to combine perl authentication and readers.conf,
##  you could try using the 2.4 version of INN
#
# This file is loaded when nnrpd starts up. If it defines a sub named
# `authenticate', then that function will be called during processing of a
# connect, auth request or disconnect.  Attributes about the connection are
# passed to the program in the %attributes global variable.  It should return
# an array with 5 elements:
#
# 1) NNTP response code.  Should be one of the codes from %connectcodes or %authcodes
# 2) Reading Allowed. Should be a boolean value.
# 3) Posting Allowed. Should be a boolean value.
# 4) Wildmat expression that says what groups to provide access to.
# 5) Maximum bytes per second a client is permitted to use for retrieving
#    articles
#
# All five of these are required.  If there is a problem with them then nnrpd
# will die and syslog the exact reason.
$defaultgroups = "misgrupos*";
require "/usr/lib/news/lib/innshellvars.pl";
use DBI;
my (%connectcodes) = ("read/post" => 200, "read" => 201, "authneeded" => 480, "permdenied" => 502);
my (%authcodes)    = ("allowed" => 281, "denied" => 502);
# This is called by nnrpd when it first starts up.
sub auth_init {
}
# This is called when a user connects or authenticates
sub authenticate
{
  my $key;
  foreach $key (keys %attributes) {
  }
  if ($attributes{type} eq "connect") {
  # return AUTHNEEDED code
   return ($connectcodes{authneeded}, "false", "false", $defaultgroups, "16384");
  }
  elsif ($attributes{type} eq "disconnect") {
   # do nothing
  }
  elsif ($attributes{type} eq "authenticate") {
     ## Cambiar aquí los datos de conexión necesarios
     $DBI::sql = DBI->connect('DBI:mysql:database=databasename;host=domain.com', 'username', 'password')
       or return($authcodes{denied}, "false", "false", $defaultgroups, "16384");
     my $user = $attributes{'username'};
     my $pass = $attributes{'password'};
    # Se deberia filtrar / securizar $user y $pass
     return ($authcodes{denied}, "false", "false", $defaultgroups, "16384")
         unless length($user) > 0;
## Sustituir el query por la comprobación de usuario necesaria
## $query="SELECT login FROM users WHERE login='$user' AND pword=SHA1('$salt$pass') LIMIT 1";
##   my $sql_res = $DBI::sql->prepare($query)
##   or return($authcodes{denied}, "false", "false", $defaultgroups, "16384");
     $sql_res->execute();
     my @rows=();
     if ($sql_res->rows ne 0) {
       my @ary=();
       while (@ary = $sql_res->fetchrow_array()) {
          push(@rows, [@ary]);
       }
     }
     $sql_res->finish();
     $DBI::sql->disconnect();
     if ($rows[0][0] ne lc($user))
     {
    return ($authcodes{denied}, "false", "false", $defaultgroups, "16384");
     }
     return ($authcodes{allowed}, "true", "true", $defaultgroups, "16384");
   }
   return 502;
}

 

Para ver si está todo OK y nuestro newsserver privado ya no deja a “cualquiera” acceder, probamos con telnet y forzaremos un usuario y contraseña inválidos:

# telnet localhost 119
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
200 xxxx.com InterNetNews server INN 2.3.5 ready
MODE READER
200 xxxx.com InterNetNews NNRP server INN 2.3.5 ready (posting ok).
LIST
480 Authentication required for command
AUTHINFO USER usuariomalo
381 PASS required
AUTHINFO PASS passwordmalo
502 Authentication error
Connection closed by foreign host.

Vale, parece que nuestro servidor ya exige credenciales. Probemos con un usuario y contraseña válidos…

# telnet localhost 119
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
200 xxxx.com InterNetNews server INN 2.3.5 ready
MODE READER
200 bigguy.sowsl.com InterNetNews NNRP server INN 2.3.5 ready (posting ok).
AUTHINFO USER  usuariobueno
381 PASS required
AUTHINFO PASS passwordbueno
281 Ok
list
215 Newsgroups in form "group high low flags".
misgrupos.anuncios 0000000000 0000000001 y
.
quit
205 .
Connection closed by foreign host.
 

Como detalle interesante, comentar que podemos usar el siguiente filtro de perl: /usr/lib/news/bin/filter/filter_nnrpd.pl para procesar cualquier mensaje (post) enviado al servidor de news, para manipular sus cabeceras (no nos dejan tocar el contenido del mensaje, por seguridad).

Otra documentación y recursos

Finalmente dejo una lista de los enlaces que me sirvieron de guía y ayuda en los varios días en que estuve trasteando con el asunto.

Algunos programas:

PHP News Reader: el cliente de protocolo NNTP en PHP más completo y bien escrito que pude encontrar.
Simple Little Usenet (News Server) Test: Un programa simple para verificar el funcionamiento de nuestro newsserver
WendzelNNTPd: Un servidor de news minimalista para Windows y Linux. No es lo más eficiente del mundo, pero nos ayuda a tener un servidor funcionando en muy poco tiempo para comprender mejor su funcionamiento y el protocolo.

Varios tutoriales (ojo, muchos son antiguos, pero contienen información útil:

How to Install and Configure INN under RedHat Linux 7.2
An Introduction to INN (NNTP SERVER)
How to Set Up a News Server
INN for the Impatient
INN: Examples on perl_auth
Linux Network Administrators Guide – Installing INN
Installing INN
NNRPD External Authentication Support
INN Docs: readers.conf
Hacking INN: de la documentación oficial
PHP: imap_mail – Manual (for NNTP post): Podemos usar la función imap_mail() de PHP para hacer un post NNTP

Esta entrada fue publicada en Linux, Servidores. Guarda el enlace permanente.

Deja un comentario