Duplicity on Ubuntu 12.04: broken ssh backend

After setting up a new server with Ubuntu 12.04, I discovered that duplicity was unable to perform backups. The first problem was the missing ssh-backend. The package python-paramiko must be installed in order to use ssh-servers as backup target.

The second pitfall is a bit tricky, because of the confusing error message:

BackendException: ssh connection to server:22 failed: [Errno -2] Name or 
service not known

The bug is fixed in the Quantal-package. Simply download the .deb from the Quantal repository and install it:

dpkg -i duplicity_0.6.19-0ubuntu1_amd64.deb

Ubuntu auf einem Rootserver mit ZFS

Es ist mal wieder so weit: der alte Server ist zu klein, ein neuer muss her. Dieser Artikel ist ein Update zu meinem Update meiner alten Anleitung zum Setup des Rootservers.

Ein Serverumzug ist eine gute Gelegenheit, ein paar Dinge anders zu machen als bisher:

  • ZFS: die bisherige Lösung aus Raid + LVM + Dateisystem funktioniert zwar, aber das vergrößern / verkleinern der Partitionen ist doch immer eine recht spannende Angelegenheit und kann nicht immer online durchgeführt werden. Btrfs bietet noch nicht alle Features, die ich haben möchte (quotas auf FS-Ebene) und mit ZFS on Linux steht ZFS auch nativ als Kernelmodul zur Verfügung.
  • LXC statt OpenVZ: der Betrieb mit OpenVZ war nie ganz frei von Sorgen. Es ist super, dass man so viele Parameter hat und Resourcen flexibel zuweisen kann, aber speziell beim Webserver hatten wir das Gefühl, dass nie genug RAM vorhanden ist. Stößt der Server an das RAM-Limit, kommt es zu den seltsamsten Effekten, incl. Stillstand des Webserver. LXC ist im Mainline-Kernel und für unsere Zwecke gut genug, d.h. ich will Resourcen flexibel zuweisen und kann auf die höhere Sicherheit einer echten Virtualisierung verzichten.
  • nginx statt Apache: weil man es mal gemacht haben sollte 😉

Eine Sache, die sich hingegen bewährt hat, ist alle Dienste (PAM, Mail, sonstige Accounts) über LDAP abzuwickeln. Damit ist LDAP der „single point of administration and failure“, der sich besonders gut scripten lässt.

Hier also wieder die Copy & Paste Anleitung für das Setup von

Ubuntu auf einem Rootserver mit ZFS

Ich habe meinen Server bei Hetzner, aber das muss jeder selbst wissen. Als erstes bootet man ins Rescue-system und sichert den Inhalt der Datei /etc/network/interfaces:

### Hetzner Online AG - installimage
# Loopback device:
auto lo
iface lo inet loopback

# device: eth0
auto  eth0
iface eth0 inet static
  address   5.9.62.176
  broadcast 5.9.62.191
  netmask   255.255.255.224
  gateway   5.9.62.161

# default route to access subnet
up route add -net 5.9.62.160 netmask 255.255.255.224 gw 5.9.62.161 eth0

Festplatten einrichten

Dieses Mal soll der Server mit zfs betrieben werden. Es gibt zwar diverse Anleitungen, wie man Ubuntu in ein natives Root-ZFS-Dateisysten installiert, ich wähle aber bewusst einen anderen Weg.

Im Recovery-Fall komme ich nicht an den physikalischen Server. Ich kann nur ein Rescue-System booten, das kein ZFS-Kernelmodul hat, damit würde ich nicht an das root-Dateisystem kommen.
Deswegen installiere ich das Basissystem auf einer ext4-Partition. Hier läuft lediglich der LDAP-Server und ein SSH-Server. Alle anderen Dienste laufen in LX-Containern und diese können auf dem ZFS untergebracht werden.

Der Server ist mit 2×3 TB Festplatten ausgestattet, d.h. mit einem MBR kommt man nicht sehr weit, statt dessen wird eine GUID Partition Table (GPT) eingerichtet. Neben Root, Swap, ZFS wird auch eine „BIOS boot partition“ benötigt (ein EFI-Requirement).

gdisk /dev/sda

Der Rechner hat 32 GB Ram, also sollten 16 GB Swap genügen. Für das Root-System stelle ich 10GB bereit. Auf LVM verzichte ich bewusst um die Komplexität gering zu halten. Ich verliere damit die Möglichkeit das Basissystem über LVM-Snapshots zu sichern, den Kompromiss kann ich eingehen. Root und Swap werden aber in einem Raid-1 betrieben um einen Plattenausfall abzufangen. So sieht das gewünschte Layout aus:

Command (? for help): p
Disk /dev/sda: 5860533168 sectors, 2.7 TiB
Logical sector size: 512 bytes
Disk identifier (GUID): 932661F7-1628-4107-A48F-8FDDC9DB29E4
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 5860533134
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            4095   1024.0 KiB  EF02  BIOS boot partition
   2            4096        33558527   16.0 GiB    FD00  Linux RAID
   3        33558528        54530047   10.0 GiB    FD00  Linux RAID
   4        54530048      5860533134   2.7 TiB     BF00  Solaris root

Die Platte /dev/sdb wird analog eingerichtet. Jetzt ist ein guter Zeitpunkt um das System zu rebooten (wir müssen nochmal das Rescue-System auswählen), damit die neue Partitionstabelle verwendet wird.

Dann können swap und / angelegt werden:

mdadm -v --create /dev/md0  --level=mirror  --metadata=0.90 --raid-devices=2 /dev/sda2 /dev/sdb2
mdadm -v --create /dev/md1  --level=mirror  --metadata=0.90 --raid-devices=2 /dev/sda3 /dev/sdb3

Wichtig: --metadata=0.90 sollte unbedingt gesetzt werden, sonst kann man große Probleme mit grub bekommen.

Swap aktivieren:

mkswap /dev/md0
swapon /dev/md0

Root-Dateisystem anlegen und mounten:

mkfs.ext4 /dev/md1
mkdir /mnt/install
mount /dev/md1 /mnt/install/

Jetzt können wir

Ubuntu installieren

Wir benötigen erstmal ein neueres debootstrap und installieren damit dann Ubuntu 12.04 LTS:

wget http://archive.ubuntu.com/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.42_all.deb
dpkg -i debootstrap_1.0.42_all.deb 
debootstrap --arch amd64 precise /mnt/install http://archive.ubuntu.com/ubuntu

Die fstab muss angepasst werden:

vim /mnt/install/etc/fstab
proc                    /proc   proc    defaults 0 0
/dev/md0                none    swap    defaults,pri=1 0 0
/dev/md1                /       ext4    defaults 0 1

ausserdem muss die Netzwerk-Config, die wir vorhin gesichert haben wieder gesetzt werden:

vim /mnt/install/etc/network/interfaces
### Hetzner Online AG - installimage
# Loopback device:
auto lo
iface lo inet loopback

# device: eth0
auto  eth0
iface eth0 inet static
  address   5.9.62.176
  broadcast 5.9.62.191
  netmask   255.255.255.224
  gateway   5.9.62.161

# default route to access subnet
up route add -net 5.9.62.160 netmask 255.255.255.224 gw 5.9.62.161 eth0

Die Spezialmounts werden noch gemountet:

mount -t proc none /mnt/install/proc
mount --bind /sys /mnt/install/sys
mount --bind /dev /mnt/install/dev
mount -t devpts none /mnt/install/dev/pts

Dann können wir ins System chrooten…

chroot /mnt/install/ /bin/bash

… und ein paar Einstellungen vornehmen. Zuerst ein neues root-Passwort:

shadowconfig on
passwd

Wir brauchen das Universe-Repository

vim /etc/apt/sources.list
deb http://archive.ubuntu.com/ubuntu precise main universe

Dann werden ein paar grundsätzliche Tools installiert:

apt-get update
apt-get install aptitude
aptitude install man-db vim ssh

Ausserdem wird mdadm zur Raidverwaltung benötigt. Da in Ubuntu eine Abhängigkeit zu einem MTA existiert und der default Postfix ist, installieren wir das sehr schlanke Tool SSMTP mit:

aptitude install ssmtp mdadm

Dann brauchen wir noch einen Bootloader. Dieser wird als Abhängigkeit des Kernels installiert:

aptitude install linux-image-generic

Damit die Boot-Partitionen durch die initrd auch einwandfrei gemountet werden, sollte Änderung der Datei mdadm.conf noch die initrd neu angelegt werden, z.B. durch Ausführen von

dpkg-reconfigure mdadm

Sofern die entsprechenden Module, md bzw. raid0, raid1, noch nicht in der /etc/initramfs-tools/modules aktiviert wurden, muss dies noch erfolgen:

echo "raid0" >>/etc/initramfs-tools/modules
echo "raid1" >>/etc/initramfs-tools/modules
echo "md" >>/etc/initramfs-tools/modules

und anschließend das initramfs mit

update-initramfs -u -k all

aktualisiert werden.

Zum Schluss solle die Option „quiet splash“ aus /etc/default/grub entfernt werden. Die Remote-Konsole LARA bei Hetzner kommt sonst mit dem Bildschirmmodus nicht klar und man würde bei bootproblemen nichts sehen.

vim default/grub

Dann noch grub updaten, aus dem Chroot aussteigen und rebooten.

update-grub
exit
reboot

Wenn kein Fehler aufgetreten und der Server wieder hoch gekommen ist, können wir

ZFS installieren

Für Ubuntu ist die Installation sehr einfach. Wir brauchen erstmal das Paket python-software-properties, da sonst add-apt nicht funktioniert:

aptitude install python-software-properties

Dann kann nach der Installationsanleitung verfahren werden:

add-apt-repository ppa:zfs-native/stable
aptitude update
aptitude install ubuntu-zfs

Das Kernelmodul zfs.ko sollte jetzt gebaut und geladen sein. Jetzt können wir einen zpool auf dem verbleibenden Festplattenplatz anlegen.

zpool create -o ashift=12 tank mirror /dev/disk/by-id/ata-ST3000DM001-9YN166_W1F0W2EW-part4 /dev/disk/by-id/ata-ST3000DM001-9YN166_W1F0WNK2-part4

Die Option ashift=12 sort für ein passendes Alignment. Die Namen der Festplatten müssen natürlich angepasst werden.

Der Pool ist angelegt…

zfs list
NAME   USED  AVAIL  REFER  MOUNTPOINT
tank   472K  2.66T   136K  /tank

…und kann jetzt mit Filesystemen bestückt werden:

zfs create -o mountpoint=/home tank/home  
zfs create -o mountpoint=/var/vm tank/vm 
zfs list
NAME        USED  AVAIL  REFER  MOUNTPOINT
tank        864K  2.66T   136K  /tank
tank/home   136K  2.66T   136K  /home
tank/vm     136K  2.66T   136K  /var/vm

Ich habe zunächst nur ein Filesystem für /home und für die Linuxcontainer angelegt. Ab hier weiss sicherlich jeder selbst am besten, wie das System organisiert wird.

Credits: Das Setup des Basissystems mit GPT hätte ich ohne die Anleitung von Mattias nicht so schnell hinbekommen.

Portable __VA_ARGS__ macros for Linux, HP-UX, Solaris and AIX

I recently needed a macro with variable arguments that work on all mainstream compilers. I use gcc only on Linux, on the other platforms I want to use the „native“ compiler that belongs to the OS. Let’s start with Linux.

Linux

If you’re inside the gnu/gcc world, things are easy. This is what everybody tries first:

1
2
3
4
5
6
7
8
9
10
11
#include "stdio.h"
/* I use "stdio.h" because my blog software cant't handle
   angle brackets inside a preformated text blog ... :( */
 
#define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  f "\n" , __VA_ARGS__);
 
int main(void)
{
    PRINTERROR("Hello %s!", "world");
    return 0;
}

The problems start, if you want to use the macro without variable arguments.

9
    PRINTERROR("Hello world!");

gcc will not accept the code, because it resolves the macro to

9
    fprintf(stderr, "ERRORMESSAGE = "  "Hello world!" "\n" ,);

(note the comma)

test.c: In function ‘main’:
test.c:9: error: expected expression before ‘)’ token

The gcc workaround for this kind of a situation is:

5
#define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  f "\n" , ## s);

If „s“ is null, this erases the comma to the left of the „##“ in the macro definition. The resulting expansion is:

9
    fprintf(stderr, "ERRORMESSAGE = "  "Hello world!" "\n" );

The comma is gone, the code compiles. Great.

HP-UX

However, this approach fails on HP-UX

cc -o test test.c
cc: "test.c", line 9: error 1000: Unexpected symbol: ")".

I found the solution for this problem in the HP-docs:

you must insert a space to the left of the comma to make it clear to the preprocessor that the comma is the left operand of the „##“ operator.

Yes. A space. Left of the comma. Sigh….

The definition that works on Linux and HP’s cc -AC99 compiler looks like this:

5
#define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , ## s);

Solaris

Solaris c89 compiles the above definition with a warning:

"test.c", line 9: warning: argument mismatch

Solaris c99 compiler is very strict and allows

5
#define FOO(...) printf(__VA_ARGS__)

only. The documentation points out, that it is possible to use #__VA_ARGS__ which seems to be similar to GNU’s/HP’s ## Operator:

5
#define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );

Ok, the code compiles, but calling PRINTERROR results in a „argument mismatch“-warning. I found no other solution than disabling the warning. Dirty but it works:

5
6
7
#define PRINTERROR(x, ...) printf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );
/* optional: disables "warning: argument mismatch" */
#pragma error_messages (off, E_ARGUEMENT_MISMATCH)

If course it is bad to hide warnings, but if your code is multi-platform, you can use very strict compiler settings on another platform to make the warnings visible at least there.

AIX

The original AIX-compiler (cc) does not complain, but c89 fails with

"test.c", line 3.16: 1506-1128 (S) The GNU variable argument identifier "s" of macro "FOO" is not permitted in the current langlvl mode.
"test.c", line 7.29: 1506-041 (E) The invocation of macro FOO contains fewer arguments than are required by the macro definition.

There is a langlvl #pragma to switch the compiler language level. All levels that allow implementation-specific language extensions are sufficient. I used extc99 which allows to use the c99 compiler. Simply add this to the beginning of your sourcefile:

#pragma langlvl (extc99)

The final result

This is the result of some hours browsing the compiler docs. The macro works on all c99-compilers mentioned above and allows zero arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifdef _AIX
#pragma langlvl (extc99)
#endif
 
#include "stdio.h"
 
#ifdef _SOLARIS
  #define PRINTERROR(x, ...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , # __VA_ARGS__ );
  /* optional: disables "warning: argument mismatch" */
  #pragma error_messages (off, E_ARGUEMENT_MISMATCH)
#else
  #define PRINTERROR(x, s...) fprintf(stderr, "ERRORMESSAGE = "  x "\n" , ## s);
#endif
 
int main(void)
{
        PRINTERROR("NO PARAM");
        PRINTERROR("Param: %s", "foo");
 
        return 0;
}

Of course you have to use the compiler switch -D_AIX, -D_SOLARIS or -D_HPUX on the apropriate platform. Let me know what do you think and how it could be done better.

Update: André suggests this solution (see his comment):

void PRINTERROR_verbose(const char * file, int line, int level, char *format, ...) {
	int size;
	char *cp;
    va_list args;
 
	if (level < = 2) {
 
	    va_start(args, format);
    	size = vsnprintf(NULL, 0, format, args) + 1;
    	va_end(args);
 
    	va_start(args, format);
		cp = (char *)calloc(size, sizeof(char));
		if (cp != NULL && vsnprintf(cp, size, format, args) > 0) {
			/* Ausgabe von cp hier via syslog, stderr, stdout, etc. */
			/* oder cp noch mal umformatieren fuer Sonderzeichen und/oder */
			/* zusaetzliche Zeitangabe */
 
			/* bei Debuglevel 1 (z.B. ERROR) wird dann noch etwas mehr angezeigt */
			if (level < = 1) {
				fprintf(stderr, "\tFile '%s' in line %i\n", file, line);
			}
		}
	    va_end(args);
		free(cp);
	}
}
 
#define PRINTERROR(...) PRINTERROR_verbose(__FILE__, __LINE__, __VA_ARGS__)

gdb attach fails with ptrace: Operation not permitted

Today I ran into a weird problem. I could not attach to my own process with gdb. The process ran under my UID, but gdb refused to attach. This is a problem of wrong permissions, although /proc/[pid]/status looked ok:

...
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
...

I am the owner but cannot attach? Well, I launched gdb as root and could attach. Strange. Without digging deeper into this, my dirty workaround was this:

sudo chmod +s /usr/bin/gdb

Update: Thanks to Mario, who pointed out, that the reason is the Kernel hardening stuff build into the Ubuntu kernel. See his comment how to fix the problem permanently.

/etc unter Versionskontrolle mit git

Auf unserem Freifunkserver sind teilweise mehrere Admins am Werk. Damit Änderungen an der Konfiguration nachvollziehbar werden und man auch nach einem halben Jahr weiss, was man eigentlich gemacht hat, steht das Verzeichnis /etc unter Versionskontrolle.

Ich hatte das schon mal auf unserem alten Server mit svn, aber das Hauptproblem war, dass ich immer vergessen habe meine Änderungen zu committen. Folglich sammeln sich über Tage/Wochen/Monate Änderungen an und man weiss gar nicht mehr, was jede einzelne Änderung zu bedeuten hat.

Zum Glück hat mich Wulf vom Berliner Freifunk auf eine super Idee gebacht: man kann das commit-Kommando in der .bash_logout von root unterbringen. Sobald root seine Session beendet hat, passiert folgendes:

  • /etc wurde nicht verändert: es passiert gar nix
  • /etc wurde verändert: es öffnet sich ein Editor, in dem man seine Änderungen kurz beschreiben muss. Nach dem Abspeichern erfolgt automatisch der commit

Prima Sache. In der Praxis gibt es damit aber noch ein kleines Problem: „sudo su -“ berücksichtigt die .bash_logout, „sudo su“ jedoch nicht. Da ich meine Schlampigkeit kenne, habe ich eine Lösung gefunden, die in beiden Fällen funktioniert: ein exit-Trap. Das ganze wird in die .bashrc vom root eingebaut:

# erstmal die letzten 10 commits anzeigen
echo "***************************************************************"
(cd /etc; git log -10 --reversepretty=’format:%cd (%h)%n%s%n’ )
echo "***************************************************************"
 
# exit trap, schlägt beim Logout zu 
function on_exit()
{
  cd /etc/
  git commit -a
}
 
trap on_exit EXIT

Der/die geneigte Leser/in mag hier nach Belieben noch Funktionen ranbauen, die z.B. den diff des letzten commits per Mail versenden 🙂

ssh host-key Prüfung abschalten

Ich habe oft verschiedene WLAN-Router an meinem Rechner. Jeder Router ist auf dem LAN-Port über die gleiche IP zu erreichen (i.d.R. 192.168.1.1), allerdings hat jeder Router einen anderen ssh host-key. Wechsel ich den Router, schlägt die Überprüfung des Keys fehlt, weil unter der gleichen IP plötzlich ein anderer Key presentiert wird. Die Lösung habe ich hier gefunden und sieht so aus:

Host router
        Hostname 192.168.1.1
        StrictHostKeyChecking no
        UserKnownHostsFile=/dev/null
        User root

Geforkte Prozesse mit Netbeans debuggen

Debugging unter Linux/Unix ist ein großer Haufen Mist. Zumindest, wenn man schon mal mit Visual Studio gearbeitet hat. Ich sitze gerade an einem Produkt, dass massiv Prozesse forkt und irgendwo in einem Child steckt ein Bug.

Weil das Erzeugen und das Beenden der Prozesse ziemlich schnell geht, kann man sich nicht an den Prozess attachen, er ist schon lange wieder weg, bevor der Debugger oben ist. Findige Kollegen haben dann soetwas in den Code eingebaut:

  if (getenv("DEBUG_XYZ")) kill (getpid(),SIGSTOP);

Ich muss also eine Umgebungsvariable DEBUG_XYZ setzen, den Master-Prozess neu starten und irgendwann wird der fragliche Prozess als Gestoppt in der Prozessliste stehen, ich kann mich in Ruhe attachen und debuggen.
Die Lösung hat natürlich massive Nachteile: ich muss den Code jedesmal ändern, wenn ich den „Breakpoint“ verschieben will und schlimmstenfalls kommt solcher Code versehentlich ins finale Release.

Es geht ein bisschen besser, wenn man gdb benutzt, der kann nämlich beim Fork entweder dem Parent- oder dem Childprozess folgen:

set follow-fork-mode mode

Wobei mode entweder „child“, „parent“ oder „ask“ ist. Nun will niemand „nativ“ mit dem gdb debuggen, man spannt Netbeans davor. Damit man aber in Netbeans den follow-fork-mode setzen kann, braucht man erstmal eine GDB Debugger Console:

-J-Dgdb.console.window=true

muss unter netbeans_default_options in der netbeans.conf hinzugefügt werden.

Jetzt kann man zumindest das Debugging starten und sich entscheiden, ob man den Childs oder den Parents folgt. „ask“ geht komischerweise nicht. Auch muss man scheinbar den follow-fork-mode bei jedem Start neu setzen.

Das ist immernoch ein riesengroßer Haufen Mist. Aber zumindest habe ich schöne gelbe Gummistifel an. Geht das nicht einfacher? 🙁

Watchdog script to supervise /proc/user_beancounters

I’m in the process of setting up my new server. The services are running inside OpenVZ-Containers. The Problem is, that you don’t know how to setup the resource parameters. You have to walk the way of trial and error, because you don’t how much resources the services consume under productive load.

After migrating some domains, my postfix process died last night because of a lack of pirvvmpages. So I wrote a script that monitors changes in /proc/user_beancounters of every container. The script runs from crontab every 5 minutes and compares UBC failcounter values to the last known values. If there is a difference, a mail is sent to root.

Feel free to re-write the script in your favorite language 😉

#!/bin/sh
# get running VEs
VES=`vzlist -H -o veid`
MAILFILE=/var/run/ubcWatchdog_mail.txt
 
# check every running VE
for VE in $VES; do
  # create file if it does not exist
  NEWFCFILE=/var/run/ubcWatchdog_$VE.new.txt
  OLDFCFILE=/var/run/ubcWatchdog_$VE.txt
  touch $NEWFCFILE
  touch $OLDFCFILE
 
  # save current failcounter
  vzctl exec $VE 'cat /proc/user_beancounters' | cut -b 13-129 | sed 's/ \+/ /g' | awk {'print $6 "\t"  $1'} > $NEWFCFILE
 
  # compare to reference failcounter
  diff -U 0 -d $OLDFCFILE $NEWFCFILE > /dev/null
  if [ $? != 0 ]; then              
    # yepp, something failed
    echo "****************************************" >> $MAILFILE
    echo "UBC fail in VE $VE!" >> $MAILFILE
    diff -U 0 -d $OLDFCFILE $NEWFCFILE | grep "^[+,-][a-z,A-Z,0-9]\+" >> $MAILFILE
    echo "****************************************" >> $MAILFILE
    echo "" >> $MAILFILE
    echo "UBC now:" >> $MAILFILE
    vzctl exec $VE 'cat /proc/user_beancounters' >> $MAILFILE
  fi
 
  # save new failcounter as reference
  mv $NEWFCFILE $OLDFCFILE
done;
 
# send mail to root
if [ -f $MAILFILE ]; then
  mail -s "UBC Fail!" root < $MAILFILE
  rm $MAILFILE
fi

The mail looks like this:

****************************************
UBC fail in VE 20!
-205 kmemsize
+59936 kmemsize
****************************************

And is the result of a fork-bomb that exploded inside VE 20 🙂

dovecot deliver – Fatal: postmaster_address setting not given

It took me an hour to figure out the reason for this message. The only hint is, that there might be special characters like tabs in the config file. But in my case, the reason was the dovecot-postfix package of Ubuntu 9.10.

There are two config-files: /etc/dovecot/dovecot-postfix.conf and /etc/dovecot/dovecot.conf. The header says:

# If there's a file /etc/dovecot/dovecot-postfix.conf, which is part of
# dovecot-postfix package, it will be used instead of dovecot.conf.
 
# Keep in mind that, if that file exist, none of the changes in
# /etc/dovecot/dovecot.conf will have effect on dovecot's configuration.
# In that case you should customize /etc/dovecot/dovecot-postfix.conf.

The problem: dovecot’s deliver command ignores dovecot-postfix.conf and reads it’s config from /etc/dovecot/dovecot.conf 🙁

Ok, how to fix this? Open /etc/postfix/master.cf and specify the configfile using the -c parameter of the deliver-command:

dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -c /etc/dovecot/dovecot-postfix.conf -f ${sender} -d ${recipient}

See bug #511295.

pam-ldap: I have no name! (ubuntu)

Ich bin gerade über einen blöden Fehler gestolpert, den ich nicht noch einmal machen möchte… Ich habe an pam und ldap geschraubt und plötzlich konnten Gruppen und User-IDs nicht mehr aufgelöst werden:

groups: cannot find name for group ID 1000
I have no name!@vz10:~$

In einem nahezu identischen System ging es ohne Probleme. Ein Vergleich der beiden /etc-Verzeichnisse hat keine Auffälligkeiten gezeigt. Nachdem ich drauf und dran war, das System wegzuschmeißen und neu aufzusetzen, habe ich doch noch die Lösung gefunden:

/etc/ldap.conf muss für alle User lesbar sein. Ich hatte testweise ein Bind-Password eingetragen und aus diesem Grund die Rechte der Datei eingeschränkt. Ein einfaches

chmod 644 /etc/ldap.conf

hat das Problem gelöst 🙂

Debian Lenny auf einem Rootserver mit Raid-1 und LVM

Das ist ein Update zu meiner alten Anleitung. Der Unterschied zur alten Anleitung:

  • Dieses Mal soll ein Server unter Debian Lenny eingerichtet werden
  • Swap liegt ebenfalls im Raid
  • Der Server soll mit OpenVZ virtualisierte Maschinen hosten
  • Das root-Dateisystem soll ebenfalls im LVM liegen, damit wir beim Backup konsistente Snapshots vom gesamten System machen können

Hier also das Update zu meiner rundum-sorglos copy&paste-Anleitung zur Einrichtung von

Debian Lenny auf einem Rootserver mit Raid-1 und LVM

„Debian Lenny auf einem Rootserver mit Raid-1 und LVM“ weiterlesen

OpenLDAP Replikation mit syncprov

Für einen neuen Server in unserer Rootserver-Kommune brauche ich eine Replik unseres LDAP-Servers. Wie sich herausstellte, ist das ziemlich einfach und in der umfangreichen Dokumentation exzellent erklärt.

Im zu replizierenden LDAP-Server („Provider“) muss man lediglich dafür sorgen, dass das syncprov-Modul in der slapd.conf geladen wird:

modulepath      /usr/lib/ldap
moduleload      back_hdb
moduleload      syncprov

Im Zielserver („Consumer“), wird so eine 1:1 Replikation des Quellservers konfiguriert:

# replication settings
syncrepl rid=001
    provider=ldaps://ldaps.yourdomain.de:636
    type=refreshAndPersist
    searchbase="dc=yourdomain,dc=de"
    schemachecking=on
    type=refreshAndPersist
    retry="60 +"
    bindmethod=simple
    binddn="cn=replicator,dc=yourdomain,dc=de"
    credentials=assword

Natürlich müssen im Quellserver entsprechende Credentials hinterlegt werden. Durch das Keyword refreshAndPersist bleibt die Verbindung dauerhaft bestehen und Änderungen im Quellserver werden sofort auf dem Zielserver repliziert.

Achtung: im Zielserver müssen natürlich die gleichen Schemata geladen sein, wie im Quellserver. Schaut euch also die Include-Section entsprechend an, sonst hagelt es Fehler.

Mit dieser Methode kann man den LDAP-Server super-komfortabel umziehen, einfach Replikation einschalten, DNS-umschwenken und dann die Replikation auf dem Zielserver ausschalten.

DynDNS mit dem Alice Modem 1121 WLAN

Das Alice Modem 1121 WLAN bzw. TV hat eine recht spartanische Administrationsoberfläche. Man kann wirklich nur das Nötigste einstellen, eine Möglichkeite den beliebten DynDNS-Dienst zu konfigurieren, sucht man vergeblich.

Die Modemsoftware bringt jedoch einen DynDNS-Client mit, allerdings ist der nur via Telnet zu konfigurieren. Das Modem ist vom LAN und WLAN aus unter seiner Standard-IP 192.168.1.1 erreichbar.

Die Zugangsdaten lauten:

  • User: admin
  • Passwort: AliceXXXXXX123

Die Zeichenfolge XXXXXX muss durch die letzten sechs Zeichen der LAN MAC-Adresse des Modems ersetzt werden. Lautet die MAC-Adresse des Modems z.B. 00:25:5E:AB:64:FE, so muss man als Passwort AliceAB64FE123 verwenden:

$ telnet 192.168.1.1
 
Entering character mode
Escape character is '^]'.
 
Alice Modem WLAN 1121
Login: admin
Password: AliceAB64FE123
 
>

Man findet sich auf einer kastrierten Linux(?)-Shell wieder. help liefert eine Liste der möglichen Kommandos. Mit ddns kann man den DynDNS-Client konfigurieren:

> ddns
ddns add hostname --username username --password password --interface interface --service tzo|dyndns
     remove hostname
     show
ddns --help>

Ok, mit add fügt man eine DynDNS-Konfiguration hinzu, mit remove wird sie wieder entfernt und mit show kann man alle anzeigen lassen.

Der folende Aufruf fügt eine Konfiguration für den Hostnamen myhomedsl.dyndns.org beim Dienst dyndns mit dem Usenamen user6 und dem Passwort 123456 hinzu:

ddns add myhomedsl.dyndns.org --username user6 --password 123456 --interface ppp_0_1_32_1  --service dyndns

Wichtig: man muss im parameter --interface den Namen des WLAN-Interfaces eingeben. Den Namen bekommt man mit dem Kommando ifconfig heraus. Es listet alle Interfaces auf, gesucht ist das mit ppp im Namen:

ppp_0_1_32_1    Link encap:Point-Point Protocol  
                inet addr:85.178.209.191  P-t-P:213.191.89.4  Mask:255.255.255.255
                UP POINTOPOINT RUNNING NOARP ALLMULTI MULTICAST  MTU:1492  Metric:1
                RX packets:9540 errors:0 dropped:0 overruns:0 frame:0
                TX packets:7456 errors:0 dropped:0 overruns:0 carrier:0
                collisions:0 txqueuelen:3 
                RX bytes:6444034 (6.1 MiB)  TX bytes:1135835 (1.0 MiB)

In meinem Beispiel lautet der Name des Interfaces also ppp_0_1_32_1.

How to enable Server Name Indication (SNI) on Debian Lenny

Server Name Indication (SNI) on Debian Lenny is easy to implement. OpenSSL is already SNI-capable, only the Apache Webserver is a bit outdated. So lets backport Apache >= 2.2.12 to Lenny. Here is my little step-by-step howto:

Install a compiler an the debian dpkg-Development Environment:

$ sudo aptitude install dpkg-dev build-essential fakeroot

To build apache, you will nee libcap2-dev and autoconf too:

$ sudo aptitude install libcap2-dev autoconf

…and the build dependencies for apache2:

$ sudo apt-get build-dep apache2

Download the Apache 2.2.14 sources for the current testing release from http://packages.debian.org/source/squeeze/apache2:

$ wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14-1.dsc
$ wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14.orig.tar.gz
$ wget http://ftp.de.debian.org/debian/pool/main/a/apache2/apache2_2.2.14-1.diff.gz

Extract the source packages:

$ dpkg-source -x apache2_2.2.14-1.dsc

Compile the package:

$ cd apache2-2.2.14/
$ dpkg-buildpackage -us -uc -rfakeroot

(-us and -uc supresses the digital signature, fakeroot allows to set the ownership of the archived files to root, even if you are not root currently)

Install the desired apache packets:

$ cd ../
$ dpkg -i apache2_2.2.14-1_i386.deb apache2.2-bin_2.2.14-1_i386.deb apache2.2-common_2.2.14-1_i386.deb apache2-mpm-prefork_2.2.14-1_i386.deb apache2-suexec_2.2.14-1_i386.deb apache2-utils_2.2.14-1_i386.deb

Finally, remove the packages you installed to build the apache2-packages.

Surfterminal mit Linux

Wenn man einen Rechner im öffentlichen Raum als Browserterminal bereitstellen will, gibt es hauptsächlich die Anforderung, dass die User keine privaten Daten hinterlassen und am System so wenig wie möglich verstellen.

Es gibt verschiedenste Lösungen für diesen Zweck, fertige Linux-Distributionen oder spezielle Internet-Café Software, die Windows-Rechner so abschottet, das man noch nicht mal einen Explorer aufbekommt.

Für die beiden vom Freifunk betriebenen Surfterminals habe ich eine extrem simple Lösung gewählt. Die Rechner verrichten seit etlichen Monaten ohne Probleme ihren Dienst, obwohl es in den Kneipen schon etwas rauher zugeht.

Zunächst wird auf den Rechner die Linux-Distribution der Wahl installiert. Ich verwende Ubuntu, aber darauf kommt es nicht an. Es werden zwei Benutzer eingerichtet: ein administrativer („tresen“) und ein unpreviligierter („gast“). Achtung, bei Ubuntu ist der erste, bei der Installation eingerichtete User ein previligierter User (mit sudo-Rechten). Also erst „tresen“ und dann „gast“ einrichten und hinterher nochmal die /etc/sudoers überprüfen.

Man loggt sich zunächst als „gast“ ein und richtet den Browser so ein, wie man es gerne für die Gäste hätte: Adblocker, keine Cookies und Passwörter speichern, Startseite ect. Die Startseite für den Browser zeigt in unser Wiki, die Kneipengäste können dort dann eigene Links ablegen und so die Startseite etwas nach ihren Bedürfnissen anpassen. Der Webbrowser wird als Autostart-Applikation eingerichtet (System -> Preferences -> Sessions -> Startup Programs). Dann loggt man sich als „tresen“ ein und richtet den Anmeldungsmanager so ein, dass „gast“ beim Rechnerstart automatisch eingeloggt wird.

Jetzt kommt der interessante Teil: das Script pack_guest.sh verpackt das Home-Verzeichnis vom Gastuser in eine .tar-Datei:

#!/bin/sh
#
# Erstellt ein neues Template (gast_home.tar.gz) aus dem aktuellen gast
# Homeverzeichnis. Dieses Script sollte nur ausgeführt werden, wenn die
# Änderungem am gast-User dauerhaft gespeichert werden sollen.
#
# siehe http://wiki.freifunk-potsdam.de/index.php?title=Olga-Surfstation
if [ `whoami` != "root" ]; then
  echo "Du musst root sein, um das Script auszufuehren! Schau Dir bitte die"
  echo "Doku auf unserer Webseite an:"
  echo "http://wiki.freifunk-potsdam.de/index.php?title=Olga-Surfstation"
  exit 1
fi
 
#
DATE=`date +%Y%m%d_%H%M%S`
TARGET=/home/tresen/Surfstation/gast_home.tar.gz
 
# backup vom alten $HOME anlegen...
if [ -e $TARGET ]; then
  mv -v $TARGET  /home/tresen/Surfstation/gast_home_$DATE.tar.gz
fi
 
cd /
tar czf /home/tresen/Surfstation/gast_home.tar.gz /home/gast

Dieses Script ruft man einmalig auf und findet dann in /home/tresen/Surfstation die Datei gast_home.tar.gz. Dieses Template kann nun immer ausgepackt werden, wenn der Rechner startet. Das erledigt das Script unpack_guest.sh:

#!/bin/sh
#
# stellt den gast-user aus dem vorgefertigten Template (gast_home.tar.gz)
# wieder her. Das ist das Standard-Script, das bei jedem Booten ausgefuehrt
# wird. siehe
# http://wiki.freifunk-potsdam.de/index.php?title=Olga-Surfstation
#
if [ `whoami` != "root" ]; then
  echo "Du musst root sein, um das Script auszufuehren! Schau Dir bitte die"
  echo "Doku auf unserer Webseite an:"
  echo "http://wiki.freifunk-potsdam.de/index.php?title=Olga-Surfstation"
  exit 1
fi
 
rm -rf /home/gast
cd /
tar xzf  /home/tresen/Surfstation/gast_home.tar.gz
 
# falls das Passwort zurueck gesetzt wurde...
echo "gast:gast" | chpasswd

Jetzt muss man nur noch dafür sorgen, dass dieses Script bei jedem Bootvorgang ausgeführt wird. Eine Zeile in /etc/rc.local genügt:

/home/tresen/Surfstation/unpack_guest.sh

[Update] (12.11.09): Mir ist gerade aufgefallen, dass es mit Upstart zu einer Race-Condition kommt. Es ist einfach nicht definiert, wann das Script unpack_guest.sh ausgeführt wird und das führt zu sehr merkwürdigen Effekten.

Wenn man also eine Distribution mit Upstart verwendet (z.B. Ubuntu 9.10 „Karmic Koala“ oder später), muss man den Aufruf des unpack_guest.sh Scriptes im Upstart-System vor den gdm legen. Entweder man definiert einen eigenen Dienst oder man fügt das Script als pre-start Script in /etc/init/gdm.conf ein:

pre-start script
    # unpack the guest account home directory
    /home/tresen/Surfstation/unpack_guest.sh
end script

[/Update]

Mir gefällt an diese Lösung, dass wir damit absolut keine Arbeit haben. Die einzigen Probleme sind Hardwareschäden, also wenn wiedermal Bier über die Tastatur gekippt wurde. Das System an sich läuft aber nahezu wartungsfrei. Man muss sich halt alle paar Monate (idealerweise remote) einloggen und die fälligen Updates einspielen.

Stacktrace gefällig?

Gelegentlich braucht man einen Stacktrace. Ich bin heute über ein Problem gestolpert, bei dessen Lösung mir ein Stacktrace sehr helfen würde.

20090907_dddlogoEine unserer Bibliotheken kümmert sich um Speichermanagement. Das sind kleine Wrapper um malloc / free, die Buchhaltung über den Speicher führen und bei Fehlern entsprechende Meldungen ausgeben. Also der übliche Weg für das Speichermanagementproblem in C, es gibt duzende Lösungen die ähnlich funktionieren.

Das Problem heute war, dass Speicher freigegeben werden sollte, der nie allokiert war oder schon einmal freigegeben wurde. Er war auf jeden Fall nicht mehr als belegt gekennzeichnet und konnte somit nicht freigegeben werden. Programme ohne solch ein Speichermanagement würden an dieser Stelle abstürzen, bei uns taucht nur eine Meldung im Log auf.

Diese Meldung war mein Startpunkt für die Suche. Die Aufrufe der free-Wrapperfunktion sind jedoch in vielen Sourcefiles auf tausenden Zeilen Code verstreut. Wie soll man dort den einen Aufruf finden, der die Ursache für die Meldung ist?

Genau: ein Stacktrace muss her. Der schmutzige Weg wäre, das Programm mutwillig zum Absturz zu bringen. Dann kann man sich aus dem core-File den Stack rauspopeln und weiss, woher der Aufruf kommt.

Es gibt jedoch etwas viel schöneres: backtrace() aus der glibc. Mit ein paar Zeilen Code kann man an jeder beliebigen Stelle des Programms einen Stacktrace erzeugen:

void *array[10];
size_t size;
char **strings;
size_t i;
 
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
 
printf ("Obtained %zd stack frames.\n", size);
 
for (i = 0; i < size; i++)
   printf ("%s\n", strings[i]);
 
free (strings);

Ich habe also in der free-Wrapperfunktion backtrace() eingebaut und konnte mir den Stacktrace für den Fehlerfall ausgeben. Im Log erhält man dann etwa diese Ausgabe:

yourbinary [0x80b9e0c]
yourbinary [0x80b9f02]
yourbinary [0x8054abd]
yourbinary [0x805124f]
yourbinary [0x8050078]
yourbinary [0x808d6f3]
yourbinary [0x804bd42]
yourbinary [0x804b04e]
libc.so.6(__libc_start_main+0xe5) [0xb7d0f775]
yourbinary [0x804a701]

Ok, das sieht schon mal gut aus 😉 Jetzt muss man nur noch yourbinary in den ddd laden um die Speicheradressen in Codezeilen umzuwandeln – et voilà: schon ist die bösartige Zeile Code identifiziert.

HFS+ unter Linux beschreiben

Ich habe heute zu meinem Erstaunen festgestellt, dass man HFS+Volumes – dem Fall meine externe USB-Platte – nicht unter Linux beschreiben kann. Nachdem ich google eine Weile mit Suchbegriffen („Read only filesystem“ „hfsplus“) beworfen hatte, kam auch ans Licht, woran das liegt.

Linux kann schon HFS+-Volumes beschreiben, allerdings nicht, wenn diese journaled sind. Die Lösung, das Journal abzuschalten, ist für mich akzeptabel. Unter Mac OS X geht das am schnellsten auf der Kommandozeile:

1
$ sudo /usr/sbin/diskutil disableJournal /Volumes/BACKUP

cannot connect to saslauthd server: Permission denied

After upgrading my server from debian etch to lenny, I discovered that the saslauthd was broken. The log looked like this:

SASL authentication failure: Password verification failed
SASL PLAIN authentication failed: generic failure
SASL authentication failure: cannot connect to saslauthd server: Permission denied
SASL LOGIN authentication failed: generic failure

The solution for this problem is documented in /usr/share/doc/sasl2-bin/README.Debian.gz:

  • edit /etc/default/saslauthd and set OPTIONS like this:
    OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"
  • run this command as root:
    dpkg-statoverride --add root sasl 710 /var/spool/postfix/var/run/saslauthd
  • add the postfix user to the sasl group:
    adduser postfix sasl
  • restart saslauthd and postfix

Karten für den LinuxTag 2008

Das schneeballartige Kartenverteilsystem des LinuxTag war wieder großzügig. Wer eine Karte haben möchte schreibts bitte in die Kommentare oder an melle (kringelchen) gmx (punkt) at.