Apache2 mit mod_fcgid und PHP5 auf CentOS 6
mod_fcgid
führt PHP nicht mit Berechtigung des Webservers aus (normalerweise apache
oder www-data
), sondern mit der des Eigentümers. Dies ist auf gemeinsam benutztem Webspace mit verschiedenen virtuellen Hosts natürlich wesentlich sicherer, weshalb Webinterface-Manager wie ISPConfig davon Gebrauch machen. Zur Erhöhung der Sicherheit wird zusätzlich open_basedir
für jeden vHost verwendet. Dies verbietet dem Webserver die Ausführung von PHP auf fremden vHosts bzw. gestattet PHP nur auf dem eigenen Webspace.
In dieser Anleitung wird auf ein Webinterface wie ISPConfig verzichtet, statt dessen wird erläutert wie man die virtuellen Hosts manuell konfiguriert, mit und ohne SSL, daher mit jeweils eigenen IP-Adressen.
Zum Einsatz kommen noch MySQL 5 und phpMyAdmin 4.0. Gewiß heutzutage ältere Versionen, aber hin und wieder ergeben sich Situationen, wo man froh ist dafür ein funktionierendes Tutorial zu haben. Zumal mod_fcgid
nicht in den Repos von CentOS enthalten ist und man daher auf Extra Packages angewiesen ist.
Wenn man nicht hunderte von vHosts zu konfigurieren hat sondern nur einige wenige, ist die hier beschrieben Methode sogar einfacher da nicht so umfangreich und kompliziert wie jene mit ISPConfig. Alles in allem eine elegante Lösung, aber nichts für Klick-Admins! 😉
Versionsübersicht
In diesem Tutorial werden folgende Versionen benutzt:
- CentOS-6.9-x86_64-minimal
- kernel-2.6.32-696.el6.x86_64
- httpd-2.2.15-60.el6.centos.6.x86_64
- mod_fcgid-2.3.9-1.el6.x86_64
- php-5.3.3-49.el6.x86_64
- mysql-server-5.1.73-8.el6_8.x86_64
- phpMyAdmin-4.0.10.20
- postfix-2.6.6-8.el6.x86_64
CentOS installieren
Prädestiniert hiefür ist die VirtualBox von Oracle. Wir laden uns also die Datei CentOS-6.9-x86_64-minimal.iso
(etwas über 400 MB) von einem schnellen Mirror herunter, erstellen in der VirtualBox eine neue virtuelle Maschine (Red Hat 64-bit) mit 20 GB Harddisk, 4 GB RAM und gebridgtem Netzwerk und fahren die ISO-Datei damit hoch. Nach ein paar Minuten ist die virtuelle Maschine auch schon installiert und wir können (neu)starten.
Netzwerkkarte aktivieren
Aus einem unerfindlichen Grund scheint in der virtuellen Maschine das Netzwerk nicht zu funktionieren, wie ein ifconfig
an der Oracle VM Konsole zeigt. Erst ein ifup eth0
startet das Netzwerk, welches aber beim nächsten Neustart wieder „verschwunden“ ist. Damit es einen Neustart überlebt, muß ONBOOT
auf yes
stehen:
vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
HWADDR=01:02:03:04:05:06
TYPE=Ethernet
UUID=71558e170-1b5-57a2-5afd-c4704c32ddc
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=dhcp
Nach einem
service network restart
ist die Netzwerkkarte persistent.
Updates installieren
yum -y update
Firewall deaktivieren
service iptables stop chkconfig iptables off service ip6tables stop chkconfig ip6tables off
SELINUX deaktivieren
vi /etc/selinux/config
# This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. #SELINUX=enforcing SELINUX=disabled # SELINUXTYPE= can take one of these two values: # targeted - Targeted processes are protected, # mls - Multi Level Security protection. SELINUXTYPE=targeted
reboot
EPEL installieren
EPEL sind die Extra Packages for Enterprise Linux – mod_fcgid
ist nämlich nicht in den Standardrepos von CentOS enthalten.
yum -y install epel-release
YUM-Priorities installieren
Sehr wichtig, das sonst früher oder später Kompatibilitätsprobleme auftauchen werden!
yum install yum-priorities -y
Vertrauen ist gut – Kontrolle ist besser!
vi /etc/yum/pluginconf.d/priorities.conf
Hier muß ein Einser stehen:
[main] enabled = 1
CentOS-Base.repo erhält höchste Priorität
vi /etc/yum.repos.d/CentOS-Base.repo
[base] name=CentOS-$releasever - Base mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra #baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-6 priority=1
[base], [updates] und [extras] erhalten Priorität 1, [contrib] und [centosplus] erhalten Priorität 2.
EPEL.repo erhält niedrigste Priorität
vi /etc/yum.repos.d/epel.repo
[epel] name=Extra Packages for Enterprise Linux 6 - $basearch #baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch failovermethod=priority enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6 priority=10
[epel], [epel-debuginfo] und [epel-source] erhalten die Priorität 10.
mod_fcgid installieren
Zuerst ein kurzes
yum update
um dann zum Höhepunkt zu kommen:
yum install mod_fcgid -y
PHP5 Apache-Modul deaktivieren
cd /etc/httpd/conf.d/
mv php.conf php.conf.disabled
cgi.fix_pathinfo=1 unkommmentieren
also das Semikolon zu Beginn der Zeile entfernen
vi /etc/php.ini
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting
; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting
; of zero causes PHP to behave as before. Default is 1. You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
; http://www.php.net/manual/en/ini.core.php#ini.cgi.fix-pathinfo
cgi.fix_pathinfo=1
PHP_Fix_Pathinfo enablen
vi /etc/httpd/conf.d/fcgid.conf
# This is the Apache server configuration file for providing FastCGI support
# through mod_fcgid
#
# Documentation is available at
# http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html
LoadModule fcgid_module modules/mod_fcgid.so
# Use FastCGI to process .fcg .fcgi & .fpl scripts
AddHandler fcgid-script fcg fcgi fpl
# Sane place to put sockets and shared memory file
FcgidIPCDir /var/run/mod_fcgid
FcgidProcessTableFile /var/run/mod_fcgid/fcgid_shm
# Diese Zeile am Ende hinzufügen:
PHP_Fix_Pathinfo_Enable 1
Apache beim Neustart automatisch starten
chkconfig --levels 235 httpd on
Zwischenzeitlich mal den Apache neu starten
service httpd restart
Nutzer und Gruppen anlegen
Unser Konzept sieht vor, daß es für jeden vHost einen eigenen User und eigene Gruppe geben soll und somit für jeden eigene Berechtigungen. Wir nummerieren ganz einfach durch und beginnen mit 1, also web1
, web2
, web3
, usw.
groupadd site1 groupadd site2 useradd -s /bin/false -d /var/www/sites/site1 -m -g site1 web1 useradd -s /bin/false -d /var/www/sites/site2 -m -g site2 web2 chmod 755 /var/www/sites/site1 chmod 755 /var/www/sites/site2
Document-Root erstellen und Eigentümer zuweisen
mkdir -p /var/www/sites/site1/web mkdir -p /var/www/sites/site2/web mkdir -p /var/www/sites/site1/tmp mkdir -p /var/www/sites/site2/tmp chown -R web1:site1 /var/www/sites/site1 chown -R web2:site2 /var/www/sites/site2
PHP-FCGI-Wrapper erstellen
Wir betreiben hier PHP mit SuExec und das erlaubt nichts außerhalb des SuExec Document Root. Dies kann zum Beispiel zu Problemen mit phpMyAdmin führen, doch davon später mehr.
/usr/sbin/suexec -V
-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/httpd/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=500
-D AP_USERDIR_SUFFIX="public_html"
Wie man sieht, ist das SuExec Document Root /var/www
. Weil aber die PHP-Binary in /usr/bin
liegt (/usr/bin/php-cgi
, also außerhalb des SuExec Document Root), kann diese nicht direkt aufgerufen werden da dies von SuExec verboten wird. Weil SuExec auch keine Symlinks zuläßt, ist der einzige Weg über einen Wrapper, also ein Script innerhalb des SuExec Document Root, welches die PHP-Binary aufruft und deshalb ausführbar sein muß. Weil aber der Wrapper nur vom Eigentümer des vHosts aufgerufen werden kann, ist für jeden virtuellen Host ein eigener Wrapper notwendig, welcher die Benutzerrechte des jeweiligen vHosts haben muß.
mkdir -p /var/www/php-fcgi-scripts/site1 mkdir -p /var/www/php-fcgi-scripts/site2
vi /var/www/php-fcgi-scripts/site1/.php-fcgi-starter
#!/bin/sh PHPRC=/etc/ export PHPRC export PHP_FCGI_MAX_REQUESTS=5000 export PHP_FCGI_CHILDREN=8 exec /usr/bin/php-cgi \ -d sendmail_path="/usr/sbin/sendmail -t -i -fwebmaster@site1.lan" \ -d session.save_path=/var/www/sites/site1/tmp \ -d upload_tmp_dir=/var/www/sites/site1/tmp \ -d open_basedir=/var/www/sites/site1/web/:/var/www/sites/site1/tmp/:/usr/share/php/
vi /var/www/php-fcgi-scripts/site2/.php-fcgi-starter
#!/bin/sh PHPRC=/etc/ export PHPRC export PHP_FCGI_MAX_REQUESTS=5000 export PHP_FCGI_CHILDREN=8 exec /usr/bin/php-cgi \ -d sendmail_path="/usr/sbin/sendmail -t -i -fwebmaster@site2.lan" \ -d session.save_path=/var/www/sites/site2/tmp \ -d upload_tmp_dir=/var/www/sites/site2/tmp \ -d open_basedir=/var/www/sites/site2/web/:/var/www/sites/site2/tmp/:/usr/share/php/
PHPRC
ist der Pfad zur php.ini
. Wenn für jeden vHost eine eigene php.ini
benötigt wird, einfach die /etc/php.ini
nach /var/www/sites/siteX
kopieren und im Wrapper den Pfad ändern. In diesem Fall wäre es PHPRC=/var/www/sites/siteX
.
chmod 755 /var/www/php-fcgi-scripts/site1/.php-fcgi-starter chmod 755 /var/www/php-fcgi-scripts/site2/.php-fcgi-starter chown -R web1:site1 /var/www/php-fcgi-scripts/site1 chown -R web2:site2 /var/www/php-fcgi-scripts/site2
Virtuelle Hosts einrichten
Es werden 2 virtuelle Hosts, jeweils mit und ohne SSL, mit verschiedenen IP-Adressen konfiguriert.
www.site1.lan ≙ site1.lan ≙ 192.168.16.49 www.site2.lan ≙ site2.lan ≙ 192.168.16.150
Damit es auch funktioniert, muß dies entweder im Nameserver (A, PTR und MX-Records) oder in der Hosts-Datei eingetragen werden, ansonsten kann Apache keine Namensauflösung durchführen. Die Hosts-Datei ist allerdings weniger gut geeignet, da man hier keine MX-Records eintragen kann. Es geht eben nichts über einen eigenen Nameserver!
Virtuelles Netzwerkinterface einrichten
Die erste IP-Adresse in diesem Tutorial wurde durch den DHCP-Server an eth0
zugewiesen und die zweite ist virtuell. Solange man aber dies dem Netzwerkinterface nicht mitteilt, wird der zweite virtuelle Host nicht funktionieren. Wir erstellen also das virtuelle Netzwerkinterface mit
vi /etc/sysconfig/network-scripts/ifcfg-eth0:0
# Please read /usr/share/doc/initscripts-*/sysconfig.txt # for the documentation of these parameters. DEVICE=eth0:0 BOOTPROTO=none NETMASK=255.255.255.0 TYPE=Ethernet IPADDR=192.168.16.150 IPV6INIT=no USERCTL=no
Nach einem Neustart des Netzwerkes mit
service network restart
ist das virtuelle Netzwerkinterface auch schon präsent, wie ein ifconfig
zeigt:
eth0 Link encap:Ethernet Hardware Adresse 01:02:03:04:05:06 inet Adresse:192.168.16.49 Bcast:192.168.16.255 Maske:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:169992 errors:0 dropped:0 overruns:0 frame:0 TX packets:71039 errors:0 dropped:0 overruns:0 carrier:0 Kollisionen:0 Sendewarteschlangenlänge:1000 RX bytes:110034286 (104.9 MiB) TX bytes:25549000 (24.3 MiB) eth0:0 Link encap:Ethernet Hardware Adresse 01:02:03:04:05:06 inet Adresse:192.168.16.150 Bcast:192.168.16.255 Maske:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 lo Link encap:Lokale Schleife inet Adresse:127.0.0.1 Maske:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:60 errors:0 dropped:0 overruns:0 frame:0 TX packets:60 errors:0 dropped:0 overruns:0 carrier:0 Kollisionen:0 Sendewarteschlangenlänge:0 RX bytes:70654 (68.9 KiB) TX bytes:70654 (68.9 KiB)
Apache-Konfigurationsdatei modifizieren
vi /etc/httpd/conf/httpd.conf
Am Ende anfügen:
##################################################################################### LogFormat "%v\t%h\t%l\t%u\t%t\t%r\t%>s\t%b\t%{Referer}i\t%{User-Agent}i" tab-combined CustomLog logs/access_log tab-combined DirectoryIndex index.php index.shtml index.html # Domainredirects Include redirects.d # vSites Include sites.d/www.site1.lan Include sites.d/www.site2.lan
Verzeichnis für vHosts einrichten
mkdir /etc/httpd/sites.d
Verzeichnis für Domainredirects einrichten
mkdir /etc/httpd/redirects.d
Domainredirects sind natürlich nur interessant für Domaingräbbler oder Leute, die glauben verschiedene Domains besitzen zu müssen.
In redirects.d
wird für jede Domain eine eigene Datei angelegt. Als Beispiel soll die Domain irgendwas.lan
(mit und ohne www) auf irgendwoh.in/?ras=klat
zeigen. Natürlich müssen auch der Nameserver und das Netzwerkinterface davon wissen, sonst wird es nicht funktionieren.
vi /etc/httpd/redirects.d/irgendwas.lan
NameVirtualHost 192.168.16.254:80 <VirtualHost 192.168.16.254:80> ServerName irgendwas.lan ServerAlias www.irgendwas.lan Redirect / http://irgenwoh.in/?ras=klat </VirtualHost>
vHost für site1 konfigurieren
vi /etc/httpd/sites.d/www.site1.lan
NameVirtualHost 192.168.16.49:80 <VirtualHost 192.168.16.49:80> Include sites.d/www.site1.lan.inc </VirtualHost> NameVirtualHost 192.168.16.49:443 <VirtualHost 192.168.16.49:443> Include sites.d/www.site1.lan.inc Include sites.d/www.site1.lan.ssl </VirtualHost>
vi /etc/httpd/sites.d/www.site1.lan.inc
ServerName www.site1.lan ServerAlias site1.lan ServerAdmin webmaster@site1.lan DocumentRoot /var/www/sites/site1/web/ <IfModule mod_fcgid.c> SuexecUserGroup web1 site1 <Directory /var/www/sites/site1/web/> Options +ExecCGI AllowOverride All AddHandler fcgid-script .php FCGIWrapper /var/www/php-fcgi-scripts/site1/.php-fcgi-starter .php Order allow,deny Allow from all </Directory> </IfModule>
vi /etc/httpd/sites.d/www.site1.lan.ssl
<IfModule mod_ssl.c> SSLEngine on SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder on SSLCertificateFile /etc/pki/tls/certs/localhost.crt SSLCertificateKeyFile /etc/pki/tls/private/localhost.key </IfModule>
vHost für site2 konfigurieren
vi /etc/httpd/sites.d/www.site2.lan
NameVirtualHost 192.168.16.150:80 <VirtualHost 192.168.16.150:80> Include sites.d/www.site2.lan.inc </VirtualHost> NameVirtualHost 192.168.16.150:443 <VirtualHost 192.168.16.150:443> Include sites.d/www.site2.lan.inc Include sites.d/www.site2.lan.ssl </VirtualHost>
vi /etc/httpd/sites.d/www.site2.lan.inc
ServerName www.site2.lan ServerAlias site2.lan ServerAdmin webmaster@site2.lan DocumentRoot /var/www/sites/site2/web/ <IfModule mod_fcgid.c> SuexecUserGroup web2 site2 <Directory /var/www/sites/site2/web/> Options +ExecCGI AllowOverride All AddHandler fcgid-script .php FCGIWrapper /var/www/php-fcgi-scripts/site2/.php-fcgi-starter .php Order allow,deny Allow from all </Directory> </IfModule>
vi /etc/httpd/sites.d/www.site2.lan.ssl
<IfModule mod_ssl.c> SSLEngine on SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder on SSLCertificateFile /etc/pki/tls/certs/localhost.crt SSLCertificateKeyFile /etc/pki/tls/private/localhost.key </IfModule>
Geschafft!
service httpd restart
Was jetzt noch fehlt ist Content für die beiden vHosts. Ohne Content wird Apache seine Default-Seite anzeigen.
vi /var/www/sites/web1/web/index.php
<?php phpinfo(); ?>
chown web1:web1 /var/www/sites/web1/web/index.php
MySQL
Sollte eigentlich schon installiert sein.
rpm -qa | grep mysql
mysql-5.1.73-8.el6_8.x86_64 mysql-libs-5.1.73-8.el6_8.x86_64 php-mysql-5.3.3-49.el6.x86_64 mysql-server-5.1.73-8.el6_8.x86_64
chkconfig --list|grep mysql
mysqld 0:Aus 1:Aus 2:Aus 3:Aus 4:Aus 5:Aus 6:Aus
Offenbar wird MySQL beim Neustart der virtuellen Maschine nicht automatisch gestartet, dies nachholen mit
chkconfig --levels 235 mysqld on
Nachdem das Root-Passwort für MySQL gesetzt wurde, den Server neu starten
service mysqld restart
netstat -putan | grep mysql
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 10194/mysqld
Sieht gut aus! Jetzt steht phpMyAdmin nichts mehr im Weg.
phpMyAdmin
Die allseits bekannte und beliebte Datenbankadministrationsoberfläche ist in der EPEL-Repo einthalten.
yum install phpmyadmin
Dies installiert phpMyAdmin in
/usr/share/phpMyAdmin
Da wir aber ein System mit SuExec betreiben, wird phpMyAdmin klarerweise nicht funktionieren, wie oben im Abschnitt „PHP-FCGI-Wrapper erstellen“ erläutert. Es wird dauernd ein „403 Forbidden“ kommen, ganz gleich wie die Zugriffsrechte von phpMyAdmin eingestellt sind.
Wir lassen also in diesem Fall die EPEL-Repo aus und installieren phpMyAdmin manuell, am besten in unserer site1.lan
, auf die nur der Admin zugreifen dürfen wird. Wir laden uns die letzte funktionierende Version für PHP 5.3 (phpMyAdmin-4.0.10.20-all-languages.zip
) von phpmyadmin.net/downloads/ herunter
cd /var/www/sites/web1/web wget https://files.phpmyadmin.net/phpMyAdmin/4.0.10.20/phpMyAdmin-4.0.10.20-all-languages.zip
Ein schnelles yum install unzip
installiert den Entpacker für ZIP-Dateien.
unzip phpMyAdmin-4.0.10.20-all-languages.zip mv phpMyAdmin-4.0.10.20-all-languages phpMyAdmin chown -R web1:web1 phpMyAdmin
Das wars eigentlich schon, jetzt ist phpMyAdmin auf https://www.site1.lan/phpMyAdmin
erreichbar.