Tutorial: Datenbank gestütze Vhosts mit Variablen mit Hilfe von mod_perl

Wer einen Webserver mit mehreren Domains betreibt, kennt das Problem:
Wie kann ich mir die Verwaltung der vielen Vhosts vereinfachen? Die einzelnen Config-Dateien beinhalten bis auf kleine Abweichungen (Domainname, Verzeichnis) alle die selben Inhalte. Und was ist, wenn man eine Einstellung ändern will? Alle Config-Dateien ändern?
Genau darauf hatte ich auch keine Lust mehr. Deshalb habe ich mich nach Möglichkeiten umgeschaut, wie man die Config-Dateien mit Variablen bestücken kann. Meine Lösung will ich euch natürlich nicht vorenthalten.

Vorbereitung

Meine Lösung basiert auf mod_perl 2.0. Dies sollte also zuerst auf dem Server installiert werden.
Unter Debian geht das ganz einfach:

sudo apt-get install libapache2-mod-perl2

Wer kein Debian-Server hat, kann sich mod_perl unter http://perl.apache.org/download/index.html downloaden.

Datenbank

Die wenigen Daten, in denen sich die Vhosts unterscheiden, werden in einer MySQL-Datenbank gespeichert. Legt euch dazu einfach folgende Tabelle in einer existierenden Datenbank an:

CREATE TABLE `apacheconfig` (
  `ID` int(11) NOT NULL auto_increment,
  `servername` varchar(100) NOT NULL,
  `alias` varchar(1000) NOT NULL,
  `dir` varchar(100) NOT NULL,
  PRIMARY KEY  (`ID`)
);

Abschließend sollten noch ein paar Testeinträge gemacht werden, damit das am Ende auch getestet werden kann.

Inhalt der Datei „/etc/apache2/sites-available/template“

Die Installation von mod_perl ermöglicht das Einbinden von Perl-Code in die Config-Dateien. Dies nutzen wir, um ein Template mit Variablen zu erstellen. In meinem Beispiel sieht das so aus:

<VirtualHost *:80>
	<Perl>
		$ServerName = $My::servername;
		$ServerAlias = $My::alias;
		$Redirect = "/ http://www.".$My::servername."/";
	</Perl>
</VirtualHost>

<VirtualHost *:80>
	<Perl>
		$ServerName = "www.".$My::servername;
		$DocumentRoot = "/home/".$My::dir."/htdocs";

		$Directory {"/home/".$My::dir."/htdocs/"} = {
			AllowOverride => "All",
			Order => "allow,deny",
			allow => "from all",
			Require => "all granted"
		};
	</Perl>

 	ServerAdmin webmaster@localhost

        <Directory />
        	Options FollowSymLinks
		AllowOverride None
	</Directory>

	Alias /phpmyadmin/ "/usr/share/phpmyadmin/"
	ErrorLog /var/log/apache2/error.log

	# Possible values include: debug, info, notice, warn, error, crit,
	# alert, emerg.
	LogLevel warn
</VirtualHost>

Inhalt der Datei „/etc/apache2/sites-available/vhost.conf“

Die vhost.conf ist das Herzstück der neuen Konfiguration. Hier werden alle Informationen aus der Datenbank geholt und mit Hilfe des Templates zusammengesetzt:

<Perl>
use DBI;
use Apache2::ServerUtil ();

# Die Daten in den folgenden 3 Zeilen bitte anpassen (DB_Name, DB_User, DB_Pass)
my $dsn = "dbi:mysql:DB_Name@localhost";
my $user = "DB_User";
my $pw = "DB_Pass";

my $s = Apache2::ServerUtil->server;

my $db = DBI->connect($dsn, $user, $pw) or die $DBI::errstr;

my $select = qq/SELECT * FROM apacheconfig ORDER BY ID/;
my $sth_select = $db->prepare($select);

$sth_select->execute() or die $DBI::errstr;
my @row;

while( @row = $sth_select->fetchrow_array ) {
	$My::servername = @row[1];
	$My::alias = @row[2];
	$My::dir = @row[3];
	$s->add_config(["Include /etc/apache2/sites-available/template"]);
}

</Perl>

Los geht’s…

Nun, da alles vorbereitet ist, wird die vhost.conf aktiviert:

a2ensite vhost.conf

Wichtig: nur die vhost.conf aktivieren, auf keinen Fall das Template.
Nachdem das erledigt ist, testen wir nochmal:

apache2ctl configtest

Der Server sollte mit „Syntax OK“ antworten. Wenn das der Fall ist, dann kann der Server mit

apache2ctl restart

neu gestartet werden.

Häufige Probleme

Es kann passieren, dass nur der erste Eintrag akzeptiert wird. In dem Fall legt noch eine Datei _default.conf an mit folgendem Inhalt:

NameVirtualHost *:80

Aktiviert diese mit

a2ensite _default.conf

Und startet den Server neu:

apache2ctl restart
Bitte teile den Beitrag, wenn er dir gefällt.
Share on FacebookTweet about this on TwitterPin on PinterestShare on Google+Share on LinkedInEmail this to someone

40 Gedanken zu „Tutorial: Datenbank gestütze Vhosts mit Variablen mit Hilfe von mod_perl

  1. Hallo 🙂 deine Lösung habe ich genau gesucht! Super Anleitung, aber bevor ich es versuche umzusetzen noch eine Frage:

    Ist das ganze dann dynamisch? Also wenn ich einen Eintrag in der DB hinzufüge, wird direkt aktiv oder muss ich zuerst den Apache2 neustarten, bevor er die Änderungen in der DB übernimmt? 🙂

    1. Die Konfiguration muss erst neu geladen werden.
      Du musst dazu nicht den Apache neu starten, es reicht ein reload.

      Das liegt in der Natur des Apache, dass die Konfiguration nach einer Änderung immer neu geladen werden muss.

  2. Okay, danke für die schnelle Antwort :D. Ich wollte es eben so ermöglichen, dass man die vHosts bequem über die DB managen kann und es kein Restart mehr braucht.

    Es gibt ja da diese mod_vhost_dbd, die dann irgendwie ein SQL-Query auslöst und in der DB nach dem RootDir sucht. Da hab ich aber noch keine gescheite Anleitung gefunden…

  3. Weiss nicht genau wie der Funktioniert, aber gemäss den Beschreibungen in den weiten des Internets geschieht dann alles vollautomatisch.

    Nach meinem Verständnis braucht er die DB gar nicht überwachen. Sondern wenn ich einen Request an test.example.com mache löst der Virtualhost-Eintrag ein DBQuery aus und fragt die MySQL-DB ob ein entsprechender Eintrag zu dem Host „test“ vorhanden ist.

    Wenn ja liefert es den Pfad zurück und Apache setzt den zurückgelieferten Pfad als DocumentRoot.

    1. Da bin ich jetzt neugierig.
      Schalt doch mal das Logging von MySQL an:
      /etc/mysql/my.cnf:
      general_log_file = /var/log/mysql/mysql.log # am Anfang entfernen
      general_log = 1 # am Anfang entfernen
      Mysql neu starten

      Und dann schau mal im Log nach, ob der bei jedem Request eine DB-Anfrage macht.

  4. Hab nun etwa 30 Requests im Abstand von einer Sekunde gemacht und er hat nur ein einziges mal „50 Execute SELECT path FROM vhost WHERE host = testtesttest.mydomain.com“ geloggt. Von dem her scheint er sich das zu cachen.

  5. Das weiss ich auch nicht genau. Wird wohl der Typ der Aktion sein… also 50 = Execute, Prepare und Connect haben auch einen entsprechenden Code vorne dran :).

  6. Vielleicht bin ich auch zu blöd aber wie muss ich die vhosts genau eintragen? immer wenn ich einen vhost anlege und den apache reloade geht nix mehr. muss dann über die kommandozeile in den mysql und den eintrag löschen dass mein phpmyadmin wieder läuft … danke für die hilfe

  7. ja das is komisch im error log kommt gar keine meldung. aber ich denke es liegt am alias eintrag ich weis nur nicht genau welche werte im alias reinkommen, servername is die domain richtig? dir is mir auch klar aber alias leider net.

    1. alias kann leer bleiben. Das ist nur interessant, wenn weitere Domains auf das selbe Verzeichnis zeigen sollen. In dem Falle alle weiteren Domains mit Leerzeichen getrennt da eintragen.

  8. Hi, wie kann ich bei der Lösung mehrere php_admin_value verwenden?
    Bräuchte für open_basedir, upload_tmp_dir und session.save_path dynamische Werte.

    $php_admin_value = „open_basedir „.$DocumentRoot.“:/usr/share/php“;
    würde theoretisch gehen allerdings wenn ich dann die anderen Werte über $php_admin_value setzen will werden diese ja überschrieben :/ .

  9. Hallo.

    Warum sind denn im Template zwei Vhosts drin?
    Ich hatte den Code so verstanden dass die vhost.conf für jeden Eintrag in der DB einen VHost anlegt?

    Ich kriege hier allerdings tatsächlich immer nur den VHost mit dem ersten DB Eintrag.

    Für jeden Tip dankbar.

  10. Hi…

    sagemal, ich teste grad Deine Config. Mein Problem ist, das nur der erste virt. Host aus der DB erkannt wird. Es scheint so, als wenn die anderen VHosts nicht aktiviert werden. Eine Idee? Gruss aus Köln!

    1. Wie bei deinem Vorredner:

      Wenn das die einzige Config-Datei ist, dann leg mal noch eine default.conf an mit dem folgenden Inhalt und aktiviere sie:

      NameVirtualHost *:80

  11. Hi Danke für die Anleitung.

    Mein Problem ist, das immer nur der erste virt. Host aus der DB erkannt wird. Es scheint so, als wenn die anderen VHosts nicht aktiviert werden. Ich hab aus auch mit der default.conf wie beschrieben versucht kein Erfolg. 🙁

    Noch eine Idee woran es liegen kann?
    Danke

    1. Klingt nach einem Fehler im Template.

      Ein ähnliches Problem hatte ich auf einem Server im Zusammenspiel mit einem Wildcard SSL-Zertifikat. Da hatte ich einen kleinen Fehler im SSL-BVereich des Templates und es wurde auch immer nur der erste Host genommen.

  12. Hallo, ich wollte deine Methode als Alternative zu dbd-modules, da dbd-modules nach einem Update meines Debian nicht mehr funktionierte, verwenden.

    Allerdings habe ich zwei Probleme:
    1. Es werden nach dem Reload bei allen Domains nur der DocumentRoot von dem ersten Eintrag meiner Tabelle verwendet (ich habe es auch schon mit der _default.conf – Datei versucht)
    2. Ich hätte es gerne, dass nicht alle Einträge (SELECT …), sondern nur der jeweilige Eintrag (SELECT … WHERE …) aus der Datenbank gelesen wird. Ist das irgendwie möglich?

    1. 1. Da ich davon ausgehe, dass du den Punkt „häufige Probleme“ beachtet hast, klingt nach einem Fehler im Template.

      Ein ähnliches Problem hatte ich auf einem Server im Zusammenspiel mit einem Wildcard SSL-Zertifikat. Da hatte ich einen kleinen Fehler im SSL-Bereich des Templates und es wurde auch immer nur der erste Host genommen.

      Schick mir mal das Template, dann schau ich mal drüber.

      2. Natürlich geht das. Muss im Perl-Script nur die SQL-Anfrage nach deinen Wünschen angepasst werden.

  13. Hallo,

    ich habe gerade versucht in die template Datei einige php ini-settings einzubauen, jedoch scheitert es bei einigen values:
    […]
    php_admin_value => „upload_tmp_dir /var/www/html/web/tmp“,
    php_admin_value => „error_reporting -1“,
    […]

    wobei error reporting funktioniert! Jemand eine Ahnung wieso das nicht klappt?

  14. Hi,

    lässt sich so auch open_basedir via php_admin_value setzen? Wenn ja, wäre ein kleines Beispiel nett. Ich kenne mich leider überhaupt nicht mit Perl aus.

    Gruß
    Dusty

  15. Hi Stefan

    Okay nach dem ich das ganze unter Ubuntu 16.04 installiert habe, kommt ein neue Problem, egal was ich für Server in der Datenbank anlege, ich lande immer auf der default Seite vom Apache und in /etc/apache2/sites-available liegen folgende Dateien:
    root@develserver:/etc/apache2/sites-available# ls
    000-default.conf default.conf default-ssl.conf template vhost.conf
    die 000-de… war schon immer da und ist auch geladen in der default.conf habe ich nur das eingetragen was einer oben steht….
    In den Logs stehen keine Fehlermeldungen

    Ich hoffe Du kannst mir ein Tipp geben.

    1. okay, scheinbar bringt mich das hier weiter: apache2ctl configtest
      ———–
      root@develserver:/etc/apache2/sites-enabled# apache2ctl configtest
      AH00548: NameVirtualHost has no effect and will be removed in the next release /etc/apache2/sites-enabled/default.conf:1
      AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/vhost.conf:
      Can’t locate DBI.pm in @INC (you may need to install the DBI module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.22.1 /usr/local/share/perl/5.22.1 /usr/lib/x86_64-linux-gnu/perl5/5.22 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.22 /usr/share/perl/5.22 /usr/local/lib/site_perl . /etc/apache2) at /etc/apache2/sites-enabled/vhost.conf line 2.\nBEGIN failed–compilation aborted
      Action ‚configtest‘ failed.
      The Apache error log may have more information.

      Na toll, was denn nun wieder ?!

      1. okay Fehler behoben, jedoch lande ich erst einmal immer im default Rootdir und ist es richtig das man
        apache dann reloaden muss?
        Wenn ich einen neuen Server in die DB schreibe wird dieser erstmal ignoriert, jedoch nach reload apache gehts dann einwandfrei
        Muss das so sein?

        1. Hallo Andreas,

          da hat sicherlich das DBI-Modul für Perl gefehlt?
          Es ist korrekt, dass der Apache neu geladen werden muss, damit die Änderungen übernommen werden. Es reicht aber ein reload, ein restart ist unnötig.

          1. HI Stefan
            Da hilft wohl nur noch ein abgesichertes exec Script was das reload macht im Anschluss oder kann man das irgendwie umgehen?

          2. Ich verwende diese Lösung auch auf mehreren Servern. Teilweise mit mehreren hundert Einträgen. Natürlich muss da auch ein gewisser Automatismus her.
            Ich habe mir eine Weboberfläche für die Administration gemacht. Jedoch wird da trotzdem nicht automatisiert der Apache neu geladen, sondern nur auf Knopfdruck und per Cronjob jede Nacht.
            Wobei der Knopfdruck nicht direkt ein Reload ausführt (das darf der Apache-User (www-data) nicht, wäre ja noch schöner…), es wird ein Shellscript aufgerufen, welches das macht. Ausschließlich dieses Shellscript darf www-data mit erweiterten Rechten ausführen.

          3. Ja es war das Perlmodul: ging aber bei Ubuntu mit folgender Zeile:
            sudo apt-get install libapache2-mod-perl2 -y && apt-get install libdbd-mysql-perl -y

          4. Ohh, da ich mich wohl falsch ausgedrückt: Klar soll der Reload vom Aachen nicht einfach los laufen, sondern aus meinem Adminbackend und auch nur dann wenn so wie Du sagst der „Button“ für reload geklickt wird. Das meine ich im groben auch, also Sudo Befehl mit PHP absetzen aber nur das eine Script wo ausschließlich ein reload drin ist und nichts anderes. So meinst Du das doch oder?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.