Appliance im Eigenbau

Aus CCC Bremen
ALT Diese Seite ist eine Altlast! Sie könnte veraltete Informationen enthalten.

Netzwerkapplicance im Eigenbau

Im Bereich der DSL-Router hat man als Kunde ja heute eine recht umfangreiche Auswahl, teils soagr mit NAS und anderen netten Funktionen. Leider sind die Firewalls in der Regel nicht übermäßig konfigurierbar, und zusätzliche Funktionen sind nicht unbedingt immer so ganz sicher.

Ich habe mir bislang damit beholfen, einen ASUS Router mit OpenWRT zu betreiben. Allerdings stößt hier die Hardware bald an ihre Grenzen; vor allem dann, wenn man das Gerät auch für andere Zwecke benutzen will. So habe ich z.B. meine privaten E-Mails auf dem Server, so daß ich von überall her meine E-Mail abrufen kann. Der Versuch Spamassassin zum laufen zu kriegen scheitert allerdings an mangelndem Hauptspeicher, der Versuch einen TOR-Knoten aufzubauen an der Prozessorleistung.

Ich habe daher beschlossen, mir meine eigene Appliance zu bauen, die alle die Dienste anbietet, die ich gerne haben möchte. Da sicher noch mehr Leute an so einer Anwendung interessiert sind, möchte ich hier im Wiki meine einzelnen Arbeitsschritte dokumentieren, damit man die Appliance nachbauen kann.

Anforderungen

  • Leise
  • Geringer Stromverbrauch
  • Platzsparend
  • NAS-Laufwerk
  • Mail-/IMAP Server
  • VPN-Endpunkt
  • Sicherheit
  • Erweiterbarkeit
  • Darknet-Knoten (zu diesem Thema mache ich demnächst einen eigenen Beitrag, soviel sei aber verraten: Die Appliance soll auch dafür Daten aufnehmen)

Hardware

Der erste Schritt ist natürlich die Beschaffung der richtigen Hardware. Ein ausgedienter PC tut es natürlich auch, der erfüllt aber nicht unsere Anforderungen an den Stromverbrauch und die Lärmentwicklung (bei mir steht das Ding im Wohnzimmer). Ich habe mich entschieden, eine Mini-ITX Lösung einzusetzen, da diese mehrere Vorteile hat:

  • Preiswert
  • Boards sind fertig bestückt (nur RAM fehlt)
  • relativ viel Prozessorleistung für's Geld
  • volle Kompatibilität zu x86 Hardware == große Softwareauswahl

Zur Zeit (Oktober 2007) das beste Preis-/Leistungsverhältnis habe ich bei Reichelt-Elektronik gefunden (danke Gwenn): Das Intel D201GY mit Celeron (1.33GHz) Prozessor, on-board Graphik und Sound kostet derzeit €62,97. Dazu habe ich ein DDR2-533 RAM Modul mit 1GB gekauft (€19,95). Als Gehäuse habe ich mich für das CUBID CP2699 entschieden. Es geht zwar noch etwas kleiner, aber man kann in dieses gehäuse auch eine 3.5" HDD einbauen (ist billiger), und es hat einen PCI-Slot nach aussen geführt. Bei dem Gehäuse muß man allerdings darauf achten, daß das Netzteil keinen 4-Pin 12V Stecker hat, um den Prozessor mit Strom zu versorgen (die Intel-Mini-ITX-Boards haben meist einen 2x10 und einen 2x2 Stromanschluß, statt des bei ATX üblichen 2x12 Anschlusses). Ein kleiner Adapter löst dieses Problem aber leicht.

Da das ganze ja unter anderem eine Firewall sein soll, sollte man eine zweite Netzwerkkarte einbauen. Ich habe eine simple REALTEK 8139 basierte Karte genommen. Die wird von Linux prima unterstützt, und kostet nur 5€. Wer sich noch den Switch sparen will, kauft eine Karte mit eingebautem Switch. Da gibt's aber gerne mal Probleme mit der Unterstützung durch Linux, weil derartige Karten oft ziemlich exotische Chipsätze haben. Da ich mich da nicht so gut auskenne, habe ich lieber zu einer Lösung gegriffen, von der ich weiß, daß sie geht. Input zum Thema ist natürlich willkommen.

Den Einbau des Boards zu erklären, spar ich mir mal. Wer dieses Wiki liest, weiß vermutlich eh' wie man das mit verbundenen Augen macht. ;-)

Installation

Als Betriebssystem habe ich mich für Gentoo-Linux entschieden, auch wenn das ein bischen mehr Arbeit beim installieren macht. Der Vorteil ist, daß man sich Gentoo so zurechtschneidern kann, wie man es gerade braucht, ohne überflüssigen Ballast mitzuschleppen. Für eine Appliance ein unbestreitbarer Vorteil.

Da unsere Appliance kein CD-Laufwerk hat müssen wir zunächst ein Gerät zum Booten einrichten. Am bequemsten geht das mit einem USB Stick. Hier gibt es eine ausführliche Dokumentation wie man den vorbereitet. Bevor von dem Stick gebootet werden kann, muß im BIOS des Intel-Boards unter "Advanced" die Option "USB-Bootable" auf "Enabled" gestellt werden.

Danach vom USB Stick booten. Die Installation läuft wie im Gentoo-Handbuch beschrieben ab. Wer es etwas bequemer mag, kann auch mit dem Befehl "Installer" einen interaktiven Installer aufrufen, der einem manche Entscheitung abnimmt, dafür aber einige Vorgaben durchsetzt. Beim Einsatz des Installers sollten die USE-Flags "X" sowie "GNOME". "GTK+". "QT3", "QT4" und "KDE" entfernt werden, da auf der Appliance in der Regel keine X11 Unterstützung benötigt wird. Das reduziert die Compilerzeiten, und spart wertvolle Systemresourcen.

Nach erfolgter Installation ist es sinnvoll mit

 # rc-update add sshd default

sicherzustellen, daß der SSH Daemon immer mit gestartet wird. Dann kann man den Bildschirm und Tastatur abstöpseln und das Gerät komplett über das Netzwerk bedienen.



TIP: In der Folge müssen immer wieder Konfigurationsdateien bearbeitet werden. Der Standardeditor von Gentoo ist "nano", ein etwas besseres Notepad für die Konsole, wie ich finde. Wer einen mächtigeren Editor möchte, installiert sich Vim oder Emacs. Gentoos Vim-Installation kommt mit einer breiten Palette an Dateitypbeschreibungen, so daß selbst exotische Konfigdateien mit Syntaxhighlighting und Auto-Vervollständigung daher kommen. Vim wird installiert mit:

# emerge -av vim

Als nächstes sollte der ACPI Daemon installiert werden. Dies ist sinnvoll, da der Rechner dann stromsparend heruntergetaktet werden kann, und auch die Lüftersteuerung funktioiert, was den Lärmpegel nochmal reduziert.

Der Daemon wird installiert mit:

# emerge -av acpid

dann sollte auch dieser Daemon automatisch beim booten gestartet werden.

# rc-update add acpid default

Um ihn gleich zu starten geht es weiter mit:

/etc/init.d/acpid start

XEN

Nachdem unser Basissystem läuft, soll es so ausgebaut werden, daß es die einzelnen Dienste ausführen kann, die wir in unserem Netz anbieten wollen. In einem großen Netz würde man für die verschiedenen Bereiche (internes Netz, internet Dienste, Firewall) verschiedene Rechner verwenden. Die Applicance soll dies ja gerade zusammenführen; dennoch haben die meisten klassichen Lösungen dieser Art, den Nachteil, daß wird z.B. ein Internetdienst kompromittiert, dies die Sicherheit unseres gesamten Netzes gefährdet. Das Risiko läßt sich durch den Einsatz virtueller Maschinen zumindest reduzieren. Deshalb sollen alle Dienstetypen auf unterschiedlichen VMs laufen. Am Ende wird es eine Maschine für die Firewall geben, eine für die internen Dienste, und eine für die im Internet verfügbaren.

Um Xen zu installieren sind einige Vorbereitungen nötig. Zunächst muss die aktuellste Version des Portage verwendet werden. Herausfinden kann man das mit

eselect portage list

Der Stern in der Ergebnisliste sollte bei 2007.0 stehen. Falls dies nicht der Fall ist sollte man

emerge --sync

ausführen.

TLS und CFLAGS

Einige Programme, besonders die TLS Bibliotheken aus der glibc führen zu einem Konflikt mit XEN, namentlich der Art wie Xen die Segmentregister benutzt. Das führt zu einem Performanceverlust von bis zu 50%. Das Problem tritt nur bei x86_32 Architekturen auf. Um das zu verhindern muß das System mit dem Flag '-mno-tls-direct-seg-refs' neu übersetzt werden. Dazu muß die Datei '/etc/make.conf' angepasst werden:

CFLAGS="-O2 -march=i686 -pipe -mno-tls-direct-seg-refs"

Damit nicht für jede vittuelle Maschine das Basissystem neu übersetzt werden muß, sollte man die Gelegenheit nutzten, und das BUILDPKG-Feature von Gentoo nutzen. Dieses erlaubt es, fertige Binärpakete zu erzeugen, die ohne erneute Übersetzung auf das Zielsystem gebracht werden. Dazu muß die FEATURES Variable in '/etc/make.conf' angepasst werden. Dies geht mit folgendem Befehl:

echo 'FEATURES="${FEATURES} buildpkg"' >> /etc/make.conf

Jetzt kann man den Build-Prouzess neu starten (Achtung, das kann etwas dauern):

emerge -evat world

Um sicher zu gehen, daß alle Konfigurationsdateien richtig sind, sollte der Befehl:

etc-update

nicht vergessen werden!

Den Hypervisor installieren

Xen ist noch maskiert, deshalb müssen folgende Zeilen in '/etc/portage/package.keywords' hinzugefügt werden.

# Xen hypervisor
app-emulation/xen ~x86
app-emulation/xen-tools ~x86
sys-kernel/xen-sources ~x86

Anschließend kann der Hypervisor installiert werden:

emerge -av app-emulation/xen app-emulation/xen-tools

Nun muß sichergestellt werden, daß der xen Daemon nach dem Neustart läuft:

rc-update add xend default

Kernel kompilieren

Um den Xen-Kernel zu kompilieren, benutzen wir genkernel. Falls noch nicht geschehen, wird dies mit

emerge genkernel

installiert.

Nun kann es losgehen. Zunächst installieren wir die Sourcen für den Xen-Kernel.

emerge -av xen-sources

Dann ändern wir in der Datei '/etc/genkernel.conf' ein paar Zeilen:

MENUCONFIG="yes"
# Force genkernel-3.4 to use the arch-specific kernel config file from /usr/share/genkernel/${ARCH}
CLEAN="yes"
# Bootsplash doesn't work with Xen
BOOTSPLASH="no"
# Force use of Xen-specific genkernel profile in /usr/share/genkernel/x86-xen0
ARCH_OVERRIDE="x86-xen0"
# Necessary if you previously encountered the "mdev not found" error
CLEAR_CPIO_CACHE="yes"
CLEAR_CACHE_DIR="yes"

Damit genkernel den richtigen Kernel kompiliert, sollten wir nicht vergessen, den Symlink auf das Xen-Kernel Verzeichnis zu setzen:

rm /usr/src/linux
ln -s /usr/src/linux-2.6.20-xen-r6 linux

Bei Versionen von genkernel >=3.4.0 fehlt das architektur Target für x86-xen. In diesem Fall schlägt der Aufruf zu genkernel fehl (genaueres zu diesem Fehler: bug #120236. Das lässt sich aber zum Glück leicht beheben:

wget http://bugs.gentoo.org/attachment.cgi?id=94540 -O x86-xen0.tar.bz2
tar -xvjf x86-xen0.tar.bz2 -C /usr/share/genkernel/arch
rm x86-xen0.tar.bz2

Wenn ihr eine Init-Ramdisk benutzen wollt, müsst ihr noch eine Modifikation an den Dateien von genkernel vornehmen. Aus irgendwelchen Gründen, schaltet nämlich die xen0-Architektur für genkernel die Verwendung von switch_root in busybox aus. Das führt dann dazu, daß der Init-Prozess nicht auf das eigentliche Root-Mount umschalten kann. Der Fehler wird mit dem Kommando:

echo "CONFIG_SWITCH_ROOT=y" >>/usr/share/genkernel/arch/x86-xen0/busy-config

behoben.

Vor dem Aufruf des Compilers müssen wir noch ein paar patches Einspielen, zumindest für unser Intel Board: Das Board hat zwar eine Lüftersteuerung, die Module dafür sind aber in der Kernelversion 2.6.20, die dem aktuellen Xen-Kernel zugrunde liegt, noch nicht drin.

Erstmal ins Buildverzeichnis wechseln

cd /usr/src/linux

Nun laden wir die Patches herunter

wget http://www.hobbes.ch/statisch/linuxfiles/patches/add-coretemp-driver.patch
wget http://www.hobbes.ch/statisch/linuxfiles/patches/coretemp-add-documentation.patch
wget http://www.hobbes.ch/statisch/linuxfiles/patches/hwmon-w83627ehf-add-w83627dhg-support.patch

Und wenden patches an:

patch -p1 -i add-coretemp-driver.patch
patch -p1 -i coretemp-add-documentation.patch
patch -p1 -i hwmon-w83627ehf-add-w83627dhg-support.patch

Wenn Menuconfig startet, muß unter

Device Drivers-->
  Hardware Monitor -->

Die 'Intel Core 2' Option zusätzlich eingeschaltet werden!

Da wir später den Kernel benutzen wollen, um einen Router aufzusetzen, sollte nicht nur das IP-Forwarding eingestellt sein, sondern auch netfilter. Dazu geht ihr in menuconfig auf

Networking-->
  Networking Options-->
   [*] Network packet filtering framework--->

und schaltet alle Optionen ein, die mit NAT zu tun haben. Es sollte ausreichend sein, diese als Module zu übersetzen. Ich habe unter Core Netfilter Configuration zusätzlich alle Targets aktiviert, dann muss man den Kernel nicht neu übersetzen, wenn man doch noch weitere Targets haben will.

Nun können wir den Kernel konfigurieren und installieren. Der Kernel sollte wie jeder andere Kernel auf die benötige Hardware abgestimmt werden. Die Xen-spezifischen Optionen werden alle verfügbaren Optionen eingeschaltet (Frontend und Backend), weil wir denselben Kernel für Dom0 und DomU verwenden wollen (Multi-Dom-Kernel). Das ist nicht unbedingt notwendig, geht aber schneller zu installieren. Achetet darauf, alle Module einzuschalten, die ihr für den Betrieb eures Kernels braucht, wenn Menuconfig aufgeht. Ich habe zum Beispiel Dinge wie ALSA nicht mit reingenommen, wer das Gerät später z.B. als Telefonanlage nutzen will, braucht aber Sound.

genkernel all

Nun ist unser Kernel fertig, aber noch nicht ganz bereit. Xen kann leider keine genkernel initrds lesen, weil diese ein paar byte Müll am Ende des gzip-Archivs mit sich herumschleppen. Daher müssen wir das initrd-Image tatsächlich mit gunzip aus- und mit gzip wieder einpacken. Vorher machen wir noch eine Kopie des Original-Image, denn wir brauchen es noch:

cd /boot
cp initramfs-genkernel-x86-xen0-2.6.20-xen-r60 initramfs-genkernel-x86-xen0-2.6.20-xen-r60.img
mv initramfs-genkernel-x86-xen0-2.6.20-xen-r60 initramfs-genkernel-x86-xen0-2.6.20-xen-r60.gz
gunzip initramfs-genkernel-x86-xen0-2.6.20-xen-r60.gz
gzip initramfs-genkernel-x86-xen0-2.6.20-xen-r60

Nun können wir den frisch gebackenen Kernel benutzen. Ich nehme zum Booten GRUB; PXELinux oder LiLo gehen natürlich auch, entsprechende Anleitungen finden sich hier.

Wir ändern die Datei '/boot/grub/grub.conf', indem wir einen neuen Eintrag hinzufügen. Den bisherigen Kernel sollte man in jedem Fall stehen lassen, damit es noch ein Fallback-System gibt, auf das man zurückgreifen kann, wenn Xen nicht geht:

title  Xen 3.0 / Linux 2.6.20
root   (hd0,0)
kernel /xen.gz
module /kernel-genkernel-x86-xen0-2.6.20-xen-r60 root=/dev/ram0 init=/linuxrc ramdisk=8192 real_root=/dev/hda3
module /initramfs-genkernel-x86-xen0-2.6.20-xen-r60.gz

Abschließend muß GRUB neu installiert werden (nicht vergessen!):

grub-install /dev/hda

Nun könnt ihr den Rechner neu booten. Wenn alles geklappt hat, sollte der Rechner zunächst den Hypervisor, und dann den neuen Kernel starten.

Den Lüfter leise bekommen

In meinem Gehäuse sind zwei Gehäuselüfter und ein CPU-Lüfter. Der CPU Lüfter ist weniger problematisch, weil er angenehm leise läuft. Die Gehäuselüfter dagegen machen unnötig Krach. Hauptgrund ist die Tatsache, daß sie mit voller Leistung laufen, obwohl die Gehäusetemperatur niedrig ist.

Um den Lüfter temperaturabhängig zu steuern, müssen wir die Kernelmodule für die Lüftersteuerung laden. Einkompiliert haben wir die Module ja vorhin schon. Nun müssen wir die zugehörigen Pakete installieren und konfigurieren.

# emerge -av lm_sensors fancontrol

Nun müssen wir herausfinden, welche Module wir brauchen. 'lm_sensors' nimmt uns den Großteil der Arbeit zum Glück ab. Der Aufruf von

# sensors-detect

startet einen Assistenten, der die nötige Konfigurationsdatei erstellt. Am einfachsten beläßt man alle Fragen auf den Standardeinstellungen, beantwortet also alle Fragen mit einfachem Druck auf 'Return'.

Wenn der Assistent fertig ist, können wir 'lm_sensors' testen. Dazu rufen wir

# /etc/init.d/lm_sensors start

auf. Wenn keine Fehlermeldungen kamen, liefert uns

# sensors

nun eine nette Übersicht über Temperatur, Lüfterdrehzahlen und Stromversorgung auf unserem Board. Graphische Tools wie 'grellkm' oder 'conky' bringen das auch auf den X11-Bildschirm, was uns jetzt aber nicht weiter interessiern muß.

Als nächstes müssen wir die Lüfterdrehzahl abhängig von der Temperatur steuern. Das Programm 'fancontrol' bedient sich dabei der Daten die 'lm_sensors' liefert. Zur Konfiguration gibt's ebenfalls ein passendes Skript. Dieses testet die Lüfterdrehzahlen, und versucht die richtigen Einstellungen zu finden. Da das Skipt ein paar mal fragt, ob der Lüfter gerade läuft, oder wann er anhält, sollte man das Skipt bei offenem Gehäuse durchführen, so daß man die Lüfter sehen kann. Aufgerufen wird das Skript mit:

# pwmconfig

Seid vorsichtig mit der Lüftersteuerung. Man kann mit angehaltenen Lüftern die CPU auch grillen! Am Ende sollte 'pwmconfig' die Datei '/etc/fancontrol' erzeugt haben. Mit dieser läßt sich der fancontrol Daemon nun starten

# /etc/init.d/fancontol start

Die Lüfter sollten nun deutlich langsamer, und damit leiser, laufen. Klappt alles, können wir die Daemons beim Start mitlaufen lassen:

# rc-update add lm_sensors default
# rc-update add fancontrol default


Unpriviligierte Domänen einrichten

Damit die Einrichtung des Xen-Kernels sich gelohnt hat, wollen wir jetzt eine unpreviligierte Domäne einrichten. Eine unpreviligierte Domäne ist unter Xen das, was andere VMMs als "virtuelle Maschine" bezeichnen. Sie steht im Gegensatz zur "priviligierten Domäne", die wir im Abschnitt Xen ja bereits erzeugt haben. Die priviligierte Domäne regelt den Zugriff auf die tatsächliche Hardware, und steuert die übrigen Domänen. Die unpriviligierten Domänen können -passende Hardware vorausgesetzt- jedes Betriebssystem aufnehmen. Damit dies ohne Modifikation am Kernel gelingt, benötigt man allerdings Prozessoren, die eine Unterstützung für Hypervisor-Technologien erlauben. Intels "VT" Technologie (früher "Vanderpool") ist hierfür geeignet. Bei AMD heißt die entsprechende Technik "SVM" (früher "Pacifica"). Alle Intel Prozessoren mit dem "Core 2" Logo unterstützen dies. Bei AMD gilt das für alle CPUs, die nach Juni 2006 hergestellt wurden. Unser Prozessor vefügt leider nicht über diese Technologie, weshalb wir den Kernel anpassen müssen (haben wir ja schon gemacht, als wir den Kernel oben erzeugt haben). Sofern ihr nicht beabsichtigt Betriebssysteme aus Redmond auf der Kiste zu benutzen, macht das also nichts.

Als Betriebssystem soll wieder Gentoo Linux dienen, das spart einiges an Konfigurationsaufwand. Es gibt verschiedene Wege ein System zu installieren, unter anderem kann man verschiedene Distributionen mit dem Tool domi einrichten.

Da ich weiterhin mit Gentoo arbeiten will, habe ich mich für die Variante mit quickpkg entschieden. Dieses Tool, welches Bestanteil der Portage-Tools ist, erlaubt es bereits zuvor compilierte Pakete einfach zu installieren, ohne nochmal zu compilieren. Da wir weiter oben ja schon mit dem buildpkg Flag gearbeitet haben, haben wir das Basissystem ja schon fertig, neu compilieren wäre da überflüssig.

Als erstes brauchen wir ein Dateisystemimage, in das wir installieren. Dies kann natürlich eine beliebige freie Partition auf eurer Platte sein, oder aber eine Datei. Ich bevorzuge die Dateimethode. Die hat zwar mehr Overhead, aber man kann ein image leicht kopieren um eine weitere VM zu erstellen, oder ein Backup zu machen. Xen kann solche Images sogar im laufenden Betrieb auf eine andere physische Maschine verschieben (das geht zwar auch mit Partitionen, aber der Admin muß etwas mehr dafür tun).

Wir basteln uns ein 20GB Image, immerhin soll das System später große Mengen von E-Mails in einer Datenbank aufnehmen, ihr könnt die Größe natürlich euren Bedürfnissen anpassen. Ausserdem erstellen wir gleich ein Dateisystem, und mounten das Image:

dd if=/dev/zero of=/var/xen/domU-gentoo-rikki.img bs=1M seek=20480 count=1

mkfs -t ext3 /var/xen/domU-gentoo-rikki.img

mkdir /mnt/rikki
mount -o loop /var/xen/domU-gentoo-rikki.img /mnt/rikki

Ich benutze für den Image-Namen, und den des Mount-Points immer den Namen, den die VM bekommen soll, in diesem Fall also rikki. Die VM kann natürlich einen beliebiegn Namen bekommen.

Damit wir gleich in die neue Umgebung chrooten können, brauchen wir ein lauffähiges System auf dem Image. Dafür laden wir ein stage2-Archiv herunter und entpacken es nach /mnt/rikki

wget http://mirrors.sec.informatik.tu-darmstadt.de/gentoo/releases/x86/2007.0/stages/stage2-i686-2007.0.tar.bz2
tar -jxvf stage2-i686-2007.0.tar.bz2-C /mnt/rikki

Nun mounten wir die /proc und /dev Dateisysteme

mount -t proc none /mnt/rikki/proc
mount -o bind /dev /mnt/rikki/dev


Als nächstes kopieren wir den existierenden Portage-Tree der dom0 in unsere domU. Das ist der richtige Moment sich eine Club-Mate zu holen, es dauert nämlich ein paar Minuten:

cp -av /usr/portage/ /mnt/rikki/usr/

Nun brauchen wir noch die Portage-config, und die resolv.conf für unsere chroot Umgebung:

cp /etc/make.conf /mnt/rikki/etc/
cp -R /etc/portage /mnt/rikki/etc/
cp /etc/resolv.conf /mnt/rikki/etc/

Sicherstellen, daß beide Umgebungen das selbe Profil benutzen, damit emerge nachher nicht Amok läuft:

rm /mnt/rikki/etc/make.profile
ln -s ../usr/portage/profiles/default-linux/x86/2006.1/server /mnt/rikki/etc/make.profile

Jetzt wird es ernst, wir machen aus dem stage2 System ein 'richtiges' Gentoo, bei dem wir die Konfiguration aus der dom0 übernehmen. Dabei nutzen wir die Binärpakete, die wir während der Installation der dom0 schon erstellt haben:

ROOT=/mnt/rikki/ CONFIG_PROTECT=-/etc FEATURES=-collision-protect emerge --usepkg --emptytree system

Achtung: Es ist wichtig, daß die ROOT-Variable richtig gesetzt ist, sonst installiert emerge ins falsche Dateisystem!

Ist die Installation fertig, können wir in die Installation chrooten (etwaige Fehlermeldungen wegen Bibliotheken können dabei ignioriert werden, der gcc-config Befehl korrigiert das):

chroot /mnt/rikki
gcc-config 1
env-update
source /etc/profile

Nun setzen wir, wie bei einer übliche Stage3-Installation, die Zeitzone:

cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime

Und den Hostnamen in /etc/conf.d/hostname:

HOSTNAME="rikki"

Jetzt müssen wir alle Pakete, die noch aus dem Stage2-Tarball kommen, aus unseren Binärpaketen neuinstallieren, damit das Portage-Archiv korrekt ist. Dazu gibt es in den gentools das Programm revdep-rebuild:

emerge --usepkg app-portage/gentoolkit
revdep-rebuild --usepkg

Jetzt noch aufräumen:

perl-cleaner all --usepkg
python-updater

Ein Passwort für root brauchen wir natürlich auch noch. Also mit

passwd

eines festlegen.

Unser System ist jetzt eingerichet. Wir können also die chroot-Umgebung mit exit verlassen.

Jetzt müssen wir Xen von unser domU erzählen. Das geht über die Datei /etc/xen/rikki:

# general
name    = "rikki";
memory  = 256;

# booting
kernel  = "/boot/kernel-genkernel-x86-xen0-2.6.20-xen-r60";
ramdisk = "/boot/initramfs-genkernel-x86-xen0-2.6.20-xen-r60.img";
builder = "linux";

# virtual harddisk
disk = [ 'file:/var/xen/domU-rikki-gentoo,xvda,w' ];
root = "/dev/xvda ro";

# virtual network
#vif = [ 'mac=aa:00:17:3e:01:ca' ];
vif = [  ];
dhcp = "dhcp";

Unsere neue VM nutzt also den selben Kernel wie die dom0. Das geht, weil wir den Kernel als MultiDom erstellt haben. Beider Ramdisk ist es wichtig in diesem Falle die ursprünglich von genkernel erzeugte zu benutzen!

Weiter geht's mit der Datei /mnt/rikki/etc/fstab/:

# <fs>                  <mountpoint>    <type>          <opts>          <dump/pass>

# NOTE: If your BOOT partition is ReiserFS, add the notail option to opts.
#/dev/BOOT              /boot           ext2            noauto,noatime  1 2
/dev/xvda               /               ext3            noatime         0 1
/swap                   none            swap            sw              0 0
#/dev/cdrom             /mnt/cdrom      audo            noauto,ro       0 0
#/dev/fd0               /mnt/floppy     auto            noauto          0 0
proc                    /proc           proc            defaults        0 0

# glibc 2.2 and above expects tmpfs to be mounted at /dev/shm for
# POSIX shared memory (shm_open, shm_unlink).
# (tmpfs is a dynamically expandable/shrinkable ramdisk, and will
#  use almost no memory if not populated with files)
shm                     /dev/shm        tmpfs           nodev,nosuid,noexec     0 0

Es ist zu beachten, daß die Rootpartition /dev/xvda ist. Diesen Namen haben wir in der Datei /etc/xen/rikki im Parameter disk = festgelegt. Man könnte natürlich auch /dev/hda nehmen, aber das führt schnell zu Verwechslungen mit physischen Geräten, vor allem wenn ihr mal in die Verlegenheit kommt, eine physische Platte in die VM einzubinden.

Unmountet nun das Dateisystem der domU:

umount /mnt/rikki/{dev,proc,} 

Vorher nochmal sicherstellen, daß xend läuft, und auch beim nächsten start wieder hochfährt:

/etc/init.d/xend start
rc-update add xend default

Fertig! Jetzt können wir unsere VM-Starten:

xm create rikki -c

Wenn alles geklappt hat begrüsst uns unsere VM nun mit einem Login-Prompt. Heimweh nach der dom0 bekämpft man mit Strg+Alt+] (auf deutschen Tastaturen etwas krampfig: Strg+AltGr+9).

Virtuelle Maschinen managen

Auf der Konsole der Dom0 gibt es das hüsche Tool xm. Damit kann man virtuelle Maschinen starten und steuern. Ich will keine detaillierte Anleitung schreiben, die man page hilft da weiter. Hier nur die wichtigsten Befehle:

# Startet eine Domäne. <Name> ist dabei der Name der Konfiguratrionsdatei in /etc/xen ohne den Pfad.
xm create <Name>

# Auflisten aller aktiven Domänen
xm list

# Wechselt auf die Konsole einer Domäne
xm console <Domäme>

# Herunterfahren einer Domäne
xm shutdown <Domäne>

# Neustarten einer Domäne
xm reboot <Domäne>
# Stoppen einer laufenden Dömäne
xm pause <Domäne>

# Angehaltene Domäne weiterlaufen lassen
xm unpause <Domäne>

xm ist ein sehr mächtiges Tool mit dem man alles steuern kann, was Xen beherrscht, ein Blick auf die Man-Page lohnt daher immer. Es gibt auch ein paar graphische Frontends, die besonders dann interessant sind, wenn man mit meheren X11 fähigem Domänen hantiert. Zudem kann man noch den Xend-Daemon verwenden, um ein Webfrontend freitzschalten.

Domänen beim Systemstart hochfahren

Wer seine Domänen gleich beim Systemstart mitstarten will, benutzt xendomains:

rc-update add xendomains default
/etc/init.d/xendomains start

Um nun eine Domäne automatisch starten zu lassen, legt man nur einen Symlink der Konfigurationsdatei in dem Verzeichnis /etc/xen/auto/ an:

ln -s /etc/xen/rikki /etc/xen/auto/rikki

xendomains prüft nun automatisch, ob rikki läuft, und startet sie bei Bedarf. Außerdem sorgt xendomains dafür, daß der Shutdown der Dom0 so lange unterbrochen wird, bis es alle DomU's sauber heruntergefahren hat.

Template erstellen

Unser Basissystem steht nun. Das ist ein guter Zeitpunkt um ein Template Image zu erstellen, aus dem wir weitere DomU's erzeugen können. Damit erpsaren wir die gesamte Konfigurationsarbeit, und können eine neue VM mit zwei Befehlen erzeugen. Unter Gentoo ist das erstellen eines solchen Image zum Glück ganz leicht, man erstellt einfach einen Tarball des Dateisystems. In der Gentoo Terminologie heißt so ein Archiv ein stage4-Archiv.

Als erstes wechseln wir auf unsere Domäne:

xm console rikki

Anmelden als root.

Dann erstellen wir uns eine Datei names stage4.excl in die wir hineinschreiben, welche Verzeichnisse nicht in den tarball sollen:

.bash_history
/mnt/*
/tmp/*
/proc/*
/sys/*
/dev/*
/etc/ssh/ssh_host_*
/usr/src/*
/usr/portage/*
/root/stage4.tar.bz2

Nun rufen wir tar auf:

tar cvjfp /root/stage4.tar.bz2 / -X stage4.excl

Jetzt halten wir die Domäne an,

init 0

und mounten das Image noch einmal, und verschieben das stage4 Archiv :

mount -o loop /var/xen/domU-gentoo-rikki.img /mnt/rikki
mv /mnt/rikki/root/stage4.tar.bz2 /var/xen/stage4.tar.gz
umount /mnt/rikki

Eine weitere DomU kann nun jederzeit aufgesetzt werden, indem man ein Dateisystem-Image erzeugt, und das stage4 Archiv hinein entpackt.

Einen Mailserver einrichten

Die Appliance soll zwar später auch als Router und Firewall eingesetzt werden, aber ich habe derzeit meine Mails auch auf der alten Appliance, weshalb ich lieber sicherstellen will, daß die Mails auf dem neuen System sind, bevor ich an dem alten rumfummele. Als Mailserver soll uns die soeben eingerichtete Maschine rikki dienen. Also booten wir zunächst in die Maschine, und wechseln hinein. Ich habe auf allen VMs einen ssh-Deamon laufen, damit kann man vom Arbeitsplatzrechner bequem in die Maschinen rein, ohne ständig die Konsole wechseln zu müssen.

Syslog und cron einrichten

Das von uns auf der VM installiere Basissystem enthält noch keinen Syslogdienst, und auch ein Cron Daemon macht sich rar. Beides richte ich in der Regel erst später ein, weil ich dann wählen kann, welche Variante für den Einsatzweck am besten geeignet ist. Syslog-ng ist für einen popeligen NFS-Server z.B. ein ziemlich schweres Geschütz. Für unseren Mailserver ist es dagegen gerade das richtige. Für Cron-Jobs benutze ich vixie-cron, der unter Gentoo-Linux eine Art Quasi-Standard bildet. Los geht's:

emerge -av syslog-ng vixie-cron

Als nächstes, wollen wir für syslog eine Facility einrichten, mit der wir den Output unseres Mailservers in die Datei /var/log/mail.log umleiten, anstatt in /var/log/messages zu schreiben. Dazu müssen wir die Datei /etc/syslog-ng/syslog-ng.conf editieren. Wir fügen eine neue destination ein, und definieren einen filter, der "mail" als facility verwendet. Dadurch werden alle Einträge, die auf mail geschrieben werden, in /var/log/mail.log geschrieben. Die geänderte Datei sollte so aussehen:

# $Header: /var/cvsroot/gentoo-x86/app-admin/syslog-ng/files/syslog-ng.conf.gentoo,v 1.7 2007/08/02 04:52:18 mr_bones_ Exp $
#
# Syslog-ng default configuration file for Gentoo Linux
# contributed by Michael Sterrett

options {
        chain_hostnames(off);
        sync(0);

        # The default action of syslog-ng 1.6.0 is to log a STATS line
        # to the file every 10 minutes.  That's pretty ugly after a while.
        # Change it to every 12 hours so you get a nice daily update of
        # how many messages syslog-ng missed (0).
        stats(43200);
};

source src {
    unix-stream("/dev/log" max-connections(256));
    internal();
    file("/proc/kmsg");
};

destination mail {file("/var/log/mail.log"); };
destination messages { file("/var/log/messages"); };

# By default messages are logged to tty12...
destination console_all { file("/dev/tty12"); };
# ...if you intend to use /dev/console for programs like xconsole
# you can comment out the destination line above that references /dev/tty12
# and uncomment the line below.
#destination console_all { file("/dev/console"); };

filter mail {facility(mail); };
filter notmail{not facility(mail); };

log {source(src); filter(mail); destination(mail); };
log { source(src); filter(notmail); destination(messages); };
log { source(src); destination(console_all); };


Cron benötigt keinere weitere Konfiguration. Nun müssen beide Dienste gestartet werden.

rc-update add vixie-cron default
rc-update add syslog-ng default
/etc/init.d/vixie-cron start
/etc/init.d/syslog-ng start

Postfix installieren

Als nächstes brauchen wir einen Mailserver, ich habe mich für Postfix enschieden, weil der sehr mächtig, und gleichzeitig nicht so schwierig zu Konfigurieren ist.

Installiert wird, wenig überraschend, mit

emerge -av postfix

Vermutlich will portage noch ein paar weitere Dinge installiern, es kann also ein paar Minuten dauern.

Als erstes wollen wir Postfix dazu bekommen lokale Mail anzunehmen, und ins Internet weiterzuleiten. Da wohl die meisten User keine eigene Maildomäne betreiben wollen, nutzen wir hier den Smarthost unseres Mailproviders. Das erspart uns unter Umständen einigen Ärger, weil -besonders bei dynamischen IP-Addressen, sonst die Gefahr besteht wegen ungültiger MX-Records als Spamversender eingestuft zu werden.

Postfix wird im wesentlichen über zwei Dateien konfiguriert: /etc/postfix/main.cf und /etc/postfix/master.cf. Für den Versand von Mail bearbeiten wir zunächst die Datei /etc/postfix/main.cf. Folgende Einträge sollten verändert werden (in der Beispieldatei sind die meisten dieser Optionen schon drin, bloß auskommentiert. Das Kommentarzeichen '#' zu entfernen, und den Eintrag anzupassen genügt also meistens. Postfix muss wissen, wie unser Rechner heisst. Gemeint ist hier der lokale Hostname als FQDN. Meine lokalen Domänen liegen meist in der fikiven Top-Level Domain .lan um Probleme mit dem DNS auszuschließen.

myhostname = host.meine-domaene.lan
mydomain = meine-domaene.lan

Wird mydomain weggelassen, nimmt Postfix automatisch myhostname ohne die erste Komponente (host) des Namens.

Nun sagen wir Postfix, wie die Domäne heißt, von der lokal versandte Mail zu kommen scheint. Üblicherweise nimmt man hier den Wert aus mydomain:

myorigin = $mydomain

Für die Zustellung von Mail, müssen wir Postfix sagen, welche Ziele lokal auf dem Rechner liegen. Mails deren Zieladdressen in einer dieser Domänen liegen, werden nicht an den Smarthost weitergegeben, sondern lokal zugestellt.

mydestination =  $myhostname, $mydomain, localhost, localhost.$mydomain

Postfix hat, seit der Version 2.1, die Fähigkeit zu erkennen, wenn Mail an nicht existierende Nutzer zugestellt werden soll, und kann diese automatisch bouncen. Das ist eigentlich praktisch, weil man für diese Konfiguration nicht erst den lokalen Tranport bemühen muß, erweist sich in unserem Fall aber als hinderlich. Da wir die Mails später mittels dbmail in einer Datenbank lagern wollen, werden keine Unix-User mehr benötigt, die den Mailboxusern entsprechen. Darum schalten wir diese Funktion ab, und überlassen das bouncen von unbekannten Usern dbmail. Die Eigenschaft local_receipient_maps muss daher so geändert werden:

#local_recipient_maps = unix:passwd.byname $alias_maps
#local_recipient_maps = proxy:unix:passwd.byname $alias_maps hash:/etc/postfix/localusers
local_recipient_maps =

Jetzt legen wir fest, wer Mail über unseren Server versenden darf, und wer nicht. Alle Mitglieder der hier angegebenen Netze können Mail über diesen Server senden. Am einfachsten geht das mit:

mynetworks_style = subnet

Damit wird allen Rechnern die im selben Subnetz operieren, wie der Mailserver, der versandt von Mail erlaubt. ACHTUNG: Wenn der Rechner direkt mit dem Internet verbunden ist, und somit z.B. die vom Provider zugewiesenen Adresse hat, öffnet ihr den Rechner für das gesamte Subnetz des Providers, also vermutlich für alle DSL-Kunden eures Providers! In diesem Fall ist

mynetworks_style = host

die bessere Wahl.

Alternativ hat man die Möglichkeit gezielt einzelne Netze festzulegen. Um z.B. dem localhost und dem privaten Netz 192.168.10.0 den Zugriff zu erlauben, schreibt man dies:

mynetworks = 192.168.10.0/24, 127.0.0.0/8

Als nächstes legen wir fest, wie Postfix unsere Mails ans Internet weitergeben soll. In der Voreinstellung tut er das selbst, wir wollen aber den Smarthost unseres Providers als Relay benutzen. Darum ändern wir die Einstellung relay_host

relay_host = [Euer.Mail-smarthost.tld]

Die eckigen Klammern sind wichtig: Die Smarthosts bei Providern sind oftmals keine MX-Hosts, weil die Annahme von Mails von anderen Servern ausgeführt wird. Damit würde Postfix an diesen Server nichts mehr zustellen, weil es glaubt, der Server sei kein Mail-Server. Die eckigen Klammern schalten die MX-Host-Abfrage aus.

Damit haben wir Postfix für den Versand von Mails vorbereitet. Als nächstes müssen wir das Ganze starten:

rc-update add postfix default
/etc/init.d/postfix start

Tragt jetzt euren Mailserver als Mailausgangsserver in eure Lieblingsmailsoftware ein, und schickt euch selbst eine Mail. Wenn sie ankommt ist alles OK. Falls nicht, schaut mal mit

mailq

auf eurem Server nach, ob die Mail noch in der Queue steht. Falls ja, liegt das wahrscheinlich daran, daß euer Provider verlangt, daß ihr euch an seinem Postausgangsserver anmeldet. Die meisten DSL-Provider machen heute die Authentifzierung über einen RADIUS-Server, d.h. ihr werdet schon über die DSL Anmeldung authentifiziert, und könnt dann den Mailserver nutzn, der selbst im Internet nicht sichbar ist, sondern nur im Netz des Providers. Der Nachteil dieser Variante besteht darin, daß ihr über das Netz eures Providers eingeloggt sein müsst, damit ihr den Maildienst nutzen könnt (ein Problem, daß sich aber über den hier beschriebenen Server lösen lässt).

Falls ihr euch anmelden müßt, gibt es noch ein paar Sachen zu tun: Erstmal muß Postfix mit TLS-Support übersetzt sein. Dazu stellt sicher, daß Postfix mit dem USE-Flag "sasl" übersetzt wurde. Falls das nicht der Fall ist, fügt dieses Flag in der Datei /etc/make.conf hinzu, und emerged postfix erneut. Damit wird die cyrus-sasl Implementierung gestartet. Dovecot bietet ebenfalls eine SASL-Bibliothek an, ich beziehe mich aber auf die Cyrusvariante.

Um die Authentifizierung über den Postfix-SMTP Client einzustellen, fügt einfach in /etc/postfix/main.cf folgende Einträge ein:

smtp_sender_dependent_authentication = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

Ausserdem legt /etc/postfix/sasl_passwd an:

[Euer.Mail-Smarthost-tld]    username:password

Wobei die Werte natürlich durch eure echten Werte ersetzt werden müssen. Nun noch die neue Konfiguration laden:

postfix reload

Nun sollte der Mailvesand klappen.

IMAP Dienst einrichten

Wir wollen ja nicht nur Mails versenden, sondern auch welche empfangen und speichern. Dies serverseitig zu tun hat mehere Vorteile:

  • Mails werden an einem zentralen Ort gespeichert. Das macht die Pflege und Wartung einfacher.
  • Mails können mit jedem Client abgerufen werden, und der Zugriff auf den gesamten Mailbestand ist möglich
  • Spamfilter und Virenscanner können zentral eingebunden werden, und müssen nur einmal konfiguriert werden.

Es gibt verschiedene Lösungen, die bekanntesten sind wohl Cyrus-IMAP und Courier-IMAP. Ich habe mich für das etwas weniger verbreitete DBMail entschieden, weil es die Mails praktischerweise in einer Datenbank ablegt, was zum einen die Handhabung vereinfacht, besonders wenn es um virtuelle Mailaccounts geht. Man muss eben nicht für jede Mailbox einen Unix-User anlegen. Außerdem speichert DBMail alles, auch die Konfiguration der Benutzerkonten, in der Datenbank. Eine Komplettsicherung des Mailsystems beschränkt sich also auf einen database-dump. Als Backend unterstützt DBMail zur Zeit MySQL, PostgreSQL und SQLite. Ich benutze hier MySQL, auch wenn einige von euch mich dafür vermutlich schlagen möchten. Aber für ein privates Mailsystem ist PostgreSQL eigentlich etwas überdimensioniert, und MySQL dürfte allen, die schon mal irgendwas mit Webanwendungen zu tun hatten geläufig sein. Alles hier vorgestellte geht aber mit einer der anderen genannten Datenbanken genauso gut.

DBMail verlangt bei MySQL die Verwendung des InnoDB-Treibers, bei älteren Versionen ist der deshalb einzuschalten. DBMail selbst soll mit Unterstützung für MySQL sowie Sieve (für das serverseitige Filtern von Mails) übersetzt werden. Um dies zu erreichen fügen wir der Datei /etc/portage/package.use folgende USE-Flags hinzu:

dev-db/mysql innodb berkdb
net-mail/dbmail mysql sieve

Nun installieren wir MySQL:

emerge -av mysql

Die nächste Mate ist jetzt angesagt, das übersetzten von MySQL dauert etwas. Nach der Installation muss MySQL konfiguriert werden. U.a. wird dabei auch das Admin-Passwort festgelegt.

emerge --config mysql

Bevor es losgehen kann muß noch sichergestellt sein, daß die MySQL Konfiguration stimmt. Die Gentoo-Default Datei ist eigentlich in Ordnung, allerdings ist die maximale Größe für eine InnDB Datei auf 128MB begrenzt. Da ich Mail von fast zehn Jahren auf meinem Server vorhalte, ist das ein bischen mager. Man kann zwar jederzeit eine neue Datei hinzufügen, aber nicht ohne Eingriff. Einzelne Dateien dagegen können dynamisch wachsen. Ich setzte daher die Dateigröße ziemlich hoch an (12GB). In der Datei /etc/mysql/my.cnf ist dafür folgende Zeile anzupassen:

innodb_data_file_path = ibdata1:10M:autoextend:max:12G

Jetzt können wir den MySQL Server starten:

/etc/init.d/mysql start

Nun brauchen wir eine Datenbank, mit der DBMail arbeiten soll:

mysqladmin create dbmail -u root -p

Und wir legen uns einen Benutzer an, den DBMail verwenden wird

mysql -u root -p
GRANT ALL ON dbmail.* to dbmail@localhost identified by '<pass>';

Wobei <pass> natürlich durch ein richtiges Passwort ersetzt wird.

Jetzt können wir DBMail installieren, mit:

emerge -av dbmail

Als nächstes richten wir unsere Datenbank mit den von DBMail benötigten Komponenten ein:

 cd /usr/share/doc/dbmail-2.2.7_rc2
 bunzip2 create_tables_innoDB.mysql
 mysql -u dbmail dbmail -p < create_tables_innoDB.mysql

DBMail selbst ist nun fertig eingerichtet. Jetzt müssen wir postfix noch sagen, daß es die Mail an DBMail weitergeben soll. Dazu richten wir einen Transport ein, der die Mails entsprechend zustellt. In /etc/postfix/master.cf fügen wir ein:

dbmail-lmtp     unix    -       -       n       -       -       lmtp 
   -o disable_dns_lookups=yes

Damit ist ein Transport eingerichtet, der die Mails an den lmtp-Dienst von DBMail weiterreicht. Damit Postfix diesen benutzt, erklären wir ihn in /etc/postfix/main.cf zum Standardtransport:

mailbox_transport = dbmail-lmtp:localhost:24

Jetzt können wir die Dienste starten:

/etc/init.d/dbmail-imapd start
/etc/init.d/dbmail-lmtpd start
postfix reload

Damit das alles passiert, wenn der Rechner hochfährt, sollten wir die Dienste ins Init-Skript aufnehmen:

rc-update add mysql default
rc-update add dbmail-lmtpd default
rc-update add dbmail-imapd default

Damit wir jetzt auch Mail empfangen können, brauchen wir ein Mailkonto. Mailkonten werden in dbmail ganz einfach erzeugt:

dbmail-users -u user -w password

Ausserdem legen wir fest, welche Zieladressen in diese Mailbox ausgeliefert werden sollen:

dbmail-users -u user -s user@meinedomain.tld

Man kann hier auch mehrere Adressen angeben, jeweils durch Komma getrennt.

Mit

dbmail-users -u -S user@meinedomain.tld

wird diese Zuordnung wieder aufgehoben.

Jetzt sollte die Mail angenommen werden, und automatisch in der richtigen Mailbox landen. Mails, die an nicht vorhandene User gehen, werden von DBMail nicht angenommen, und daher von Postfix gebounct.

Spam abwehren

Im Prinzip können wir uns jetzt zurücklehnen, unser Mailserver läuft jetzt. Was noch fehlt, ist ein Filter für Spam (wer will den Mist schon jeden Tag von Hand löschen). Eine der bekanntesten, und sicher auch wirkungsvollsten Anti-Spam-Lösungen im Open-Source-Bereich ist SpamAssassin. SpamAssassin lässt sich als Daemon betreiben und per procmail mit Mails befüttern. Eleganter ist aber die Verwendung eines Content-Filters, wie Amavis, um diese Aufgabe zu übernehmen. Amavis hat den Vorteil, daß es nicht einmal auf der selben Maschine laufen muss wie Postfix, was für große Systeme sehr nützlich ist. Als eigener Server lässt es sich auch effektiver parallelisieren als procmail.

Um die Konfiguration zu verstehen, muß man sich vor Augen führen, welchen Weg die Mail geht. Zuerst nimmt Postfix die Mail an, reicht sie dann an Amavis weiter, wo sie bearbeitet wird. Amavis schiebt die Mail zurück an Postfix, welches sie dann an DBMail weiterleitet. Damit Postfix weiß, wie diese Chain funktioniert, dürfen wir natürlich nicht die Ports 24 und 25 nehmen, um die Mail zwischen Amavis und Postfix auszutauschen. Postfix wüßte sonst ja nicht, daß Amavis die Mail schon hatte, und wir hätten einen Mail-Loop. Stattdessen erzeugen wir zwei weitere Transports in /etc/postfix/master.cf, die auf den Ports 10025 lauschen bzw. die Mail an 10024 weitergeben.

Die Installation geht wie üblich mit emerge:

emerge -av amavisd-new spamassassin

Amavis einzurichten ist leider nicht ganz so einfach, die Amavis Konfiguration ist ziemlich lang. Die Konfigdatei ist deshalb in Sektionen unterteilt. Ich beschreibe jetzt nur, was man ändern muß. Editiert wird /etc/amavisd.conf.

Sektion I:

Als erstes legen wir fest welche Domäne wir checken wollen, und auf welchem Rechner der Mailserver läuft, damit Amavis weiß, für welche Rechner es zuständig ist:

$mydomain = 'mydomain.tld';      # (no useful default)
$myhostname = 'thishost.mydomain.tld';  # fqdn of this host, default by uname(3)

Den zweiten Parameter anzupassen ist wichtig, da der Default keinen vollqualifizierten Hostnamen zurückgibt, und Amavis dann meckert.

Ausserdem muß die Zeile

# @bypass_spam_checks_maps = (1);  # uncomment to DISABLE anti-spam code

auskommentiert sein. Sonst werden alle Spam-Checks abgeschaltet.Die Zeile

@bypass_virus_checks_maps = (1);

habe ich erstmal eingeschaltet, weil noch kein Virenscanner installiert ist.

Als nächstes sagen wir Amavis, daß es die Mails an Postfixzurückgeben soll. Natürlich nicht an Port 25, sonst würden wir ja einen Mail-Loop erzeugen. Deshalb verwenden wir einen Server auf Port 10025 (es kann auch jeder andere freie Port sein, muß aber der gleiche sein, auf dem Postfix wartet).

# POSTFIX, or SENDMAIL in dual-MTA setup, or EXIM V4
# (set host and port number as required; host can be specified
# as an IP address or a DNS name (A or CNAME, but MX is ignored)

$forward_method = 'smtp:[127.0.0.1]:10025'; # where to forward checked mail

$notify_method = $forward_method;       # where to submit notifications

Sektion II:

Nun komplettieren wir das Bild, indem wir Amavis sagen, daß es auf Port 10024 auf lmtp Verbindungen warten soll.

$inet_socket_port = 10024;        # accept SMTP on this local TCP port
                                  # (default is undef, i.e. disabled)

Sektion III: Hier kann man den Log-Level anpassen. Defaulteinstellung ist 2, erstmal kann man das auch so lassen

$log_level = 2;           # (defaults to 0)

Sektion IV: Als nächstes sagen wir Amavis, was es mit der Mail machen soll, wenn sie von dem jeweiligen Filter abgelehnt wurde.

$final_virus_destiny      = D_DISCARD; # (defaults to D_DISCARD)
$final_banned_destiny     = D_BOUNCE;  # (defaults to D_BOUNCE)
$final_spam_destiny       = D_PASS;  # (defaults to D_BOUNCE)
$final_bad_header_destiny = D_PASS;    # (defaults to D_PASS)

Ich habe $final_spam_destiny auf D_PASS gestellt. Das lässt den Spam durch, anstatt ihn ins Quarantäneverzeichnis zu schreiben (D_DISCARD). SpamAssassin markiert die Spam-Mails, so das wir sie dem Benutzer zustellen, und gleich in ein Spam-Verzeichnis sortiern können. Das ist bequemer, zumal man die Kontrolle der Mails dem User überlassen kann, und steht nicht im Widerspruch zum Briefgeheimnis. Strenggenommen ist das abfangen von Mails Dritter -auch des Spams- nämlich ein strafbarer Eingriff in das Fernmeldegeheimnis.

Schließlich schalten wir noch aus, daß der Mailadmin für jeden gefundenen Spam eine Infomail erhält. Diese Funktion stammt wohl noch aus Zeiten vor dem 'ewigen September':-).

$virus_admin = undef;
$spam_admin = undef;
 
$mailfrom_notify_admin     = undef;
$mailfrom_notify_recip     = undef;
$mailfrom_notify_spamadmin = undef;

Die nächsten Sektionen brauchen wir nicht zu ändern, es geht weiter mit Sektion VII. In dieser Sektion werden die Parameter für Spamassassin festgelegt. Ich habe nur eine Einstellung geändert, nämlich die Punktezahl, ab der Spamassassin eine Mail als Spam einstuft. Diesen Wert habe ich auf 5.0 gesetzt, um die Zahl der false negatives zu senken.

$sa_tag2_level_deflt = 5.0;# add 'spam detected' headers at that level to
                           # passed mail (e.g. when $final_spam_destiny=D_PASS
                           # or for spam_lovers or when below kill_level)

Jetzt können wir den Dienst starten:

/etc/init.d/amavis start

Nun müssen wir Postfix so konfigurieren, daß es unseren Content-Filter benutzt. Zuerst kommentieren wir in /etc/postfixmaster.cf den Standard-SMTP-Transport aus, und fügen einen Transport ein, der stattdessen die Mails an Amavis weitergibt:

#smtp      inet  n       -       n       -       -       smtpd –v
#
#Instruct smptd on port 25 to deliver to mail amavisd on port 10024
smtp       inet  n       -       n       -       4       smtpd
    -o content_filter=scan:[127.0.0.1]:10024
    -o receive_override_options=no_address_mappings

Als nächstes erzeugen wir den Transport scan, den wir oben benutzen:

# mail transport used above
scan    unix    -       -       n       -       4       lmtp
    -o disable_dns_lookup=yes
    -o lmtp_send_xforward_command=yes
    -o lmtp_data_done_timeout=1200

Und zuletzt müssen wir die Mail, die Amavis uns zurückschiebt wieder in die Mail-Queue von Postfix zurückschieben:

# inject mail back into postfix after content filter
localhost:10025 inet n -        n       -       2       smtpd
    -o content_filter=
    -o myhostname=rikki.orca-central.de
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
    -o smtpd_authorized_xforward_hosts=127.0.0.0/8

Jetzt noch die Postfixkonfiguration neu laden, und die Dienste beim Systemstart hochfahren:

rc-update add amavisd start
postfix reload

Jetzt sollte alle Mail durch den Spamfilter geleitet werden.

Eingehende Mails mit Fetchmail abholen

In den meisten Fällen wird die Maildomain nicht auf dem eigenen Server liegen, sondern über eine POP3 Mailbox beim Provider abgewickelt. Um diese Mail in den Server zu bekommen benötigt man einen POP3-Client. Ich nehme hierfür den Klassiker fetchmail, getmail und Co. eigenen sich aber ebenso gut. Die Konfiguration ist debkbar einfach:

Zunächst wird fetchmail installiert:

emerge -av fetchmail

Dann erstellen wir die Datei /etc/fetchmailrc:

poll pop3server.beimProvider.tld protocol pop3
        user "meineAdresse@beimProvider.tld" there with password "passwort" is localuser here
        with options rewrite mimedecode pass8bits no keep flush

"meineAdresse@beimProvider.tld" und "passwort", werden dabei von den Daten eures Pop3-Accounts ersetzt, und "localuser" ist der Name der Mailbox in dbmail. Ein solcher Eintrag wird für jede Mailbox angelegt, die abgefragt werden soll.

Die Datei /etc/fetchmailrc darf nur dem Benutzer root zugänglich sein:

chmod 0600 /etc/fetchmailrc

Nun starten wir den Dienst:

rc-update add fetchmail default
/etc/init.d/fetchmail start

Spamassassin optimieren

Spamassassin filtert zwar jetzt jede eingehende Mail, aber die meisten Spams kommen durch: Zum einen ist der Bayes-Filter noch nicht trainiert, zum anderen sind die RBL und DNS Filter default mäßig abgeschaltet, und das Scoring ist sehr vorsichtig. Damit sich das ändert, modifizieren wir die Datei /etc/spamassassin/local.cf:

#   Use Bayesian classifier (default: 1)
#
use_bayes 1


#   Bayesian classifier auto-learning (default: 1)
#
bayes_auto_learn 1

use_razor2 1
# Tells SA that we want to use Razor version 2 

use_dcc 1
# In case you want DCC.
dcc_add_header 1
# DCC header in case you want it.
dns_available yes
# If you are sure you have DNS access set it to "yes".

## Optional Score Increases
score DCC_CHECK 4.000
score RAZOR2_CHECK 2.500
score BAYES_99 5.300
score BAYES_90 4.500
score BAYES_80 4.000
## For scores have a look at /usr/local/share/spamassassin/50_scores.cf
## file.
score HTML_FONT_INVISIBLE 3
score HTML_FONTCOLOR_UNKNOWN 2
score ORDER_NOW 1.5
score CLICK_BELOW 1
score LIMITED_TIME_ONLY 1
## This rule might be extreme but html only spams get through too easy.
## In other words, if you can't take the time to write something and are
## posting an image only, then you're 86'd!
score HTML_IMAGE_ONLY_02 2
score HTML_IMAGE_ONLY_04 2
score OFFERS_ETC 2
score HTML_LINK_CLICK_HERE 1
score LINES_OF_YELLING 1

Nun brauchen wir noch einen bequemen Weg, den Benutzern zu ermöglichen, False Positives oder False Negatives zu melden. Anstatt dafür einen eigenen Mailaccount einzurichten, an den die User dann weiterleiten müssen, erzeugen wir einfach ein Verzeichnis "LearnAsHam", und eines "LearnAsSpam" in jedem Mailaccount ein. Dann lassen wir fetchmail diesen Ordner regelmäßig auslesen, und das Ergebnis an sa-learn, das Lernscript von spamassassin, weiterzugeben. Freundlicherweise löscht fetchmail die Mails nach dem Lernen auch noch für uns.

In /etc/fetchmailrc we add the following lines for each user:

poll host.lokaledomain.tld protocol IMAP:
  user myusername with password mypassword
  with folder LearnAsSpam fetchall no keep mda "/usr/bin/sa-learn --spam"

poll host.lokaledomain.tld protocol IMAP:
  user myusername with password mypassword
  with folder LearnAsHam fetchall no keep mda "/usr/bin/sa-learn --ham"

Nun noch schnell den fetchmail Dienst neu starten:

 /etc/init.d/fetchmail restart


Viel Spaß beim Mailen!


Firewall einrichten

Unser Mailserver läuft nun, alle Mails sind gesichert. Zeit, die Router und Firewallfunktionen auf das neue System zu holen. Am einfachsten wäre es natürlich den Router in der Dom0 laufen zu lassen, dann bräuchten wir kaum noch was zu machen. Das hat aber den Nachteil, daß ein etwaiger Einbruch auf den Router dem Angreifer gleich auch noch die Kontrolle über unsere DomU's überlässt. Also bauen wir auch für den Router eine neue DomU auf. Dazu gehen wir genauso vor wie unter Unpriviligierte Domänen einrichten beschrieben.

Wenn ihr alles richtig gemacht habt, gibt es jetzt eine neue Domain, die z.B. router heißt. Leider können wir mit der noch nicht so wahnsinnig viel anfangen, denn leider ist sie an unsere xenbr0 bridge gebunden, die nur zugriff auf eth0 erlaubt, also das Interface, auf dem unsere internes Netz läuft. Wir müssen also die zweite Netzwerkkarte konfigurieren. Da auf eth1 vermutlich kein DHCP-Sever läuft, sollten wir zuerst auf unserer Dom0 eine Konfiguration einrichten; Gentoo nimmt nämlich ansonsten DHCP an, und braucht daher beim starten ewig. Am besten gehen wir dabei auf ein Netz, daß es nicht gibt, drauf zugreifen wollen wir ja eh' nicht. Ich habe dafür folgende Zeile in /etc/conf.d/net eingefügt:

config_eth1=( "192.168.121.254/24" )

,wobei es das genannte Netz natülich nicht gibt. Wenn ihr versucht dieses Netz anzusprechen, kommt dann "Host unreachable".

Jetzt noch automatisch starten:

rc-update add net.eth1 default

Jetzt müssen wir Xen mitteilen, daß wir eine weitere Bridge haben wollen, die auf eth1 läuft. Dazu brauchen wir ein kleines Script; bei mir heißt das /etc/xen/scripts/network-multibridge:

#!/bin/sh
dir=$(dirname "$0")
"$dir/network-bridge" "$@" vifnum=0 netdev=eth0 bridge=xenbr0
"$dir/network-bridge" "$@" vifnum=1 netdev=eth1 bridge=xenbr1

Das Script ruft zweimal nacheinander das Originalscript auf, und ezeugt die nötigen bridges für die Interfaces. Nicht vergessen das Script ausführbar zu machen:

chmod u+x /etc/xen/scripts/network-multibridge

In der Datei /etc/xen/xend-config.sxp ändern wir die Zeile

(network-script network-bridge)

in

(network-script network-multibridge)

Jetzt ist es am günstigsten, die Appliance einmal neu zu starten, damit xend Gelegenheit hat, das Netzwerk neu zu konfigurieren.

Nach dem Neustart sollte brctl show folgende Ausgabe präsentieren:

bridge name     bridge id               STP enabled     interfaces
xenbr0          8000.feffffffffff       no              vif0.0
                                                        peth0
                                                        vif1.0
xenbr1          8000.feffffffffff       no              vif0.1
                                                        peth1

xenbr0 hat ein Interface mehr, weil dort ja unser Mailserver schon eines angelegt hat.

Als nächstes ändern wir die vif Einstellungen in /etc/xen/router:

vif = ['mac=00:16:3e:70:01:01,bridge=xenbr0','mac=00:16:3e:70:02:01,bridge=xenbr1'];

Die MAC-adressen könnt ihr euren Wünschen anpassen, sie sind beliebig (solange sie nicht mit anderen MAC-Adressen im Netz kollidieren).

Wenn ihr die DomU das nächste mal startet, hat diese dann zwei Netzwerkinterfaces. Nun müssen wir diese Interfaces konfigurieren. Da dies unser Router werden soll, macht es natürlich wenig Sinn ihn mit DHCP zu betreiben (er soll ja der DHCP Server werden). Die Konfiguration in /etc/conf.d/net sollte daher so aussehen:

config_eth0=( "192.168.120.251/24" )
config_eth1=( "133.1.1.1/248" ) #Diese Adresse bitte durch die eigene ersetzen, falls ihr dynamische Adressen habt: "dhcp"

routes_eth0=( "default via 192.168.120.1" ) # Diese Route sollte auf euren bisherigen Router zeigen, sie ist nur da, damit wir 
                                            # ins Internet könenn, bevor wir mit dem umbauen fertig sind, und wird nachträglich         
                                            # entfernt.

Als nächstes sollten wir unserer Firewall einen Syslogger spendieren.

emerge -av syslog-ng
rc-update add syslog-ng default
/etc/init.d/syslog-ng start

Damit wir die netfilter Module des Kernels nutzen können, brauchen wir erstmal ein Frontend dafür. Dieses heißt iptables und wird bei Gentoo mit

emerge -av iptables 

installiert.

Nun konfigurieren wir unsere beiden Interfaces so, daß auf dem externen Interface IP-Masquerading durchgeführt wird. Dazu flushen wir erstmal alle Tabellen, um dann das Masquerading einzurichten. Die übrigen Zeilen aktivieren das IP-Forwarding und stellen eine Verbindung zwischen den Interfaces her:

iptables -F; iptables -t nat -F; iptables -t mangle -F
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/conf/all/forwarding
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m state --state NEW -i ! eth0 -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -P INPUT DROP

Wer paranoid ist, läßt die vorletzte Zeile weg, dann läßt sich der Router aus dem Internet nicht mehr anpingen, sondern dropped ICMP-Traffic. Während des Testens ist es aber ganz praktisch den Eintrag zu belassen.

Als nächstes müssen wir die Datei /etc/sysctl.conf anpassen. Die Zeile

# net.ipv4.ip_forward = 0

wird wieder einkommentiert (# weg) und die Null durch eine 1 ersetzt.

net.ipv4.ip_forward = 1

Damit erledigt Gentoo das echo 1 > /proc/sys/net/ipv4/conf/all/forwarding beim Systemstart für uns. Nun stellen wir noch sicher, daß unsere Regeln erhalten bleiben:

/etc/init.d/iptables save

Damit das ganze auch gestartet wird, wenn der Firewall neu bootet, das (sattstam bekannte)

/etc/init.d/iptables start
rc-update add iptables default

Der erste Befehlt startet iptables. Das ist zwar technisch gesehen nicht nötig, hat aber den Vorteil, daß schon beim nächsten runterfahren Änderungen, die wir an den Netfilter-Regeln jetzt noch durchführen, beim nächsten Shutdown automatisch gespeichert werden.

Unser Firewall ist damit eigentlich fertig eingerichtet. Aber da wir uns ja mit diesem Ding das Leben erleichtern wollen, sollten wir unbedingt noch einen wichtigen Dienst installieren: einen DHCP Dienst. Dieser erlaubt uns, beliebige Rechner mit einem Minimum an Konfgurationsaufwand in unser Netz einzubinden. Ich habe mich dabei für dnsmasq entschieden. Der ist leichter zu konfigurieren als z.B. ICS DHCPD, und liefert uns gleich noch einen lokalen DNS Dienst mit, der den Komfort der Namensauflösung bietet, ohne daß wir uns mit den Fallstricken von bind das Hirn verwinden müssen:

emerge -av dnsmasq

Einen Nachteil hat dnsmasq aber: Wir brauchen eine gültige /etc/resolv.conf, ihr müsst also die Nameserver eures Providers abfragen (praktischerweise kann man aber auch andere Nameserver benutzen, z.B. wenn ihr Acror-Kunden seid, und trotzdem YouPorn.com anschauen wollt ;-) ).

# /etc/resolv.conf
domain = meine.Domain.lan
nameserver = 127.0.0.1
nameserver = <PrimärerNameserverDesProviders>
nameserver = <SekundärerNameserverDesProviders>

Nun müssen wir noch /etc/dnsmasq.conf editieren, in der Defaultdatei sind alle Zeilen auskommentiert, die Zeilen die ich angebe sollte man also vorher heraussuchen, und abändern.

Da wir gute Netzbürger sind, und nicht andere DNS-Server mit Anfragen an unser lokales Netz nerven wollen, sorgen wir erstmal dafür das Anfragen, die nur einen Hostnamen enthalten, nicht ins Internet weitergegeben werden, wenn dnsmasq den Rechner selber nicht kennt. Das heißt natürlich, wir müssen immer brav die FQDNs angeben, wenn wir ins Internet wollen.

domain-needed

Den umgekehrten Weg gibts natürlich auch: Reverse-DNS Anfragen auf lokale Netzadressen gehen auch nicht an die externen Nameserver.

bogus-priv

Wer eine Wählleitung hat (Dial-On-Demand), und Windowsrechner betreibt, sollte mit

filterwin2k

sicherstellen, daß das broadcastwütige Windows 2000 (und höher) nicht wegen jedes SMB-Pakets eine Verbindung ins Internet aufmacht.

Anfragen beantworten wir natürlich nur aus dem lokalen Netz:

interface=eth0

Wir können dnsmasq einen Domänennamen geben, wenn sich ein Client dann z.B. mit DHCP eine Adresse holt, und selber nur einen Hostnamen spezifiziert, kriegt der diese Domäne automatisch angehängt.

domain=meineDomaene.lan

Jetzt kommt der eigentlich wichtige Teil: DHCP. Mit dem nächsten Befehl sagen wir dnsmasq welche Adressen es vergeben darf.

dhcp-range=192.168.121.20,192.168.121.150,255.255.255.0,12h

Diese Zeile sagt z.B, daß die Adressen 192.168.121.20..192.168.121.150 von dhcp verwaltet werden.

Manchmal ist es nützlich, einem Rechner immer dieselbe Adresse zuzuweisen. Ich mache das zum Beispiel bei dem virtuellen Mailserver so. Mit dhcp-host kann man erwzingen, daß einer bestimmten MAC-Adresse immer dieselbe IP zugewiesen wird. Bei dem Mailserver ist das Praktisch, weil ich mich so darauf verlassen kann, daß der Server stets dieselbe IP bekommt, ohne,daß ich an dem Server selbst was konfigurieren muß.

dhcp-host= 00:13:AE:24:98:C7,192.168.121.11

Ihr könnt soviele Einträge dieser Art machen, wie ihr möchtet. Achtet nur darauf, dieselbe Adresse nicht zweimal zu vergeben.

Einige Rechner, wie zum Beispiel der DHCP-Server selber, müssen feste IP-Adressen haben. Dnsmasq wertet /etc/hosts aus, löst also auch Rechnernamen auf, die dort gelistet sind. Dafür sollten die Optionen

local=/meineDomain.lan/
expand-hosts

eingeschaltet sein.

Man kann den Server dazu veranlassen sich beim Start alle Leases zu krallen, die irgendwo im Netz gebroadcastet werden. Das kann Timeoutwartezeiten verkürzen, sollte aber nur angewendet werden, wenn ihr wirklich der einzige DNS Dienst in eurem Netz seid.

dhcp-authorative

Als letztes sollten wir noch ein paar Optionen mitgeben, z.B. die Information, daß wir der Nameserver sind, und gleichzeitig die Defaultroute anbieten. Die letzte Zeile ist nur für Windows-User Interessant: dnsmasq mimt auch einen WINS-Dienst, was dem Windowsrechner damit mitgeteilt wird.

dhcp-option=3,192.168.121.251                             
dhcp-option=6,192.168.121.251                             
dhcp-option=44,192.168.121.251

Nun ist unser Router fertig. Jetzt muss nur noch der Dienst gestartet werden, und es kann losgehen:

/etc/init.d/dnsmasq start
rc-update add dnsmasq default