Inn & Suck

Ziel ist es, einen Newsserver trotz Wählverbindung mit dynamischer IP-Adressvergabe zu betreiben. Die Gründe hierfür sind vielfältig:

Software

Vorbereitung

Inn läßt man am besten unter einem eigenen Gruppen- und Benutzeraccount laufen; da ich strikt zwischen normalen Benutzern und Systemaccounts trenne, werden der Gruppe und dem Benutzer nntp explizit eine GID bzw. UID zugewiesen. Außerdem benötigen wir noch ein Verzeichnis (vorzugsweise eine eigene Partition auf einer seperaten Festplatte), unter dem die Newsdatenbank und die einzelnen Inn-Programme installiert werden.
Also:

$ su
$ groupadd -g 20 news
$ useradd -u 20 -g news -d /var/news -s /bin/bash news
$ mkdir /var/news
$ mount /var/news           # sofern nicht in /etc/fstab
$ chown news.news /var/news

Compilieren

Konfigurationsdateien erwarte ich unter /etc, Logdaten unter /var/log und Manpages in /usr/share/man. Daher konfigurieren wir Inn wie folgt:

$ ./configure --prefix=/var/news --sysconfdir=/etc/news \
  --mandir=/usr/share/man --with-etc-dir=/etc/news \
  --with-log-dir=/var/log/news --with-run-dir=/var/run/news \
  --with-sendmail=/var/qmail/bin/qmail-inject
$ make
$ su -c "make install"

Die Option --with-perl funktioniert scheinbar nicht mit Perl 5.6.1 und Threadsupport. Statt qmail kann man als Mailer natürlich auch smtppipe :-) oder jedes andere Programm nehmen, das Mails von STDIN lesen bzw. verschicken kann.
Analog wird Suck installiert:

$ ./configure --prefix=/var/news --sysconfdir=/etc/news --mandir=/usr/share/man
$ make
$ su -c "make install"

Konfiguration

Jegliche Arbeiten am Newssystem sollten als User news vorgenommen werden, um nicht Dateien schreibgeschützt als root anzulegen oder zu verändern.
Zunächst legen wir fest, welche Hosts ausschließlich als Newsreader auf den Server zugreifen dürfen, hier also localhost und das lokale Netz (192.168.0.0/255.255.255.0):

$ cat >/etc/news/readers.conf
auth localhost {
    hosts: localhost,127.0.0.1,stdin
    default: localhost
}

auth lan {
    hosts: 192.168.0.0/24
    default: lan
}

access all {
    users: localhost,lan
    newsgroups: *
    access: RPA
}

In /etc/news/incoming.conf wird festgelegt, wer unseren Server per Newsfeed füttern darf; da wir ja suck verwenden werden, welches erst Artikel von anderen Newsservern pollt und dann per IHAVE; in unseren Server einspielt, tragen wir lediglich die (private) IP-Adresse ein, auf der der innd lauschen wird (hier: 192.168.0.1):

$ cat >/etc/news/incoming.conf
streaming:              true
max-connections:        1

peer 192.168.0.1 {
}

In der Datei storage.conf legen wir fest, wie die einzelnen Artikel gespeichert werden; wenn man ohnehin eine eigene Partition für /var/news eingerichtet hat, kann man durchaus auf das herkömmliche Spoolverfahren zurückgreifen:

$ cat >/etc/news/storage.conf
method tradspool {
  newsgroups: *
  class: 1
}

Um ein Überlaufen der Newsdatenbank zu vermeiden, müssen überreife Artikel gelöscht werden. Dies kontrollierte Entfernen wird mit der Datei expire.ctl gesteuert:

$ cat >/etc/news/expire.ctl
# Message-ID eines Artikels min. noch 10 Tage
# nach seinem Löschen vorhalten
/remember/:10
# Artikel in allen Spool-Klassen und allen Newsgruppen
# min. 7 Tage vorhalten (auch wenn ein Expire-Datum 
# im Header schon überschritten ist)
# nach 30 Tagen "normale" Artikel löschen
# nach max. 60 Tagen auch Artikel löschen, deren
# Expire-Datum noch nicht abgelaufen ist
*:A:7:30:60

Die oft als kompliziert verrufene Datei newsfeeds regelt, welche Newsserver die auf unserem eigenen Server geposteten Artikel bekommen. Jede Zeile dieser Datei besteht aus vier durch Doppelpunkte getrennten Feldern. Das erste Feld bezeichnet den entfernten Server, das zweite die Gruppen, deren Artikel der Server von uns erhalten soll. Das dritte und vierte Feld spezifizieren zusätzliche Parameter, wie z.B. die weiterzuleitenden Artikel beschaffen sein müssen; sie interessieren hier allerdings nicht weiter.

$ cat >/etc/news/newsfeeds
ME:!*::
news.provider.com:de.*,owl.*,pbinfo.*::

Der ME-Eintrag hat eine besondere Bedeutung: Alle Werte des zweiten, dritten und vierten Feldes werden als default in den entsprechenden Feldern aller anderen Zeilen eingesetzt. Sollte der entfernte Newsserver nicht seinen Hostnamen (news.provider.com) in die Path-Zeile der Artikel schreiben, so muß dieses in der newsfeeds durch einen Slash im ersten Feld bekannt gemacht werden. Schließlich wollen wir nicht Artikel an news.provider.com schicken, die wir von ihm gepollt haben:

$ cat >/etc/news/newsfeeds
ME:!*::
# news.provider.com setzt rechner1.provider.com
# in der Path:-Zeile ein
news.provider.com/rechner1.provider.com:de.*,owl.*,pbinfo.*::

Wenn nun ein Artikel auf unserem Server in eine Gruppe unterhalb von de., owl. oder pbinfo. gepostet wird, dann notiert inn dies in der Datei /var/news/spool/outgoing/news.provider.com. Wer möchte, kann in /etc/news/motd noch einen mehr oder weniger sinnvollen Spruch, Hinweis, etc. schreiben, der dann beim LIST MOTD-Kommando ausgegeben wird.
Vor dem ersten Start des Newsserver besorgen wir uns ein aktuelles active-File, sprich eine Liste aller auf dem entfernten Server geführten Gruppen. In /etc/news/actsync.ign trägt man die Gruppen ein, die man überhaupt in sein eigenes active-File aufnehmen möchte. Auf jeden Fall sollten dies die control-Gruppen sein:

$ cat >/etc/news/actsync.ign
# alles ignorieren bis auf:
# control*, de.* owl.* pbinfo.*
i *
c control
c control.*
c de.*
c owl.*
c pbinfo.*

Danach stoßen wir actsync einmalig an:

$ actsync -b 12 -d 12 -i /etc/news/actsync.ign -I 2 -m -o x -p 0 news.provider.com

Sinnvoll ist es auch, die Gruppenbeschreibungen zu übernehmen:

$ getlist -h news.provider.com newsgroups | grep '^control\|^de.\|^owl.\|^pbinfo.' | sort >/var/news/db/newsgroups

Abschließend richten wir als Benutzer news noch einige cron-Jobs ein (crontab -e). Das Skript news.daily aus dem Inn-Paket erledigt diverse Routineaufgaben, darunter das Löschen abgelaufener Artikel und Versenden von Logfileauswertungen an den Usenet-Admin:

$ crontab -l
# jeden tag um 23 Uhr news.daily starten
0 23 * * * /var/news/bin/news.daily

Das Senden und Empfangen von Artikeln kann man entweder mit einem weiteren cron-Job oder in /etc/ppp/ip-up erledigen. Auf jeden Fall sollte man sich ein Skript erstellen, das rpost und suck ansteuert. Ferner braucht man noch einen Filter, der zum einen aus den Tokens in /var/news/spool/outgoing/news.provider.com die zugehörigen Artikel generiert und an rpost übergibt. Zum anderen sollten Headerzeilen wie NNTP-Posting-Host, X-Trace, Xref usw. vor dem Posten aus den Artikeln gelöscht werden. Ein simples Filterskript in Perl:

#!/usr/bin/perl
# /var/news/bin/rpost-filter.pl

$token = $ARGV[0];
$outfile = $ARGV[1];

$scanning_header = 1;

die $! if !open(FH,">$outfile");

# storage manager liefert Artikel zum Token
@article = `/var/news/bin/sm '$token'`;

# Headerzeilen entfernen
foreach (@article) {
  s/\r|\n//g;
  $scanning_header = 0 if ($_ eq "");
  if ($scanning_header) {
    if (!( (/^nntp-posting-host:/i) ||
           (/^x-trace:/i) ||
           (/^x-complaints-to:/i) ||
           (/^nntp-posting-date:/i) ||
           (/^xref:/i) )) {
      print FH "$_\r\n";
    }
  }
  else {
    print FH "$_\r\n";
  }
}

close (FH);

Das Shellskript, welches als cron-Job bzw. in /etc/ppp/ip-up eingerichtet wird:

#!/bin/sh
# /var/news/bin/suckpost.sh

NEWS=/var/news
NEWSGROUPS=$NEWS/db/newsgroups
FILTER=$NEWS/bin/rpost.pl
TMP=$NEWS/tmp/$$.art

post () {
  SERVER=$1
  OUTGOING=$NEWS/spool/outgoing/$SERVER

  mv $OUTGOING $OUTGOING.old
  ctlinnd flush $SERVER
  rpost $SERVER -n -d -b $OUTGOING.old -f $FILTER \$\$o=$TMP \$\$i $TMP
  rm -f $TMP

  suck $SERVER -bp -c -dd /etc/news -dm /tmp -dt /var/news/tmp -HF /var/news/db/history -hl 192.168.0.1 -n -m -p .$SERVER
}

post "news.provider.com"

Letztlich brauchen wir noch eine /etc/news/sucknewsrc.news.provider.com, in der die eigentlich zu pollenden Gruppen stehen. Jede Zeile dieser Datei besteht aus drei durch Leerzeilen getrennte Felder: Das erste Feld gibt den Gruppennamen an, das zweite die Nummer des jüngsten Artikels auf dem entfernten Server und das optionale dritte Feld gibt an, wie viele Artikel suck maximal in einem Poll downloaden darf. Initialisiert wird eine sucknews.rc wie folgt:

control 0 2000
control.newgroup 0 2000
de.test 0 2000
de.comp.os.unix.linux.misc 0 2000

Mehrere Polls

Vor mehreren entfernten Newsservern wird man hauptsächlich dann pollen, wenn nicht alle Gruppen auf einem Newsserver vorhanden sind, wie das zum Beispiel bei einigen lokalen Gruppen der Fall ist. Natürlich sollten dann Postings in solche ausgefallenen Gruppen nur an den entsprechenden Server gehen. Also muss /etc/news/newsfeeds erweitert werden:

$ cat >/etc/news/newsfeeds
ME:!*::
news.provider.com:de.*,owl.*,pbinfo.*::
news.uni-woauchimmer.org:uni-intern.*::

Postings in die Hierachie uni-intern.* gehen zukünftig nur an news.uni-woauchimmer.org. Natürlich sollte auch das active-File mit dem des weiteren Newsservers gemergt werden. Wir erweitern /etc/news/actsync.ign um den Eintrag

c uni-intern.*

und rufen actsync wie oben erneut auf. Ebenso brauchen wir alle Gruppenbeschreibungen; dazu nehmen wir mergelist aus dem inntools-Paket:

$ (getlist -h news.provider.com newsgroups; getlist -h news.uni-woauchimmer.org newsgroups) | mergelist > /var/lib/news/newsgroups

Letztlich müssen noch das Skript /var/news/bin/suckpost.sh angepasst und /etc/news/sucknewsrc.news.uni-woauchimmer.org angelegt werden. Wir erweitern /var/news/bin/suckpost.sh um die Zeile

post "news.uni-woauchimmer.org"

und initialisieren /etc/news/sucknewsrc.news.uni-woauchimmer.org:

control 0 2000
control.newgroup 0 2000
uni-intern.physik 0 2000

Clients

Clients (aka Newsreader) sollten ebenfalls rpost zum Absetzen von Artikeln auf unserem Server verwenden; inews ist IMHO ungeeignet, da es unter anderem redundante Headerzeilen schreibt.

related topics

see my inntools

Download (deprecated, use my inntools instead)

suckpost.sh
rpost-filter.pl