![]() |
SELFHTML Server Konfiguration:
|
Die Userverwaltung war die meiner Meinung nach größte Herausforderung: das User- und Gruppen-Konzept mußte so angelegt sein, dass zwar der Apache in jedes Verzeichnis darf, die verschiedenen System-User jedoch nur zu bestimmten Bereichen Zugang haben.
Zunächst haben wir eine Gruppe "xyz" fuer den Apache-User selber. Weiterhin hat jedes Projekt seine
eigene Gruppe, abgesehen von den "Zwitter-Projekten". So hat z. B. das Projekt "SELFHTML-Suche"
die Gruppe "zxy", der alle Dateien innerhalb des Projekt-Verzeichnisbaums zugeordnet sind. Dafür
wird über das "set-group-ID-on-execution-bit" (siehe Manpage
chmod)
gesorgt, ein Bit, das, wenn es auf ein Verzeichnis gesetzt wird, unterhalb dieses Verzeichnisses wiederum für
alle neu angelegten Verzeichnisse und Dateien per Default die Gruppe des Projekt-Verzeichnisses setzt. Doch das allein
genügt nicht: User haben die Angewohnheit, sich nicht an Bestimmungen zu
halten (sorry, wenn ich jetzt jemandem auf die Füße trete), also mußte ein Absicherungs-Mechanismus
her, der dafür sorgt, dass die Dateien und Verzeichnisse die richtige Gruppen-Zugehörigkeit haben.
Hierfür habe ich ein Perl-Script geschrieben
(
Die Rechte).
Zunächst wollten wir weg von der Multi-User-Verwaltung. Auf dem alten Server war es so, dass jeder für jedes Projekt ein eigenes Login bekamen, sprich ein User, der für die Projekte SELFHTML und SELFHTML-Suche eingetragen war, hatte wenigstens 2 Logins. War er noch weiteren Projekten zugeordnet, hatte er noch zusätzlich pro Projekt ein Login. Das war sehr, sehr umständlich und nervig, außerdem bot es eine Sicherheitslücke: mit jedem Account auf dem System wird der Server angreifbarer.
So bekam jetzt jeder User lediglich einen Account. Der Zugriff wurde über die Gruppen-Zugehörigkeit geregelt.
User, die keinen SSH-Zugriff benötigten, erhielten keine Shell (/sbin/nologin), die anderen erhielten
die
BASH2 als Shell.
Jeder User bekam ein maschinen-generiertes Passwort, das wenigstens 8 Zeichen lang war. Das heißt, die Passwörter sind keiner bekannten Sprache entnommen und bestehen aus sämtlichen, im ASCII-Code verfügbaren Zeichen. So ist z. B. das Ausprobieren durch Wörterbücher sinnlos und durch Brute-Force mehr als sehr aufwendig: es gibt bei einem Zeichensatz von sagen wir mal 128 Zeichen und einer Einschränkung von 12 Zeichen Passwort-Länge 1288 (= 72057594037927936) bis 12812 (= 19342813113834066795298816) mögliche Kombinationen (in Wirklichkeit ist die Maximal-Länge nicht gesetzt).
Eine Ausnahme bildet der Apache-Prozess: er bekam ein Passwort, das durch crypt() oder md5()
nicht erreichbar ist. Außerdem bekam er keine Shell. So ist es im Grunde unmöglich, sich über ihn
einzuloggen.
Jetzt war zwar die Aufteilung in Gruppen geregelt, jedoch noch nicht die Rechtevergabe. Mit einer zu restriktiven Rechtevergabe (z. B. 700) könnte der Apache-Prozess keinen Zugriff auf alle Projekte haben. Allerdings sollten Projekt-Mitglieder auch vollen Schreib- und Lesezugriff erhalten. So blieb nur eine Wahl: Verzeichnisse und Scripts erhielten 770 (rwxrwx---) als Mode, "normale" Dateien erhielten 660 (rw-rw----) als Mode.
Das lief soweit sehr gut — solange die Anzahl der Projekte sich in Grenzen hielt. Danach saßen wir einem Bug des Apachen auf: war er zu vielen Gruppen zugeordnet, glaubte er, bestimmte Dateien nicht mehr ausführen zu duerfen. Ich weiß nicht, woran das liegt, ich weiß nur, dass für dieses Problem eine Lösung gefunden werden mußte. So änderte ich die Modes für Scripte auf 771 (rwxrwx--x).
Dieses Rechte-System war sehr empfindlich. Ich hatte zwar die entsprechenden umode-Einstellungen gemacht,
aber wenn sich nur ein User nicht daran hielt, war alles sinnlos. Um sicherzustellen, dass die Modes richtig
eingestellt blieben, erarbeitete ich ein Script, das einmal pro Stunde läuft:
#!/usr/bin/perl -w
use strict;
use vars qw($VERSION $CONFF);
BEGIN
{
$VERSION = 1.02;
$CONFF = '/home/cron/modes/modes.xml';
}
use XML::Simple; # um die Konfigurations-Datei einzulesen
sub readin($$$);
my $config = XMLin($CONFF);
foreach(@{$config->{groups}->{group}}) # jede Gruppe durchiterieren
{
chmod oct( # und bei jeder Gruppe das Basis-Verzeichnis ändern
$config->{modes}->{mode}->{$_->{groupname}} ?
$config->{modes}->{mode}->{$_->{groupname}}->{content} :
$config->{modes}->{mode}->{normdir}->{content}
), $_->{dir} or do {
print 'Error in chmod: ',$!, 'Directory: ',$_->{dir},"\n";
next;
};
# leider scheint es kein chgrp zu geben - also backticks benutzen
my $command = "chgrp ".$_->{groupname}." ".$_->{dir};
print `$command`; # eventuelle fehlermeldungen ausgeben, die werden dann per Mail verschickt
readin(
$_->{dir},
$_->{groupname},
$_->{recursive} eq 'yes' ? 1 : 0
);
}
sub readin($$$)
{
my $aktdir = shift;
my $grpname = shift;
my $rec = shift;
local *DIR;
opendir DIR, $aktdir or print 'Error: ',$aktdir,': ',$!,"\n" and return; # das verzeichnis öffnen und einlesen
foreach my $file (readdir DIR)
{
next if $file eq "." || $file eq ".."; # . und .. ignorieren
if(-d $aktdir.'/'.$file) # bei verzeichnissen eine neue rekursions-stufe starten
{
chmod oct($config->{modes}->{mode}->{normdir}->{content}), $aktdir.'/'.$file or do {
print 'Error in chmod: ',$!,' at directory ',$aktdir,'/',$file,"\n";
next;
};
my $command = 'chgrp '.$grpname.' '.$aktdir.'/'.$file;
print `$command`;
readin($aktdir.'/'.$file,$grpname,$rec) if $rec;
next;
}
if($file =~ /\.([^\.]+)$/) # die Datei-Endung ist ermittelbar
{
if(defined $config->{modes}->{mode}->{$1}) # gibt es einen fuer die Datei-Endung definierten
{ # Mode, so wird dieser genommen
chmod oct($config->{modes}->{mode}->{$1}->{content}),$aktdir.'/'.$file or do {
print 'Error in chmod: ',$!,' at directory ',$aktdir,'/',$file,"\n";
next;
};
my $command = 'chgrp '.$grpname.' '.$aktdir.'/'.$file;
print `$command`;
}
else # wenn nicht - Standard-Modes nehmen
{
chmod oct($config->{modes}->{mode}->{normfile}->{content}), $aktdir.'/'.$file or do {
print 'Error in chmod: ',$!,' at directory ',$aktdir,'/',$file,"\n";
next;
};
my $command = 'chgrp '.$grpname.' '.$aktdir.'/'.$file;
print `$command`;
}
}
else # Datei-Endung nicht ermittelbar, Standard-Modes nehmen
{
chmod oct($config->{modes}->{mode}->{normfile}->{content}),$aktdir.'/'.$file or do {
print 'Error in chmod: ',$!,' at directory ',$aktdir,'/',$file,"\n";
next;
};
my $command = 'chgrp '.$grpname.' '.$aktdir.'/'.$file;
print `$command`;
}
}
closedir DIR or print 'Error: ',$aktdir,': ',$!,"\n" and return;
}
# eof
Tja, jetzt fehlt nur noch die Konfiguration:
<?xml version="1.0"?> <!DOCTYPE config SYSTEM "modes.dtd"> <config> <groups> <!-- webroot --> <group recursive="yes"> <dir>/home/www/teamone.de/selfaktuell</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/selfhtml</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/www</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/selfsuche</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/rockyhorrorpictureshow</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/selfdeveloper</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/selffan</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/selfforum</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/alpentouren</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/selfhtml.com.fr/selfforum</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/selfhtml.com.fr/selfhtml</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/selfhtml.com.fr/www</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/selfhtml.com.fr/recherche</dir> <groupname>gruppenname</groupname> </group> <group recursive="yes"> <dir>/home/www/teamone.de/webalizer</dir> <groupname>gruppenname</groupname> </group> </groups> <modes> <mode name="pl">771</mode> <mode name="cgi">771</mode> <mode name="sh">771</mode> <mode name="php">771</mode> <mode name="normfile">660</mode> <mode name="normdir">771</mode> </modes> </config>
Und natürlich darf auch die DTD nicht fehlen:
<!ELEMENT config (groups,modes)>
<!ELEMENT groups (group+)>
<!ELEMENT group (dir,groupname)>
<!ATTLIST group
recursive CDATA #IMPLIED
>
<!ELEMENT dir (#PCDATA)>
<!ELEMENT groupname (#PCDATA)>
<!ELEMENT modes (mode+)>
<!ELEMENT mode (#PCDATA)>
<!ATTLIST mode
name CDATA #REQUIRED
>
Mit diesem Script ist es mir möglich, die Rechte relativ genau zu verteilen und sicherzustellen, dass sie auch eingehalten werden.