Ubuntu-Server 9.04 installieren

Die Überschrift sagt es bereits -- es geht darum, einen Ubuntu-Server für den Einsatz als Web-/Emailserver auf einem bei Hetzner angemieteten Server zu installieren.

Ich schreibe hier einfach mal in Stichworten mit, damit ich später nicht so viel Arbeit habe, wenn ich noch so einen Server installieren muss. Bei der Hardware handelt es sich um einen im August 2009 bei Hetzner angemieteten "Root Server EQ 4".

Vielleicht hilft es ja auch jemandem der hier mitliest -- aber eigentlich ist folgende Auflistung nur für mich selbst gedacht. Sie soll mir helfen, so einen Server noch einmal installiert zu bekommen.

Inhalt

Paketquellen auf den neuesten Stand bringen und System upgraden

aptitude update
aptitude full-upgrade

Midnight Commander installieren

aptitude install mc

konfigurieren nicht vergessen

screenrc anpassen

Beispiel: screenrc

cd /etc
mv screenrc screenrc_original
wget http://halvar.at/krimskrams3/linux/screenrc

Meine screenrc habe ich mir von Gentoo geliehen und ein wenig angepasst.

rcconf installieren

aptitude install rcconf

Damit kann man auf einfache Weise einstellen, welche Daemons beim Start des Systems gestartet werden sollen.

Passwort für "root" einstellen

passwd

Neuen Benutzer zum Arbeiten anlegen

adduser <Benutzername>

sudoers bearbeiten:

root<-->ALL=(ALL) ALL
%sudo ALL=NOPASSWD: ALL

Neuen Benutzer zur Gruppe "sudo" hinzufügen:

usermod --groups sudo --append <neuer Benutzername>

Die Datei /etc/bash.bashrc anpassen

rm, cp und mv etwas entschärfen:

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

Ich will, dass sich dir genau so verhält:

alias dir='ls -al --color --group-directories-first'

"bash completion" aktivieren:

if [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
fi

Skripte-Ordner erstellen und zum Pfad hinzufügen

mkdir /scripts
cd /etc/profile.d
touch scriptpath.sh
mcedit scriptpath.sh
#!/bin/sh

PATH=$PATH:/scripts
export PATH

findstring-Tool

cd /scripts
touch findstring
mcedit findstring
#!/bin/sh

#
# Sucht nach Dateien in denen der Suchstring vorkommt
#

find -type f -exec grep -iq "${1}" {} \; -print
chmod +x findstring

NICHT installieren

..., da später die aktuelleste Version händisch installiert wird:

  • cherrypy
  • mod_wsgi
  • cheetahtemplate

MySQL-Server installieren

aptitude install mysql-server mysql-client

Nach der Installation wird nach dem MySQL-"root"-Passwort gefragt. Das ist nicht das Passwort für den Unix-Benutzer, sondern für den MySQL-Benutzer root@localhost.

Auf UTF-8 umstellen

/etc/mysql/my.cnf:

[mysqld]:

collation_server = utf8_unicode_ci
character_set_server = utf8

phpMyAdmin installieren

aptitude install phpmyadmin

Während der Installation muss das MySQL-"root"-Passwort und ein neues MySQL Anwendungspasswort für phpMyAdmin angegeben werden.

Netzwerk: Zusätzliche IP-Adressen einstellen

Früher hatte ich das immer direkt in der Datei /etc/network/interfaces mit eth0:1 usw. erledigt. Aber diesmal nimmt der Computer die zusätzlichen IP-Adressen immer erst nach einem Neustart des Netzwerkes (/etc/init.d/network restart) an. Das ist natürlich nicht tragbar. Deshalb habe ich mich dafür entschieden, die zustätzlichen IP-Adressen mit ifconfig ... add ... zu setzen. Und damit dieser Befehl auch immer zum richtigen Zeitpunkt ausgeführt wird, habe ich diesen im Ordner /etc/network/if-up.d in eine neue Datei gelegt.

Neue Datei im Ordner /etc/network/if-up.d erstellen:

cd /etc/network/if-up.d
touch additional-addresses
chmod +x additional-addresses

/etc/network/if-up.d/additional-addresses:

#! /bin/bash

PATH=/sbin:/bin

ifconfig eth0 add <neue IP-Adresse>
ifconfig eth0 add <neue IP-Adresse>
ifconfig eth0 add <neue IP-Adresse>

Webmin installieren

http://webmin.com/

Installationsdatei (deb-Paket) herunterladen

Abhängigkeiten installieren:

aptitude install libnet-ssleay-perl libauthen-pam-perl \
libio-pty-perl libio-pty-perl libmd5-perl

Webmin selbst kann dann mit dpkg installiert werden:

dpkg -i webmin_x.xxx_all.deb

Webmin über den Browser konfigurieren

Thema einstellen

Sprache einstellen

Unnötige Module löschen:

z.B: ADSL-Client, CD-Brenner, Druckerverwaltung, Exim Mailserver, Heartbeat-Monitor, LILO - Boot-Konfiguration, QMail Mailserver, Sendmail Mailserver, WU-FTP FTP-Server, NIS-Client und -Server, Systemzeit, Volume-Management (LVM)

Das Systemzeit-Modul wird auch entfert, da ich das lieber direkt von Ubuntu erledigen lassen möchte. Webmin soll hier nicht rein pfuschen. Siehe weiter unten: "Zeitsynchronisation"

Zeitsynchronisation (NTP) installieren und konfigurieren

http://doc.ubuntu.com/ubuntu/serverguide/C/NTP.html

aptitude install ntp

Zeitserver in deiner Nähe sind unter http://www.pool.ntp.org/ zu finden.

Die zu verwendenden Zeitserver werden in der Datei /etc/ntp.conf eingestellt:

server de.pool.ntp.org
server at.pool.ntp.org
server ch.pool.ntp.org

Das Skript /etc/network/if-up.d/ntpdate wird immer dann ausgeführt, wenn das Netzwerk aktiviert wird. Das heißt, dass schon in der Standard- Einstellung des Ubuntu-Servers bei jedem Start des Servers die Uhrzeit eingestellt wird. Das hier genannte Skript ruft den Befehl ntpdate-debian auf. Die Einstellungen für diesen Befehl befinden sich in der Datei /etc/default/ntpdate.

Der Daemon ntpd ist dazu da, die Systemzeit im Hintergrund ständig in ganz kleinen Schritten an die reale Zeit anzupassen.

unattended-upgrades installieren

aptitude install unattended-upgrades

Nachdem der Emailserver installiert wurde, muss die Datei /etc/apt/apt.conf.d/50unattended-upgrades angepasst werden, dass Emails bei Störung an den Admin geschickt werden.

apticron installieren

aptitude install apticron

apticron will configure a cron job to email an administrator information about any packages on the system that need updated as well as a summary of changes in each package.

http://doc.ubuntu.com/ubuntu/serverguide/C/automatic-updates.html

Nagios (Serverüberwachung) installieren und konfigurieren

aptitude install nagios3

Die Einstellungen befinden sich unter /etc/nagios3

Passwortdatei für den Zugriff auf die Nagios-Seite erstellen:

htpasswd -c /etc/nagios3/htpasswd.users <Benutzername>

Evt. muss man Nagios und den Apachen neu starten:

/etc/init.d/nagios3 restart
/etc/init.d/apache2 restart

Die Nagios-Seite ist nun unter http://<Domäne>/nagios3 erreichbar

SMART installieren

Festplattenüberwachung

aptitude install smartmontools

Die Daten können mit dem Kommandozeilenprogramm smartctl oder über Webmin ausgelesen werden.

jnettop und iftop installieren

Beides sind Überwachungsprogramme um feststellen zu können, wieviel so an Daten über die Netzwerkkarten übertragen werden.

aptitude install jnettop iftop

Emailsystem mit Postfix und Cyrus

Das Emailsystem ist so geplant: Postfix empfängt die Email und legt diese in die eigene Queue. Dann wird die Email an Spamassassin übergeben. Dabei werden noch keine Zuordnungen zu irgendwelchen Emailboxen gemacht. Der Header wird nicht verändert. Spamassassin scannt die Email, schreibt den Spam-Level in den Emailheader und gibt die Email wieder an Postfix zurück. Jetzt legt Postfix fest, wohin die Email ausgeliefert werden soll. Postfix übergibt die Email an Cyrus. Cyrus übernimmt die Email und ordnet sie in das zuständige Postfach ein. Ist eine Sieve-Regel definiert, dann ist diese dafür zuständig, dass die Email evt. in einen speziellen Ordner wie z.B. den Spam-Ordner des Benutzers verschoben wird.

"saslauthd" wird nicht als Daemon benötigt. Es wird direkt auf die SASL-Datenbank zugegriffen.

Hinweis

Zuerst wird ein funktionierender Verbund aus Postfix und Cyrus eingerichtet. Erst wenn diese Kombination getestet wurde und funktioniert, wird Spamassassin als Filter aktiviert. Wie man Spamassassin aktiviert, wird weiter unten erklärt.

Postfix, Cyrus und SASL installieren und konfigurieren

aptitude install postfix postfix-doc
aptitude install sasl2-bin
aptitude install cyrus-imapd-2.2
aptitude install cyrus-pop3d-2.2
aptitude install cyrus-admin-2.2

Erklärungen zu den SASL-Postfix-Einstellungen sind unter http://www.postfix.org/SASL_README.html zu finden.

/etc/cyrus.conf:

imap cmd="imapd -U 30" listen="imap" prefork=0 maxchild=100
pop3 cmd="pop3d -U 30" listen="pop3" prefork=0 maxchild=50
#nntp cmd="nntpd... (wird nicht benötigt)
lmtpunix cmd="lmtpd" listen="/var/run/cyrus/socket/lmtp" prefork=0 maxchild=20

/etc/imapd.conf:

altnamespace: yes
admins: cyrus
sieveusehomedir: false
allowplaintext: yes
sasl_pwcheck_method: auxprop
lmtpsocket: /var/run/cyrus/socket/lmtp

altnamespace: yes kümmert sich darum, dass die IMAP-Ordner nicht unterhalb des INBOX-Ordners sind. Ich empfinde das im Thunderbird als angenehmer. Aber das ist reine Geschmackssache.

/etc/postfix/master.cf:

Lmtp so einstellen, dass die Übermittlung der Emails an Cyrus nicht in einer CHROOT-Umgebung läuft:

lmtp      unix  -       -       n       -       -       lmtp

Man kann zum Debuggen "-v" als Parameter an "smtpd" anhängen. Das sollte man aber wirklich nur zum Debuggen verwenden. Mit tail -f /var/log/syslog kann man mitverfolgen, was der Server so tut, wenn man vesucht, darüber ein Email zu verschicken.

debuggen:

smtp      inet  n       -       -       -       -       smtpd -v

normal:

smtp      inet  n       -       -       -       -       smtpd

/etc/postfix/main.cf:

smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain = <SASL-Domäne>
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
broken_sasl_auth_clients = yes

virtual_alias_domains = <Domänen die angenommen werden sollen>
virtual_alias_maps = hash:/etc/postfix/virtual
mailbox_transport = lmtp:unix:/var/run/cyrus/socket/lmtp
myhostname = <FullQualifiedDomainName>
mydomain = <FullQualifiedDomainName>

Mit smtpd_sasl_local_domain wird eingestellt, welche Domäne von SASL beim Durchsuchen der SASL-Datenbank verwendet wird. Diese Domäne ist (glaube ich) der Hostname des Servers. Den findet man mit cat /etc/hostname heraus. Ansonsten, wenn man schon einen Benutzer in die SASL-Datenbank (mit saslpasswd2 -c <Benutzername>) geschrieben hat, dann kann man mit sasldblistusers2 herausfinden, welches die Standarddomäne für SASL ist. Gibt man diese Einstellung nicht oder falsch an, dann bekommt man so nette Debugmeldungen von Postfix wie z.B. "...no secret in database...". Nicht lachen, das hat mich mindestens vier Stunden gekostet, bis ich das heraus fand. myhostname und mydomain sollten auf den voll qualifizierten Domain Name des Servers eingestellt werden. Wichtig ist, dass dieser Domain Name bei einem Reverse Lookup der IP-Adresse des Servers heraus kommt.

Datei-Zugriffsrechte für Postfix einstellen

Postfix erlauben, auf den LMTP-Socket und die SASL-DB zuzugreifen:

adduser postfix mail
adduser postfix sasl

Hardlink für Postfix (chroot) zur SASL-DB erstellen:

ln /etc/sasldb2 /var/spool/postfix/etc/sasldb2

Postfix und Cyrus neu starten

/etc/init.d/postfix restart
/etc/init.d/cyrus2.2 restart

Admin-Benutzer für Cyrus erstellen

saslpasswd2 -c cyrus

sicheres Passwort für den Benutzer vergeben und merken

Mit sasldblistusers2 kann man prüfen ob der Benutzer erfolgreich erstellt wurde und welcher Domäne er zugewiesen wurde. Diese Domäne muss in /etc/postfix/main.cf als Einstellung smtpd_sasl_local_domain gesetzt werden, falls das noch nicht passiert ist.

Test-Emailaccount erstellen und ausprobieren

Zum Testen habe ich mir die Subdomain "test.halvar.at" erstellt und auf die primäre IP-Adresse des Servers zeigen lassen. Auch der MX-Eintrag für "test.halvar.at" zeigt auf diese Adresse.

Die neue Domäne muss in /etc/postfix/main.cf eingetragen werden:

virtual_alias_domains = test.halvar.at

Damit wird der Benutzer in der SASL-DB erstellt:

saslpasswd2 -c testertest

Das Passwort wird interaktiv abgefragt.

Weiter geht's mit dem Erstellen des Postfaches:

cyradm --user cyrus localhost

localhost> cm user.testertest
localhost> exit

Hinweis

Mit setacl können die Zugriffsrechte des Benutzers auf das Postfach eingestellt werden. Statt "all" kann man die Berechtigungen auch detaillierter angeben:

  • l: Lookup (visible to LIST/LSUB/UNSEEN)
  • r: Read (SELECT, CHECK, FETCH, PARTIAL, SEARCH, COPY source)
  • s: Seen (STORE SEEN)
  • w: Write flags other than SEEN and DELETED
  • i: Insert (APPEND, COPY destination)
  • p: Post (send mail to mailbox)
  • c: Create and Delete mailbox (CREATE new sub-mailboxes, RENAME or DELETE mailbox)
  • d: Delete (STORE DELETED, EXPUNGE)
  • a: Administer (SETACL)

Jetzt muss man Postfix noch mitteilen, dass Emails an tester@test.halvar.at an das Postfach testertest übergeben werden sollen. Das kann man im Webmin mit "Postfix/Virtuelle Domänen" erledigen. Siehe:

Webmin

Das erzeugt in der Datei /etc/postfix/virtual folgenden Eintrag:

tester@test.halvar.at       testertest

Man kann das jetzt weiter spinnen, indem man z.B. auch für "root" einen Eintrag erstellt und diesen über die /etc/aliases zu diesem Postfach leitet.

/etc/postfix/virtual:

root@test.halvar.at testertest

/etc/aliases:

root: root@test.halvar.at

Spamassassin

Spamassassin wird als Filter für Postfix aktiviert. Dazu werden die Emails, die über SMTP (inet) hereinkommen, an den Spamassassin-Client (spamc) übergeben. spamc parst die Email (die Arbeit macht spamd) und schreibt den Spam-Status in den Header. Dann sendet spamc die Email (mit sendmail) wieder an Postfix zurück. Da Postfix die lokalen Emails nicht filtert, wird das Email von Postfix direkt an Cyrus übergeben.

aptitude install spamassassin re2c libc6-dev gcc make

Spamassassin-Server aktivieren und konfigurieren

Spamassassin läuft zwar auch als einfaches Kommandozeilenprogramm, aber schneller geht's als Server (spamd).

/etc/default/spamassassin:

ENABLED=1
CRON=1

/etc/spamassassin/local.cf:

report_safe 0

Spamassassin-Benutzer erstellen

Mit diesem Befehl wird ein Benutzer erstellt, der sich nicht einloggen kann, da ein "!" als Passwort eingestellt wird.

adduser spamassassin

Spamassassin speichert Einstellungen im Home-Ordner dieses Benutzers und das Programm spamc wird in dessen Kontext ausgefürt.

Spamassassin in Postfix integrieren

/etc/postfix/master.cf:

smtp            inet  n       -       -       -       -       smtpd
    -o content_filter=spamassassin

spamassassin    unix  -       n       n       -       -       pipe
    user=spamassassin
    null_sender=
    argv=/usr/bin/spamc -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

Spam automatisch in einen eigenen IMAP-Ordner verschieben

Zuerst muss ein entsprechender IMAP-Ordner für den Benutzer erstellt werden.

cyradm --user cyrus localhost

localhost> cm user.testertest.Spam
localhost> exit

Dann muss man eine Textdatei mit der Sieve-Regel erstellen. Diese wird dann mit dem Programm sieveshell in den Sieve-Ordner des Benutzers kopiert und aktiviert.

mkdir /etc/sieve_templates
touch /etc/sieve_templates/spam

/etc/sieve_templates/spam:

require "fileinto";

if header :contains "X-Spam-Level" "******" {
    fileinto "Spam";
    stop;
}

Mehr Informationen über Sieve-Regeln bekommst du hier:

Mit dem Programm sieveshell kopiert man nun die Vorlage in den Sieve-Ordner des Benutzers:

sieveshell --user=testertest --authname=cyrus localhost

Man wird nach dem Passwort für den Cyrus-Benutzer gefragt.

> put /etc/sieve_templates/spam
> activate spam
> quit

Das Programm sieveshell kann auch mit einem Skript als Parameter aufgerufen werden. Siehe man sieveshell.

Backup

Ziel ist es, zuerst einmal alle wichtigen Daten eine Woche lang auf Lager zu halten. Falls mal eine Datei gelöscht wird, kann man diese bis zu einer Woche rückwirkend wieder herstellen.

Zweites Ziel ist es, eine Notfallsicherung auf dem Hetzner-Backup-Server zu halten, damit ein totaler Systemausfall (z.B. Festplattencrash oder Controllerfehler) nicht den Totalverlust aller Daten bedeutet.

Die zu sichernden Ordner sind (diesesmal):

  • etc
  • home
  • root
  • scripts
  • var/log
  • var/spool

Gesichert wird in den /backup-Ordner. Zum Sichern wird tar eingesetzt. Es soll einstellbar sein, wie lange rückwirkend gesichert wird.

Zum Verschlüsseln der Sicherungen, bevor diese zum FTP-Server übertragen werden, dient CCrypt:

aptitude install ccrypt

Programm: copy_to_ftp.py

Dieses kleine Programm kopiert die letzten Sicherungen zum Backup-FTP-Server von Hetzner. Das Programm ist einfach aufgebaut. Man muss nur ein paar Konstanten anpassen und die Passwörter für den FTP-Zugang und zum Verschlüsseln der Sicherungen in zwei Textdateien hinterlegen.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import ftplib
from cStringIO import StringIO
import subprocess
import tempfile

FTP_HOST = "<IP-Adresse des FTP-Backup-Servers>"
FTP_USER = "<Benutzername fuer den FTP-Backup-Server>"
FTP_PASS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ftppasswd")
FTP_PASS = file(FTP_PASS_FILE, "r").readline().strip()
CCRYPT_PASS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ccryptpasswd")
PLACEHOLDER = "_placeholder"
CCRYPT = "/usr/bin/ccrypt"

MYSQL_SOURCE_DIR = "/backup/mysql"
MYSQL_SOURCE_CURRENT_FILE = "/backup/mysql/.current"
MYSQL_DEST_DIR = "mysql"
MYSQL_DEST_CURRENT_FILE = "current"
MYSQL_DEST_MAX_BACKUPS = 10

FILES_SOURCE_DIR = "/backup/files"
FILES_SOURCE_CURRENT_FILE = "/backup/files/.current"
FILES_DEST_DIR = "files"
FILES_DEST_CURRENT_FILE = "current"
FILES_DEST_MAX_BACKUPS = 2


def copy_to_ftp(
    source_dir, source_current_file, dest_dir, dest_current_file, dest_max_backups
):
    # Verbinden
    ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)

    # Platzhalter-Datei im Root-Ordner erstellen/ueberscheiben
    ftp.storbinary("STOR %s" % os.path.join("/", PLACEHOLDER), StringIO())

    # Basisordner erstellen
    if (
        os.path.join("/", dest_dir) not in
        (os.path.join("/", item) for item in ftp.nlst("/"))
    ):
        ftp.mkd(dest_dir)

    # Platzhalter-Datei im Basis-Ordner erstellen/ueberscheiben
    ftp.storbinary(
        "STOR %s" % os.path.join("/", dest_dir, PLACEHOLDER),
        StringIO()
    )

    # QUELLE Current-Datei auslesen
    source_current = int(file(source_current_file, "r").readline().strip())

    # ZIEL Current-Datei auslesen
    dest_current_file_path = os.path.join("/", dest_dir, dest_current_file)
    if dest_current_file_path in ftp.nlst(os.path.join("/", dest_dir)):
        current_file = StringIO()
        ftp.retrbinary("RETR %s" % dest_current_file_path, current_file.write)
        current_file.seek(0)
        try:
            dest_current = int(current_file.readline().strip()) + 1
        except ValueError:
            dest_current = 1
    else:
        dest_current = 1
    if dest_current > dest_max_backups:
        dest_current = 1

    # Pfade herausfinden
    full_source_dir = os.path.join(source_dir, str(source_current))
    full_dest_dir = os.path.join("/", dest_dir, str(dest_current))

    # Zielordner löschen
    if full_dest_dir in ftp.nlst(os.path.join("/", dest_dir)):
        for full_filename in ftp.nlst(full_dest_dir):
            ftp.delete(full_filename)
        ftp.rmd(full_dest_dir)

    # Zielordner und Platzhalter-Datei erstellen
    ftp.mkd(full_dest_dir)
    ftp.storbinary(
        "STOR %s" % os.path.join(full_dest_dir, PLACEHOLDER),
        StringIO()
    )

    # Dateien des Quellordner durchlaufen
    for filename in os.listdir(full_source_dir):
        # Verbindung schliessen
        try:
            ftp.quit()
            ftp.close()
        except: pass

        # Pfade
        full_source_filename = os.path.join(full_source_dir, filename)
        full_dest_filename = os.path.join(full_dest_dir, filename + ".cpt")

        print "Copy to FTP:", full_source_filename

        # Datei verschluesseln
        tmp_file = tempfile.TemporaryFile()
        args = [CCRYPT, "--encrypt", '--keyfile=%s' % CCRYPT_PASS_FILE]
        proc = subprocess.Popen(
            args, stdout = tmp_file, stdin = file(full_source_filename, "rb"),
            stderr = subprocess.PIPE
        )
        (stdout_str, stderr_str) = proc.communicate()
        if stderr_str:
            for line in stderr_str.splitlines():
                print "    ", line.rstrip()

        # Verbindung neu oeffnen
        ftp = ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS)

        # Datei in den Zielordner kopieren
        tmp_file.seek(0)
        ftp.storbinary("STOR %s" % full_dest_filename, tmp_file)
        tmp_file.close()

    # Ziel Current-Datei erstellen/ueberschreiben
    current_file = StringIO()
    current_file.write(str(dest_current))
    current_file.seek(0)
    ftp.storbinary("STOR %s" % dest_current_file_path, current_file)

    # Platzhalter-Dateien entfernen
    try:
        ftp.delete(os.path.join(full_dest_dir, PLACEHOLDER))
    except ftplib.error_perm:
        pass
    try:
        ftp.delete(os.path.join(dest_dir, PLACEHOLDER))
    except ftplib.error_perm:
        pass
    try:
        ftp.delete(os.path.join("/", PLACEHOLDER))
    except ftplib.error_perm:
        pass

    # Fertig
    try:
        ftp.quit()
        ftp.close()
    except: pass


def copy_mysql():
    copy_to_ftp(
        MYSQL_SOURCE_DIR, MYSQL_SOURCE_CURRENT_FILE, MYSQL_DEST_DIR,
        MYSQL_DEST_CURRENT_FILE, MYSQL_DEST_MAX_BACKUPS
    )


def copy_files():
    copy_to_ftp(
        FILES_SOURCE_DIR, FILES_SOURCE_CURRENT_FILE, FILES_DEST_DIR,
        FILES_DEST_CURRENT_FILE, FILES_DEST_MAX_BACKUPS
    )


def main():
    # Letzte MYSQL-Sicherung zum FTP-Server kopieren
    copy_mysql()

    # Letzte FILES-Sicherung zum FTP-Server kopieren
    copy_files()


if __name__ == "__main__":
    main()

Konstante: FTP_HOST

Hier trägt man die IP-Adresse des Backup-FTP-Servers von Hetzner ein.

Konstante: FTP_USER

Hier trägt man den Benutzernamen fuer den Backup-FTP-Server von Hetzner ein.

Datei: ftppasswd

In dieser Datei befindet sich das Passwort für den FTP-Zugang zum Backup-Server von Hetzner. Diese Datei sollte sich im selben Ordner befinden wie das copy_to_ftp.py-Programm.

Diese Datei darf nur für "root" lesbar sein:

chown root:root ftppasswd
chmod 600 ftppasswd

Datei: ccryptpasswd

In dieser Datei befindet sich das Passwort zum Verschlüsseln der Sicherungsdateien. Diese Datei sollte sich im selben Ordner befinden wie das copy_to_ftp.py-Programm.

Diese Datei darf nur für "root" lesbar sein:

chown root:root ccryptpasswd
chmod 600 ccryptpasswd

Programm: backup_mysql.py

Dieses Programm liest aus, welche Datenbanken es gibt und sichert jede Datenbank in eine eigene Dump-Datei (SQL). Die Sicherungen werden mit gzip gepackt. Nach den mit MAX_BACKUPS eingestellten Sicherungen wird die älteste Sicherung überschrieben.

#!/usr/bin/env python
# coding: utf-8

import os
import sys
import subprocess
import shutil
import stat


MAX_BACKUPS = 50
BACKUP_DIR = "/backup/mysql"
CURRENT_FILE = "/backup/mysql/.current"
MYSQL_ROOTPW_FILE = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "mysqlrootpasswd"
)
MYSQL = "/usr/bin/mysql"
MYSQLDUMP = "/usr/bin/mysqldump"
GZIP = "/bin/gzip"


def main():
    # MySQL-Root-Passwort auslesen
    password = file(MYSQL_ROOTPW_FILE, "r").readline().strip()

    # Current-Datei auslesen
    if os.path.isfile(CURRENT_FILE):
        try:
            current = int(file(CURRENT_FILE, "r").readline().strip()) + 1
        except ValueError:
            current = 1
    else:
        current = 1
    if current > MAX_BACKUPS:
        current = 1

    # Pfad für Sicherung
    dest_dir = os.path.join(BACKUP_DIR, str(current))
    if os.path.isdir(dest_dir):
        shutil.rmtree(dest_dir, ignore_errors = True)
    os.makedirs(dest_dir)
    os.chmod(BACKUP_DIR, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
    os.chmod(dest_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)

    args = [MYSQL, "--user=root", "--password=%s" % password]
    proc = subprocess.Popen(args, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
    proc.stdin.write("SHOW DATABASES;")
    proc.stdin.close()
    proc.stdout.readline()
    for line in proc.stdout:
        dbname = line.strip()
        print "Backup:", dbname

        # Dateiname zusammensetzen
        sql_file_name = os.path.join(dest_dir, dbname + ".sql")

        # Sichern
        cmd = MYSQLDUMP + ' --user="root" --password="%s" %s > %s'
        cmd = cmd % (password, dbname, sql_file_name)
        os.system(cmd)

        # Zippen
        cmd = GZIP + " %s" % sql_file_name
        os.system(cmd)

    # Current-Datei neu schreiben
    f = file(CURRENT_FILE, "w")
    f.write(str(current))
    f.close()


if __name__ == "__main__":
    main()

Datei: mysqlrootpasswd

In dieser Datei befindet sich das Passwort des MySQL-Root-Benutzers. Diese Datei sollte sich im selben Ordner befinden wie das backup_mysql.py-Programm.

Diese Datei darf nur für "root" lesbar sein:

chown root:root mysqlrootpasswd
chmod 600 mysqlrootpasswd

Programm: backup_files.py

Dieses Programm sichert alle Ordner, die in der Konstante SOURCE_PATHS angegeben werden, in den Zielordner (BACKUP_DIR). Nach den mit MAX_BACKUPS eingestellten Sicherungen wird die älteste Sicherung überschrieben.

Zum Sichern wird tar verwendet.

#!/usr/bin/env python
# coding: utf-8

import os
import sys
import subprocess
import shutil
import stat


TAR = "/bin/tar"
MAX_BACKUPS = 7
BACKUP_DIR = "/backup/files"
CURRENT_FILE = "/backup/files/.current"
SOURCE_PATHS = [
    "/etc",
    "/home",
    "/root",
    "/scripts",
    "/var/log",
    "/var/spool",
]


def main():
    # Current-Datei auslesen
    if os.path.isfile(CURRENT_FILE):
        try:
            current = int(file(CURRENT_FILE, "r").readline().strip()) + 1
        except ValueError:
            current = 1
    else:
        current = 1
    if current > MAX_BACKUPS:
        current = 1

    # Pfad für Sicherung
    dest_dir = os.path.join(BACKUP_DIR, str(current))
    if os.path.isdir(dest_dir):
        shutil.rmtree(dest_dir, ignore_errors = True)
    os.makedirs(dest_dir)
    os.chmod(BACKUP_DIR, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
    os.chmod(dest_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)

    # Ordner sichern
    for path in SOURCE_PATHS:
        print "Backup:", path

        # Dateiname zusammensetzen
        file_name = os.path.join(
            dest_dir, path.replace(os.sep, "_")[1:] + ".tar.gz"
        )

        # Sichern
        args = [TAR, "--create", "--file=%s" % file_name, "--gzip", path]
        proc = subprocess.Popen(
            args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT
        )
        for line in proc.stdout:
            if not (
                "Removing leading" in line or
                "socket ignored" in line
            ):
                print "    ", line.rstrip()

    # Current-Datei neu schreiben
    f = file(CURRENT_FILE, "w")
    f.write(str(current))
    f.close()


if __name__ == "__main__":
    main()

Tägliche Sicherung aller Dateien inkl. Datenbanken

Diese Sicherung sollte, auf Grund der doch recht massiven Arbeit, die beim Sichern vom Server zu verrichten ist, nur einmal am Tag durchgeführt werden. Am Besten dann, wenn der Server sowiso kaum ausgelastet ist.

Zuerst müssen die Dateien gesichert und dann zum SQL-Server übertragen werden. Danach werden die Datenbanken gesichert und ebenfalls zum SQL-Server übertragen. Ich habe dafür ein kleines Skript geschrieben, welches von Cron täglich in der Nacht ausgeführt wird.

Programm: backup_full_daily.py

#!/usr/bin/env python
# coding: utf-8

import backup_files
import backup_mysql
import copy_to_ftp


def main():

    # Dateien sichern und zum FTP-Server übertragen
    backup_files.main()
    copy_to_ftp.copy_files()

    # MySQL-Datenbanken sichern und zum FTP-Server übertragen
    backup_mysql.main()
    copy_to_ftp.copy_mysql()


if __name__ == "__main__":
    main()

/etc/crontab

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

...

# Taegliches Backup
50 1 * * *      root    /scripts/backup_full_daily.py

# Vereinzelte MySQL-Backups
43 10 * * *      root    /scripts/backup_mysql.py
43 16 * * *      root    /scripts/backup_mysql.py

Info:

man 5 crontab

Erweiterung: PostgreSQL-Backup

Backup-Benutzer erstellen

Dazu muss man zuerst von "root" zum "postgres"-Benutzer wechseln:

su - postgres

Als "postgres"-Benutzer kann man sich jetzt an die DB anmelden:

psql localhost

Im psql-Programm kann man nun mit folgenden Befehlen einen Backup-Benutzer erstellen und das Passwort vergeben:

CREATE USER backupuser WITH SUPERUSER;
\password backupuser

Man wird nun nach dem Passwort des backupuser-Benutzers gefragt.

Passwortdatei

Man kann an die im Backup-Programm benötigten Kommandozeilenprogramme psql und pg_dump kein Passwort direkt als Parameter übergeben (was auch gut ist). Stattdessen kann man mit Hilfe der Umgebungsvariable PGPASSFILE den Pfad zu einer Textdatei übergeben, die das Passwort enthält.

Die Textdatei /scripts/pgpassfile muss folgende Struktur aufweisen:

hostname:port:database:username:password

Das sieht bei mir z.B. so aus:

localhost:*:*:backupuser:DasPasswort

Dann sollte man sich noch darum kümmern, dass niemand außer root auf die Datei zugreifen kann:

cd /scripts
chown root:root pgpassfile
chmod 600 pgpassfile

Das Programm /scripts/backup_postgresql.py:

#!/usr/bin/env python
# coding: utf-8

import os
import sys
import subprocess
import shutil
import stat


MAX_BACKUPS = 50
BACKUP_DIR = "/backup/postgresql"
CURRENT_FILE = "/backup/postgresql/.current"
BACKUP_USER = "backupuser"
PGPASSFILE = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "pgpassfile"
)
PSQL = "/usr/bin/psql"
PG_DUMP = "/usr/bin/pg_dump"
GZIP = "/bin/gzip"


def main():
    # Umgebungsvariable PGPASSFILE
    os.environ["PGPASSFILE"] = PGPASSFILE

    # Current-Datei auslesen
    if os.path.isfile(CURRENT_FILE):
        try:
            current = int(file(CURRENT_FILE, "r").readline().strip()) + 1
        except ValueError:
            current = 1
    else:
        current = 1
    if current > MAX_BACKUPS:
        current = 1

    # Pfad für Sicherung
    dest_dir = os.path.join(BACKUP_DIR, str(current))
    if os.path.isdir(dest_dir):
        shutil.rmtree(dest_dir, ignore_errors = True)
    os.makedirs(dest_dir)
    os.chmod(BACKUP_DIR, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
    os.chmod(dest_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)

    args_db = [
        PSQL, "--username=%s" % BACKUP_USER, "--dbname=postgres", "--tuples-only",
        "--command=SELECT datname FROM pg_database WHERE (datname NOT LIKE 'template_');"
    ]
    proc_db = subprocess.Popen(args_db, stdout = subprocess.PIPE)
    for line in proc_db.stdout:
        dbname = line.strip()
        if not dbname:
            continue
        print "Backup:", dbname

        # Dateiname zusammensetzen
        sql_file_name = os.path.join(dest_dir, dbname + ".sql")

        # Sichern
        args_dump = [
            PG_DUMP, "--encoding=utf-8", "--no-owner", "--username=%s" % BACKUP_USER,
            "--format=plain", "--file=%s" % sql_file_name,  dbname
        ]
        proc_dump = subprocess.Popen(args_dump)
        proc_dump.communicate()

        # Zippen
        cmd = GZIP + " %s" % sql_file_name
        os.system(cmd)

    # Current-Datei neu schreiben
    f = file(CURRENT_FILE, "w")
    f.write(str(current))
    f.close()


if __name__ == "__main__":
    main()

Ordnerstruktur für Home-Ordner nach /etc/skel

Beim Erstellen eines Unix-Benutzers wird für den zugehörigen Home-Ordner eine Kopie des Ordners /etc/skel gemacht. Alle Ordner und Dateien, die bei fast allen Benutzern gleich sind, kann man dort erstellen. Das erleichtert die Arbeit ein wenig.

Apache konfigurieren

Es gibt normale "virtuelle Domänen" und einige deren Datenübertragung mit SSL geschützt werden muss (z.B. Shops). Darum kümmere ich mich später. Dann gibt es noch den Sonderfall "meine eigene Homepage". Die läuft mit CherryPy (http://cherrypy.org/) und soll per mod_wsgi (http://code.google.com/p/modwsgi/) in den Apachen eingebunden werden. Später kommt vielleicht noch Zope hinzu, welches per mod_proxy hinter dem Apachen werkeln soll.

Mit der Installation von phpMyAdmin wurde bereits PHP installiert. PHP ist also kein Thema mehr. Es gibt vielleicht noch ein paar kleine Anpassungen in der INI-Datei oder in den Einstellungen der virtuellen Domänen. Aber ich rechne hier nicht mit großen Schwierigkeiten.

Die Einstellungen des Apachen befinden sich im Ordner /etc/apache2.

Im Ordner /etc/apache2/conf.d/ sind Apache-Konfigurationen, die von Programmen beim Installieren abgelegt werden. phpMyAdmin oder auch nagios haben sich über diesen Ordner in den Apachen eingeklinkt. Alle Dateien, dieses Ordners werden an die Datei /etc/apache2/apache2.conf angehängt -- sind also eine Ergänzung der Apache-Konfigurationsdatei.

Die Datei /etc/apache2/apache2.conf ist die Haupt-Konfigurationsdatei des Apachen.

Apache-Module aktivieren

Im Ordner mods-available sind die Konfigurationen und Ladeanweisungen für die installierten Apache-Module abgelegt. Möchte man ein Modul aktivieren, dann verlinkt man die zum gewünschten Modul gehörenden Dateien in den mods-enabled-Ordner. Beim Verlinken sollte man absolute Pfade verwenden.

Ich finde es einfacher ln -s zu verwenden als immer wieder nach dem dafür vorgesehenen Apache-Programm zu suchen. Ja, das gibt es. Es heißt -- warte, ich muss mal nachsehen -- a2enmod. :-)

IP-Adresse(n) und Port(s) des Apache-Servers einstellen

Dafür ist die Datei /etc/apache2/ports.conf zuständig. Hier wird eingestellt, an welchen IP-Adressen und Ports der Apache als Server arbeiten soll. Diese Datei sieht bei mir im Moment so aus:

Listen 188.40.88.80:80
NameVirtualHost 188.40.88.80:80

<IfModule mod_ssl.c>
    # SSL name based virtual hosts are not yet supported, therefore no
    # NameVirtualHost statement here
    Listen 188.40.88.80:443
</IfModule>

Eines ist aber klar: sie wird sich im Laufe der weiteren Konfiguration auf jeden Fall noch ändern.

Apache-Sites

Man kann für jede virtuelle Domäne eine Konfigurationsdatei erstellen. Diese legt man in den Ordner sites-available. Verlinkt man die Konfigurationsdatei mit einem symbolischen Link in den Ordner sites-enabled, dann wird die Konfiguration nach einem Neustart des Apachen aktiv. Zum Verlinken kann man ln -s, oder das Apache-Programm a2ensite verwenden. Nimmt man ln -s, dann kann man zusätzlich den Namen (und somit die Sortierung) der Links bestimmen.

Die Datei /etc/apache2/sites-available/default muss man kaum verändern. Man sollte aber die Emailadresse des Server-Admins korrekt einstellen.

Virtuelle Testdomäne erstellen

Für die Emails habe ich mir bereits die Testdomäne test.halvar.at eingerichtet. Sie zeigt auf die IP-Adresse des neuen Servers. Diese kann jetzt auch zum Testen des Apachen verwendet werden.

Zuerst erstelle ich den Linux-Benutzer test_halvar_at und folgende zugehörigen Ordner:

  • /home/test_halvar_at/awstats
  • /home/test_halvar_at/public
  • /home/test_halvar_at/public/cgi-bin.
  • /home/test_halvar_at/log
  • /home/test_halvar_at/webalizer

Der public-Ordner wird der Root-Ordner der Website. Dann erstelle ich dort noch die Datei index.html und schreibe "Hallo Welt - test.halvar.at" rein. Das genügt um später feststellen zu können, ob der Apache die richtige Datei ausliefert.

Dann erstelle ich die Konfigurationsdatei für die Testdomäne. Ich kopiere mir dazu die default-Datei und passe sie nach meinen Vorstellungen an.

/etc/apache2/sites-available/test_halvar_at

#
# test.halvar.at
#
<VirtualHost 188.40.88.80:80>

    ServerAdmin gerold.penz@aon.at

    ServerName test.halvar.at
    ServerAlias www.test.halvar.at

    DocumentRoot /home/test_halvar_at/public

    <Directory /home/test_halvar_at/public>
        Order allow,deny
        Allow from all
        Options Indexes MultiViews
        IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* FoldersFirst XHTML
        AddDefaultCharset utf-8
        AllowOverride All
    </Directory>

    <Directory /home/test_halvar_at/public/cgi-bin>
        AllowOverride None
        Options +ExecCGI -Indexes -MultiViews
        Order allow,deny
        Allow from all
        AddHandler cgi-script .cgi .pl .py
    </Directory>

    # Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
    LogLevel info
    ErrorLog /home/test_halvar_at/log/apache_error.log
    CustomLog /home/test_halvar_at/log/apache_access.log combined

</VirtualHost>

Aktiviert wird diese Konfiguration mit:

a2ensite test_halvar_at
/etc/init.d/apache2 stop
/etc/init.d/apache2 start

Ein Aufruf der Adresse http://test.halvar.at/ im Browser, zeigt dann, ob alles funktioniert hat. :-)

Zum Testen des cgi-bin-Ordners, erstelle ich dort eine Datei mit dem Namen hallowelt.py. Diese muss unbedingt als "ausführbar" gekennzeichnet werden, sonst funktioniert es nicht.

cd /home/test_halvar_at/public/cgi-bin
touch hallowelt.py
chmod +x hallowelt.py

Das Testprogramm sieht so aus:

#!/usr/bin/env python
# coding=utf-8

import time

print "Content-type: text/plain"
print
print "Hallo Welt!"
print
print "Es ist jetzt %s Uhr." % time.strftime("%H:%M:%S")

Ein Aufruf der Adresse http://test.halvar.at/cgi-bin/hallowelt.py im Browser, sollte jetzt in etwa dieses hier zu tage bringen:

Hallo Welt!

Es ist jetzt 20:53:34 Uhr.

Ja! :-) Es funktioniert.

Somit ist der Apache getestet. Das Einrichten von virtuellen Domänen funktioniert. Jetzt kann ich mit der Arbeit beginnen und die echten "virtuellen Domänen" einrichten. :-)

Apache weniger Informationen

Der Apache gibt ziemlich viele Informationen bei jedem Response und in den eigenen Fehlermeldungen über sich preis. Das zu unterbinden, erschwert es vielleicht den Crackern, herauszufinden, welches OS und welche Apache-Version auf dem Server läuft.

Das kann man mit zwei Einstellungen in folgender Datei ändern.

/etc/apache2/conf.d/security:

ServerTokens Prod
ServerSignature Off

ProFTP installieren

aptitude install proftpd

Beim Installieren wird man gefragt, ob der Server "standalone" oder "from_inetd" ausgeführt werden soll. --> "standalone"

Die Einstellungen können über Webmin vorgenommen werden. Hier würde ich einstellen, dass Benutzer nur auf ihre Home-Ordner zugreifen dürfen.

Benutzer für den FTP-Zugang müssen als Unix-Benutzer angelegt werden. Man kann die Benutzer aber einschränken, indem man ihnen /bin/false als Shell zuweist und "!" oder "*" als Passwort in /etc/shadow vergibt.

Als Befehl zum Hinzufügen der Benutzer empfehle ich useradd. Denn damit wird kein Home-Ordner angelegt und als Passwort wird "!" vergeben.

## Den Home-Ordner kann man in /etc/passwd auf /nonexistent einstellen.

Das Passwort kann dann im Webmin-Fenster "Authentisierung" zugewiesen werden. So ist der Unix-Benutzer nur auf FTP beschränkt, denn für andere Anwendungen hat er kein gültiges Passwort.

Im Webmin kann man über den Punkt "Pro-Verzeichnis-Optionen hinzufügen für..." Optionen angeben, die vom Ordner abhängig sind. Ich habe hier jeden Home-Ordner vermerkt und über das Fenster "Benutzer und Gruppen" (der Pro-Verzeichnis-Option) eingestellt, dass egal wer darauf zugreift, die hochgeladenen Dateien dem Besitzer des Home-Ordners gehören.

Logrotate

Dieses Programm ist dafür zuständig, dass Logdateien nicht bis ins Nirwana anwachsen.

/etc/logrotate.d/homedirs:

/home/*/log/*.log {
    weekly
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 640 root root
    sharedscripts
    postrotate
        if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
            /etc/init.d/apache2 reload > /dev/null
        fi
    endscript
}

mod_wsgi installieren

http://code.google.com/p/modwsgi/

Quellcode herunterladen und in einen Ordner entpacken. Dann in diesen entpackten Ordner wechseln.

aptitude install apache2-dev
aptitude install python-dev
./configure
make
make install

/etc/apache2/mods-available/wsgi.load:

Das ist die Datei zum Einbinden des Modules in den Apachen.

LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi.so

Nach dem Erstellen der Datei kann man das neue Modul mit folgendem Befehl einbinden:

a2enmod wsgi

CherryPy und Cheetah installieren

aptitide install python-setuptools
easy_install cherrypy
easy_install cheetah

Webalizer installieren

Apache Logfile-Analyse

aptitude install webalizer

Die Konfigurationsdatei /etc/webalizer/webalizer.conf musste ich etwas anpassen, damit nicht jeder Spider/Robot mitgezählt wird. Außerdem musste noch die Ausgabe des Content-Type auf "utf-8" umgestellt werden. Mit AllSites, AllURLs, usw. kann man einstellen, dass nicht nur die ersten Top-Ergebnisse angezeigt werden. Ganz wichtig ist die Einstellung Incremental yes. Ist diese Einstellung nicht gesetzt, dann funktioniert das kleine Programm nicht, mit dem ich die Statisiken generieren lasse.

Diese Einstellungen habe ich hinzugefügt:

HTMLHead <META NAME="Content-Type" CONTENT="text/html; Charset=utf-8">

Incremental     yes

HideURL *.ico
HideURL *.jpeg

AllSites        yes
AllURLs         yes
AllReferrers    yes
AllAgents       yes
AllSearchStr    yes
AllUsers        yes

IgnoreAgent     webcrawler
IgnoreAgent     Jyxobot
IgnoreAgent     betaBot
IgnoreAgent     Yeti
IgnoreAgent     search.msn.com
IgnoreAgent     Baiduspider
IgnoreAgent     msnbot
IgnoreAgent     Java
IgnoreAgent     ia_archiver
IgnoreAgent     Eurobot
IgnoreAgent     Gigabot
IgnoreAgent     rdfbot
IgnoreAgent     libwww-perl
IgnoreAgent     Googlebot-Image
IgnoreAgent     anonymous
IgnoreAgent     eZ Publish Link Validator
IgnoreAgent     DoCoMo
IgnoreAgent     Python-urllib
IgnoreAgent     librabot
IgnoreAgent     netEstate
IgnoreAgent     nutch/Nutch
IgnoreAgent     BaiduImagespider
IgnoreAgent     OOZBOT
IgnoreAgent     librabot
IgnoreAgent     yacybot
IgnoreAgent     Googlebot
IgnoreAgent     Wget
IgnoreAgent     Xenu
IgnoreAgent     curl
IgnoreAgent     findlinks
IgnoreAgent     page_test
IgnoreAgent     iCcrawler
IgnoreAgent     Yahoo! Slurp
IgnoreAgent     Exabot
IgnoreAgent     Exabot-Images
IgnoreAgent     Twiceler
IgnoreAgent     Ask Jeeves
IgnoreAgent     YoudaoBot
IgnoreAgent     Yandex
IgnoreAgent     MJ12bot
IgnoreAgent     Twiceler
IgnoreAgent     KaloogaBot
IgnoreAgent     DotBot
IgnoreAgent     nutch-agent
IgnoreAgent     VEDENSBOT
IgnoreAgent     YoudaoBot
IgnoreAgent     SurveyBot
IgnoreAgent     atraxbot
IgnoreAgent     NaverBot
IgnoreAgent     VoilaBot
IgnoreAgent     Plukkie
IgnoreAgent     Moreoverbot
IgnoreAgent     Yahoo! SearchMonkey
IgnoreAgent     Websiteshadowbot
IgnoreAgent     envolk
IgnoreAgent     Speedy Spider
IgnoreAgent     Sosospider
IgnoreAgent     Sogou web spider
IgnoreAgent     Sogou-Test-Spider
IgnoreAgent     Spinn3r

Die Konfiguration sollte noch etwas besser durchdacht werden, aber für den Moment genügt es mir so. Die automatische Erstellung der Webalizer-Berichte habe ich früher mit Webmin eingerichtet. Dort ist das ziemlich komfortabel. Aber inzwischen habe ich mir ein kleines Programm geschrieben, welches für mich besser funktioniert. Dieses kleine Programm wird für jeden Benutzer ein oder zwei mal am Tag über die Datei /etc/crontab aufgerufen.

Hier das kleine Programm (/scripts/generate_webalizer_stats.py):

#!/usr/bin/env python
# coding=utf-8
#
# Generiert mit *webalizer* Zugriffsstatistiken
#
# Erwartet als Parameter den Benutzernamen und den Hostname
# Syntax: ``generate_webalizer_stats.py <benutzername> <web-domain>``
#   z.B.: ``generate_webalizer_stats.py wallerforum_com www.wallerforum.com``
#

import os
import sys
import glob
import subprocess


WEBALIZER = "/usr/bin/webalizer"


def main():

    username = sys.argv[1]
    hostname = sys.argv[2]
    userdir = os.path.join("/home", username)
    logdir = os.path.join(userdir, "log")
    destdir = os.path.join(userdir, "webalizer")

    logfilenames = sorted(
        glob.glob(os.path.join(logdir, "apache_access.log*")),
        reverse = True
    )
    for logfilename in logfilenames:
        args = [WEBALIZER, "-n", hostname, "-o", destdir, logfilename]
        proc = subprocess.call(args)


if __name__ == "__main__":
    main()

Es ist nichts Besonderes an diesem Programm. Das Programm erwartet als Parameter den Benutzernamen und den Hostnamen (Domäne) für die Website, deren Logdateien ausgewertet werden sollen. Das Skript ruft webalizer auf und übergibt die benötigten Parameter.

Die zugehörigen Einträge in der /etc/crontab sehen so aus:

# Webalizer
0 0,12 * * *  halvar_at       /scripts/generate_webalizer_stats.py halvar_at halvar.at
1 1,13 * * *  lugt_halvar_at  /scripts/generate_webalizer_stats.py lugt_halvar_at lugt.halvar.at
2 2,14 * * *  horde_webmail   /scripts/generate_webalizer_stats.py horde_webmail webmail.wallerforum.com
...

Awstats installieren

Genau so wie Webalizer ist Awstats ein Programm mit dem die Logdateien des Apachen analysiert werden können.

aptitude install awstats

Danach muss man für jeden Benutzer/Domäne eine Datei im /etc/awstats-Ordner anlegen. Die Dateinamen müssen so aussehen: awstats.<benutzername>.conf.

/etc/awstats/awstats.halvar_at.conf:

Include "/etc/awstats/awstats.conf"
SiteDomain="halvar.at"
HostAliases="localhost 127.0.0.1"
DirData="/home/halvar_at/awstats"
LogFile="cat /home/halvar_at/log/apache_access.log.1 /home/halvar_at/log/apache_access.log |"

Mit Include wird die Haupt-Konfigurationsdatei eingebunden. Mit LogFile=... wird festgelegt, welche Konfigurationsdateien in welcher Reihenfolge geparst werden. Mit dem Pipe-Zeichen (|) kann man statt einem Dateinamen auch einen Befehl angeben, der die Daten per Pipe an Awstats schickt.

[Weiter gehts mit dem Updaten der Statistiken und dem Erstellen der HTML-Seiten]



Hier gehts weiter...






Horde Groupware Webmail Edition

http://www.horde.org/webmail/

Notiz: Es wurde Deutsch als Sprache eingestellt. Dafür musste man im Linux die Sprache aktivieren /usr/...locale

Squirrelmail installieren und konfigurieren

(noch nicht erledigt)

aptitude install squirrelmail

# squirrelmail-spam-buttons ???

Interessante Ideen

Jabber

Jabber auf dem Server installieren. Da fast alles Fischer auf dem Server sind, könnte das so eine Art Fischer-Chat werden.

Mögliche Clients:

Web-cyradm

...

Weitere ToDo's

Email-Installationsroutine (Usermin)

Floodcontrol: Dateien die älter als 1 Monat sind löschen

Email-Passwort per Usermin ändern lassen:

  • Emailadresse (Benutzername)
  • Altes Passwort
  • zwei mal neues Passwort
  • Ausgabe des Befehls sasldbpasswd2 anzeigen

PHP konfigurieren (Limits erhöhen)

MySQL konfigurieren (Limits erhöhen)

Apache: Memory-Cache und Datei-Cache konfigurieren. Alle Bilder könnten eigentlich bis zu einer gewissen Speichergrenze im Speicher verbleiben und direkt (ohne Festplattenzugriff) ausgeliefert werden.

Alles auf UTF-8 umstellen

Adminseiten mit Links erstellen:

  • Squirrelmail
  • Horde Webmail
  • Horde Groupware
  • phpMyAdmin
  • Usermin
  • Webalizer
  • Awstats
  • net2ftp

Webmin konfigurieren (/ip/ vom Log ausnehmen)

Awstats installieren und konfigurieren (/ip/ vom Log ausnehmen)

rkhunter und chkrootkit installieren und konfigurieren

Backup testen

Nagios konfigurieren

Spam und Ham lernen lassen

Spamassassin Whitelist lernen lassen

Nagios Server:

  • so einstellen, dass mit den Daten auch etwas angefangen werden kann.
  • Emailbenachrichtigung bei Fehler