Gruppen-Authentifizierung für Nginx
Der Web-Server Nginx hat kein Äquivalent für Apaches gruppenbasierte Zugriffskontrolle. Dieses Apache-Feature kann aber mit etwas Scripting leicht emuliert werden.
Damit der Browser beim Zugriff auf bestimmte Webressourcen einen hässlichen Dialog zur Abfrage von Usernamen und Passwort öffnet, muss man in nginx eine Konfiguration wie die Folgende erstellen:
location /protected {
auth_basic "Mein kleiner geheimer Dienst";
auth_basic_user_file /pfad/zu/auth/htusers;
}
Die Passwortdatei /pfad/zu/auth/htusers
kann entweder mit dem Apache-Tool htpasswd oder dem Python-Skript htpasswd.py erzeugt werden. Das Ergebnis sieht so aus:
$ cat /pfad/zu/auth/htusers
hihosilver:$apr1$7iHB6eAM$bhrCoWXyXVDHXYo4aXZlf.
tom:$apr1$k03jYOif$hcf8rXosgreIz0v5oAtqD0
dick:$apr1$ZtrDNWP4$1G7sOMKOGZuBYdCu37bSX.
harry:$apr1$Lv1OSEIx$HHNvUurRBs/IRtSuYGXMh/
superuser:$apr1$wh3lz3Zw$TW4BO3mhEcWkFwcx.fZ760
Die äquivalente Apache-Konfiguration wäre:
<Location /protected>
AuthType Basic
AuthName "Mein kleiner geheimer Dienst"
AuthUserFile "/pfad/zu/auth/htusers"
Require valid-user
</Location>
Die Anweisung Require valid-user
bedeutet dabei, alle "Benutzer in AuthUserFile
".
Gruppenbasierte Authentifizierung, die simple Methode
Jetzt wird ein weiterer geschützter Bereich /protected/admin
erforderlich, der nur für die Benutzer hihosilver
und superuser
zugänglich sein soll. Mit Apache ist das trivial:
<Location /protected/admin>
AuthType Basic
AuthName "Mein kleiner geheimer Dienst"
AuthUserFile "/pfad/zu/auth/htusers"
AuthGroupFile "/pfad/zu/auth/htgroups"
Require group admin
</Location>
In Zeile 5 wird eine Gruppendatei eingeführt, und nur Benutzern aus der Gruppe admin
wird Zugriff auf diesen Bereich gewährt. Die Gruppendatei kann mit einem beliebigen Texteditor erstellt werden:
$ cat /pfad/zu/auth/htgroups
admin: superuser hihosilver
users: superuser hihosilver tom dick harry
Nur superuser
und hihosilver
gehören der Gruppe admin
an, die zum Zugriff auf /protected/admin
berechtigt ist.
Da nginx jedoch keine Gruppen kennt, bleibt nichts anderes übrig, als eine zweite Passwortdatei zu erstellen:
location /protected/admin {
auth_basic "Mein kleiner geheimer Dienst";
auth_basic_user_file /pfad/zu/auth/admin.users;
}
Die Konfiguration entspricht exakt der für den Bereich /protected
. Es wird aber eine andere Passwortdatei verwendet (line 4), die sich zum Beispiel so erzeugen ließe:
$ cd /pfad/zu/auth
$ grep 'hihosilver|superuser:' htusers >admin.users
Damit wird die Passwortinformation für die Benutzer hihosilver
und superuser
kopiert. Und leider auch für alle anderen Benutzer, die einen dieser Strings im Passwort-Digest oder auch im Benutzernamen enthalten. Der reguläre Ausdruck muss noch etwas verfeinert werden:
$ cd /pfad/zu/auth
$ grep -E '^(hihosilver|superuser):' htusers >admin.users
Für viele einfache Szenarien ist das bereits ausreichend, und es lohnt sich nicht, den Mechanismus zu verbessern. Es sollte jedoch darauf geachtet werden, dass in allen Konfigurationen der gleiche Authentifizierungs-Bereich ("realm", das ist der Wert für auth_basic
) verwendet wird. Anderenfalls werden auch die Admin-Benutzer beim Wechsel in den Admin-Bereich erneut zur Passworteingabe aufgefordert.
Gruppenbasierte Authentifizierung, die komfortable Methode
Die simple Lösung mit grep wird irgendwann unpraktisch, wenn es viele verschiedene Gruppen oder geschützte Bereich gibt. Hier wäre eine Gruppendatei à la Apache dann doch hilfreich. Zum Glück ist das nicht sehr schwer.
Um eine vergleichbare Lösung für nginx zu haben, muss das Skript nginx-groups.pl heruntergeladen und im Verzeichnis /path/to/auth
. Dann muss die Gruppendatei htgroups
(siehe oben) erzeugt werden, und das Skript ausgeführt werden:
$ perl nginx-groups.pl htusers htgroups
Writing users file 'admin.users'.
Writing users file 'users.users'.
Das Skript liest die Dateien mit den Benutzer- und Gruppeninformationen ein und erzeugt daraus eine Datei GRUPPE.users
für jede Gruppe GRUPPE
.
Das Skript ist ziemlich primitiv und eingeschränkt. Zum Beispiel wird immer ins aktuelle Verzeichnis geschrieben, und das Namensschema GRUPPE.users
ist auch hartkodiert.
Wer den Ehrgeiz dazu hat, kann das Skript gerne aufbohren oder an individuelle Bedürfnisse anpassen. Es ist einfach zu verstehen:
#! /usr/bin/env perl
use strict;
die "Usage: $0 USERSFILE GROUPSFILE\n" unless @ARGV == 2;
my ($users_file, $groups_file) = @ARGV;
my %users;
open my $fh, "<$users_file" or die "cannot open '$users_file': $!\n";
while (my $line = <$fh>) {
chomp $line;
my ($name, $password) = split /:/, $line, 2;
next if !defined $password;
$users{$name} = $line;
}
open my $fh, "<$groups_file" or die "cannot open '$groups_file': $!\n";
while (my $line = <$fh>) {
my ($name, $members) = split /:/, $line, 2 or next;
next if !defined $members;
$name =~ s/[ \t]//g;
next if $name eq '';
my @members = grep { length $_ && exists $users{$_} }
split /[ \t\r\n]+/, $members;
my $groups_users_file = $name . '.users';
print "Writing users file '$groups_users_file'.\n";
open my $wh, ">$groups_users_file"
or die "Cannot open '$groups_users_file' for writing: $!\n";
foreach my $user (@members) {
print $wh "$users{$user}\n"
or die "Cannot write to '$groups_users_file': $!\n";
}
close $wh or die "Cannot close '$groups_users_file': $!\n";
}
In den Zeilen 8 bis 15 wird die Benutzerdatei zeilenweise eingelesen. Jede passende Zeile wird in einem Hash (assoziativen Array) unter dem Benutzernamen abgespeichert.
Danach wird die Gruppendatei auf die gleiche Weise gelesen und in Zeile 30 für jede Gruppe eine eigene Passwortdatei geschrieben. Es ist wichtig, dass diese Passwortdatei auch geschrieben wird, wenn die die Gruppe keine Mitglieder hat, weil nginx ansosten nicht mehr aktuelle Dateien verwendet und eventuell ungewollten Zugriff erlaubt.
Leave a comment