Apache - virtuelle Hosts
Datum: 2024-06-13
Distribution: getestet mit Debian 12 (ebenso mit Debian 11 und 10 machbar)
Mit virtuellen Hosts lassen sich mehrere Kunden mittels nur einer Apache-Installation bedienen. Man unterscheidet:
IP-basierte virtuelle Hosts (die Netzwerkkarte erhält mehrere Adressen, früher: IPv4-Aliase)
Namensbasierte virtuelle Hosts (der Host hat nur eine Adresse, wird aber über mehrere Namen angesprochen)
Die Webdokumente werden dann aus verschiedenen Verzeichnissen ausgeliefert, in unserem Fall seien dies
/srv/vhosts/firma1
/srv/vhosts/firma2
Um die Konfigurationsdateien anschaulich ohne Kommentare und Leerzeilen ausgeben zu können, ist der folgende Alias sinnvoll:
alias gg="grep --color -Ev '^\s*#|^$'"
alias gg >> ~/.bashrc
IP-basierte virtuelle Hosts
Das Folgende soll nur als Übersicht dienen. Im einfachsten Fall werden einer Netzwerkkarte mittels Aliasen mehrere Adressen mitgegeben:
ifconfig eth0:0 80.0.0.1 netmask 255.255.255.224 # für Firma 1
ifconfig eth0:1 80.0.0.2 netmask 255.255.255.224 # für Firma 2
Die erste Konfigurationsdatei würde dann entsprechend so aussehen, man beachte <VirtualHost 80.0.0.1:80>
, außerdem muss als wichtige Direktive ServerName
aktiviert und angepasst werden:
<VirtualHost 80.0.0.1:80>
ServerName www.firma1.test
ServerAlias debvhost.firma1.test
ServerAdmin webmaster@firma1.test
DocumentRoot /srv/vhosts/firma1
<Directory /srv/vhosts/firma1/>
Options none
AllowOverride none
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error-firma1.log
CustomLog ${APACHE_LOG_DIR}/access-firma1.log combined
</VirtualHost>
Die zweite Konfigurationsdatei sieht dann in etwa so aus, man beachte wiederum die erste Zeile, nunmehr auf <VirtualHost 80.0.0.2:80>
geändert, weiterhin spielt die Zeile ServerName www.firma2.test
eine wichtige Rolle:
<VirtualHost 80.0.0.2:80>
ServerName www.firma2.test
ServerAlias debvhost.firma2.test
ServerAdmin webmaster@firma2.test
DocumentRoot /srv/vhosts/firma2
<Directory /srv/vhosts/firma2/>
Options none
AllowOverride none
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error-firma2.log
CustomLog ${APACHE_LOG_DIR}/access-firma2.log combined
</VirtualHost>
Schließlich muss die Namensauflösung z.B. mittels /etc/hosts
mit zwei IP-Adressen durchgeführt werden:
80.0.0.1 www.firma1.test debvhost.firma1.test
80.0.0.2 www.firma2.test debvhost.firma2.test
Namensbasierte virtuelle Hosts
In älteren Apache-Versionen gab es dazu als Voraussetzung in der globalen httpd.conf
die Anweisung NameVirtualHost *:80
, die aber nur das Akzeptieren von bestimmten Requests betraf und nicht das Port-Listening definiert (das geschieht mittels Listen 80
). Siehe dazu https://httpd.apache.org/docs/2.2/en/vhosts/name-based.html
Die grundlegenden Schritte zur praktischen Einrichtung sind:
Verschiedene Verzeichnisse für DocumentRoot erzeugen, z.B. /srv/vhosts/firma1
Dort drin unterschiedliche index.html Dateien erzeugen
Kopieren und Umbenennen der Datei 000-default.conf, z.B. in firma1.conf
Anpassungen in firma1.conf
Kopieren und Umbenennen der firma1.conf in firma2.conf
Aktivieren der VHosts, reload der Config
Namenausflösung sicherstellen (anfänglich/testweise in der /etc/hosts)
Testen…
zu 1) und 2) DocumentRoot, Rechte und Webdokumente
Zuerst legen wir die beiden Verzeichnisse an, setzen die Rechte so, dass Kunden selber Dateien ablegen können und erzeugen zwei Textdateien für das abschließende Testen.
mkdir -p /srv/vhosts/firma{1,2}
chown -v www-data: /srv/vhosts/firma?
echo 'Das ist der erste VHost für Firma 1' > /srv/vhosts/firma1/index.html
echo 'This is the VHost for Firma 2' > /srv/vhosts/firma2/index.html
zu 3) und 4) Konfigurationsdatei für Firma1 erstellen
cd /etc/apache2/sites-available
cp 000-default.conf firma1.conf
vi firma1.conf
gg firma1.conf
<VirtualHost *:80>
ServerName www.firma1.test
ServerAlias debvhost.firma1.test
ServerAdmin webmaster@firma1.test
DocumentRoot /srv/vhosts/firma1
<Directory /srv/vhosts/firma1/>
Options none
AllowOverride none
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error-firma1.log
CustomLog ${APACHE_LOG_DIR}/access-firma1.log combined
</VirtualHost>
zu 5) Konfigurationsdatei für Firma2 erstellen
gg firma1.conf | sed 's/firma1/firma2/g' > firma2.conf
cat firma2.conf
<VirtualHost *:80>
ServerName www.firma2.test
ServerAlias debvhost.firma2.test
ServerAdmin webmaster@firma2.test
DocumentRoot /srv/vhosts/firma2
<Directory /srv/vhosts/firma2/>
Options none
AllowOverride none
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error-firma2.log
CustomLog ${APACHE_LOG_DIR}/access-firma2.log combined
</VirtualHost>
zu 6) Aktivieren der VHosts, reload der Config
a2ensite firma1
a2ensite firma2
ls -ltrc /etc/apache2/sites-enabled/
apachectl graceful
zu 7) Namenausflösung sicherstellen (anfänglich/testweise in der /etc/hosts)
echo '127.0.0.1 debvhost.firma1.test debvhost.firma2.test' >> /etc/hosts
echo '127.0.0.1 www.firma1.test www.firma2.test firma1.test firma2.test' >> /etc/hosts
zu 8) Testen der Vhost-Konfiguration
# Es erscheint "Das ist der erste VHost für Firma 1":
wget -qO- debvhost.firma1.test
wget -qO- www.firma1.test
# Es erscheint "This is the VHost for Firma 2":
wget -qO- debvhost.firma2.test
wget -qO- www.firma2.test
Privates Verzeichnis für Firma 2
Für Firma 2 soll nun ein privates Verzeichnis mit Zugriffsschutz eingerichtet werden, hier direkt in der Konfigurationsdatei (also nicht mittels separater .htaccess
-Datei, das ist unter Zur Methode A) via .htaccess beschrieben).
Zuerst legen wir das Verzeichnis und ein kleines Webdokument an:
mkdir /srv/vhosts/firma2/private
echo "Willkommen im privaten Verzeichnis." > /srv/vhosts/firma2/private/index.html
Nun folgt das Ergänzen der Konfigurationsdatei /etc/apache2/sites-enabled/firma2.conf
um 6 Zeilen, beginnend mit der einleitenden Zeile, die das neue Verzeichnis definiert: <Directory "/srv/vhosts/firma2/private/">
. Ohne Kommentare sieht diese Konfigurationsdatei schließlich so aus:
<VirtualHost *:80>
ServerName www.firma2.test
ServerAlias debvhost.firma2.test
ServerAdmin webmaster@firma2.test
DocumentRoot /srv/vhosts/firma2
<Directory /srv/vhosts/firma2/>
Options none
AllowOverride none
Require all granted
</Directory>
<Directory "/srv/vhosts/firma2/private/">
AuthName "Private"
AuthType Basic
AuthUserFile /etc/apache2/passwords/access-passwd.firma2
Require valid-user
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error-firma2.log
CustomLog ${APACHE_LOG_DIR}/access-firma2.log combined
</VirtualHost>
Nicht vergessen, diese Änderung mit apachectl graceful
einzulesen!
Jetzt wird noch eine entsprechende Password-Datei mit zwei Benutzern erzeugt (bei der zweiten Zeile -c
weglassen!!):
htpasswd -c /etc/apache2/passwords/access-passwd.firma2 user01
htpasswd /etc/apache2/passwords/access-passwd.firma2 user02
Und schließlich kann es schon ans Testen gehen; ohne die Benutzerauthentifizierung erhalten wir aber keinen Output (wget-Fehler via Exit-Code 6):
wget -qO- www.firma2.test/private/ ; echo $?
Übergeben wir nun aber den Benutzernamen samt Passwort, wird „Willkommen im privaten Verzeichnis.“ mit dem nachfolgenden Exit-Code 0 (Erfolg) ausgegeben:
wget -qO- --user="user01" --password="1234567" www.firma2.test/private/ ; echo $?
DNS-Zonen für die virtuellen Hosts in Apache
Anstelle der einfachen, dezentralen Namensauflösung mittels ‚/etc/hosts‘, ist für jeden Kunden eine eigene Zone empfehlenswert.
Dazu hier zwei Beispiele für unsere VHosts mittels bind9
. Diese Zonendefinitionen werden einfach zur bestehenden Datei ‚/etc/bind/named.conf.local‘ hinzugefügt.
zone "firma1.test" {
type master;
file "/var/lib/bind/db.firma1.test";
};
zone "firma2.test" {
type master;
file "/var/lib/bind/db.firma2.test";
};
Und dazu ein erstes passendes Zonenfile als Beispiel (db.firma1.test):
$TTL 604800
@ IN SOA dns1.firma1.test. root.dns1.firma1.test. (
5 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS dns1.firma1.test.
; A-Records sind auf alle Fäller erforderlich, ein CNAME-Record wird
; mittels dem tatsächlichen Hostnamen realisiert:
dns1 IN A 10.0.2.200
dns1 IN A 192.168.2.222
; Für ganz kurzen Aufruf: nslookup firma1.test
@ IN A 10.0.2.200
@ IN A 192.168.2.222
www IN CNAME dns1
Im zweiten Zonenfile (db.firma2.test) ist der Hostname dns1 wiederum derselbe, nur der Domänennname ändert sich:
$TTL 604800
@ IN SOA dns1.firma2.test. root.dns1.firma2.test. (
5 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS dns1.firma2.test.
; A-Records sind auf alle Fäller erforderlich, ein CNAME-Record wird
; mittels dem tatsächlichen Hostnamen realisiert:
dns1 IN A 10.0.2.200
dns1 IN A 192.168.2.222
; Für ganz kurzen Aufruf: nslookup firma2.test
@ IN A 10.0.2.200
@ IN A 192.168.2.222
www IN CNAME dns1
SSL-Verschlüsselung für die virtuellen Hosts
Skizzierung der wichtigsten Schritte für Apache 2.2.22
$ cd /etc/ssl/
$ openssl req -new -outform PEM -out certs/testing.pem -newkey rsa:2048 -nodes \
-keyout private/testing.key -keyform PEM -days 3650 -x509
$ openssl x509 -fingerprint -noout -in certs/testing.pem
$ a2enmod ssl
Konfiguration für VHost 1
$ cd /etc/apache2/sites-available/
$ cp default-ssl test01.test-ssl
$ vi test01.test-ssl
Anpassen: <VirtualHost www.test01.test:443>
Anpassen: DocumentRoot, Directory- und Logfile-Einstellungen (s.o.)
Anpassen: Namen der Zertifikatsdateien (.pem und .key der Direktiven
SSLCertificateFile bzw. SSLCertificateKeyFile)
$ a2ensite test01.test-ssl
Konfiguration für VHost 2
$ cp test01.test-ssl test02.test-ssl
$ vi test02.test_ssl
Anpassen: <VirtualHost www.test02.test:443>
Anpassen: DocumentRoot, Directory- und Logfile-Einstellungen (s.o.)
Anpassen: Namen der Zertifikatsdateien (.pem und .key der Direktiven
SSLCertificateFile bzw. SSLCertificateKeyFile)
$ a2ensite test02.test-ssl
$ /etc/init.d/apache2 restart
Tests im Browser
https://192.168.0.222/ <--- kein Zugang (Versuch, die Website zu identifizieren ist fehlgeschlagen)!
https://www.test01.test/ <-- OK
https://www.test02.test/ <-- OK
Aktivieren von SSL für mehrere virtuelle Hosts (Server Name Indication)
Änderung in der Datei /etc/apache2/ports.conf
Im Abschnitt <IfModule…> oberhalb der Zeile Listen 443 hinzufügen von (bei Apache 2.4 gibt es „NameVirtualHost“ nicht mehr):
NameVirtualHost *:443
Anpassung der betreffenden Vhost-Konfiguration für SSL
Änderung von <VirtualHost *default:443>* auf
<VirtualHost *:443>
Erzwingen von https beim Zugriff auf ein Unterverzeichnis
Voraussetzung: a2enmod rewrite
In der Vhost-Konfiguration FÜR PORT 80 unterhalb von DocumentRoot /var/www/test02.test
hinzuzufügen:
# Nur für das Unterverzeichnis 'mail' die Verschlüsselung erzwingen
RewriteEngine on
ReWriteCond %{SERVER_PORT} !^443$
RewriteRule ^/mail/(.*) https://%{HTTP_HOST}/mail/$1 [NC,R,L]
Zur Bedeutung der Flags:
- nocase|NC
Beim Suchmuster wird nicht zwischen Groß- und Kleinschreibung unterschieden
- redirect|R[=Statuscode]
Normalerweise sorgt RewriteRule für eine interne URI-Änderung
- last|L
Mit diesem Flag können Sie dafür sorgen, dass das Rewriting sofort beendet wird; weitere RewriteRules werden nicht mehr beachtet.
Siehe auch: