Aufbau Webserver
From RootForum Community » Wiki
Contents |
Aufbau Webserver
Der Webserver wird etwas komplizierter. Hier müssen wir uns ein paar Gedanken machen. Zum einen soll der Content gesynct werden, zum anderen will man sich mit der Konfiguration auch möglichst leicht tun.
Entsprechend sollte man bei der Installations des Systemes eine eigene Partition für den drbd Sync vor sehen. Dort landen alle vhost basierenden Konfigurationen und Webinhalte. Überlegt euch also, wie gross das sein muss. Des weiteren Empfehle ich eine eigene Partition für die Logfiles und das Backup.
Bei mir sieht es wie folgt aus:
Partiton boot = 256 MB Partition SWAP = Size of RAM Partition / = 25 GB Partition /LOGS = 20 GB Partition /WEB = 250 GB Partition /BACKUP = 250 GB
Dies ist nur ein Vorschlag und kann an die eigenen Bedürfnisse angepasst werden.
Optional Partitionierung basierned auf LVM
Dies ist wirklich optional, da man im drbd Verbund kaum vorteile hat. Bitte beachtet, LVM und drbd in Kombination führte bei debian zu Problemen.
Bei den typischen Rootserver mit 2 Festplatten könnte das FS Layout wie folgt aus sehen:
VG = System Physical Extend 4 MB LVs BOOT 256 MB Partition auf sda1 ext4 SWAP 4 GB 2 Stripe 8 KB ROOT 50 GB 1 Stripe 64 KB BACKUP 256 GB 1 Stripe 64 KB WEB-DATA 128 GB 2 Stripe 8 KB TEMP 10 GB 2 Stripe 8 KB LOGS 10 GB 2 Stripe 8 KB
Die Befehle pvcreate, lvcreate vgcreate werden euch dabei helfen.
-> Je nach Distribution kann man die Partitionen bei der Installation anlegen.
Die Installation des Betriebsystemes der eigenen Wahl setze ich vorraus. Der Webserver selbst basiert in diesem Beispiel auf lighttpd, aber man kann natürlich auch jeden anderen dafür verwenden.
Aufbau drbd
Zuerst einmal wird die Konfiguration angelegt cat>/etc/drbd.conf<<EOF
global {
dialog-refresh 1; usage-count no; minor-count 5;
}
common { }
resource WEB {
protocol C;
disk {
on-io-error pass_on;
}
syncer {
rate 8M;
}
net {
allow-two-primaries;
}
startup {
degr-wfc-timeout 120;
}
on nodea {
device /dev/drbd0;
address 10.10.0.12:7788;
meta-disk internal;
disk /dev/SYSTEM/WEB-DATA;
}
on nodeb {
device /dev/drbd0;
address 10.10.0.13:7788;
meta-disk internal;
disk /dev/SYSTEM/WEB-DATA;
}
}
EOF
Diese Datei auf beiden Nodes ausrollen. Die Devices erzeuge:
drbdadm create-md WEB
danach wird drbd gestartet /etc/init.d/drbd start
Für die erste Einrichtung, geht man wie folgt vor:
drbdadm attach WEB drbdadm syncer WEB drbdadm connect WEB
Diesen Befehl nur auf einer Seite anwenden.
drbdadm -- --overwrite-data-of-peer primary WEB
cat /proc/drbd
sollte nun anzeigen, dass der Storage gesynct wird. Wir warten, bis das abgeschlossen ist. Dies kann mehrere Stunden, abhängig von Partitionsgrösse und Netzwerkinterface, dauern.
-> Oben wurde der Syncer auf eine Rate von 8M eingestellt. Hintergrund ist, dass ich von einer 100Mbit Anbindung des vlans aus ging. Ich möchte an der Stelle tunlichst vermeiden, dass die später Kommunikation vom Proxy zum Webserver und zur Datenbank bei grösseren Schreiboperationen auf dem Storage unnötig leidet. Wer eine höhere Bandbreite am Netzwerk hat, sollte das für sich anpassen. Wir warten mit der weiteren Arbeit, bis sich der Storage initial gesynct hat.
Aubfau Clusterfilesystem
Hier benötigen wir die Binaries für ocfs2-tools sowie ocfs2-tools-o2cb
Wir erzeugen folgende Konfiguration:
cat>/etc/ocfs/cluster.conf<<EOF
node:
name = nodea
cluster = ocfs2
number = 0
ip_address = 10.10.0.12
ip_port = 7777
node:
name = nodeb
cluster = ocfs2
number = 1
ip_address = 10.10.0.13
ip_port = 7777
cluster:
name = ocfs2
node_count = 2
EOF
Danach starten wir auf beiden Nodes ocfs:
/etc/init.d/o2cb start ocfs2
Auf der 1. Node legen wir nun das Filesystem an:
mkfs.ocfs2 /dev/drbd0
Auf der 2. Node wird das drbd device ebenfall primary gesetzt:
drbdadm primary WEB
Nun können wir auf beiden Nodes das gesyncte Device mounten
mount /dev/drbd0 /srv/www
Wir bauen die erforderliche Dateistruktur auf:
cd /srv/ mkdir logs temp sockets cd srv/www mkdir vhosts conf htdocs bin
Erhalten somit folgende Lokale Verzeichnistruktur:
/srv/logs # Webserver und php Lofiles /srv/sockets # PHP Fastcgi sockets /srv/temp # Verzeichnis für temporäre Dateien, Uploads und Sessiondaten
Sowie gesyncte Verzeichnisse
/srv/www/vhosts # Hier werden alle vhosts eingetragen /srv/www/conf # Ablage alle Konfigurationen wie logrotate, webalizer, vhosts und php /srv/www/bin # Startscripte für phpfastcgi /srv/www/htdocs # Standardseiten wie z.B. webmail, phpmyadmin etc.
Aufbau Webserver
Nachdem die Verzeichnisse angelegt wurden bzw. existieren, geht es an den Webserver. Ich erweitere die die Konfiguration des Lighttpd um 2 Punkte (Für den Einsatz des Apachen muss die Konfiguration sinngemäss angepasst werden)
- Sofern wir kein IPv6 nutzen kann dies abgeschaltet werden.
server.use-ipv6 = "enable"
- Server soll nicht von extern erreichbar sein.
server.bind = "10.10.0.12"
- Die IP Adresse für die 2. Node bitte anpassen!
var.log_root = "/var/log/lighttpd/" var.server_root = "/srv/www/" var.state_dir = "/var/run/" var.home_dir = "/var/lib/lighttpd/" var.cache_dir = home_dir + "cache/compress/" var.vhosts_dir = server_root + "vhosts/"
- Ganz am Ende anfügen:
include_shell "cat /srv/conf/vhosts/*.conf"
- Sofern wie z.B. Plattformübergreifend standard Subdomains verwenden wollen
- können wir dies mit folgender Zeite und nachfolgender Konfiguration erreichen;
- z.B. mailer.example.com oder mysqladmin.example.com
include_shell "cat /srv/conf/default-subdomains.conf"
Exemplarischer Inhalt der /srv/conf/default-subdomains.conf für eine default subdomain (mysqladmin) für alle Domains Bitte nicht vergessen, mod_fastcgi einbinden!
$HTTP["host"] =~ "^mysqladmin\.([^.]+\.[^.]*)$" {
var.server_name = "mysqladmin"
server.name = server_name
server.document-root = "/htdocs//phpmyadmin/"
index-file.names = ("index.xhtml", "index.html", "index.htm", "default.htm", "index.php")
url.access-deny = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".scgi" )
fastcgi.server = ( ".php" =>
(
( "socket" => "/var/lib/lighttpd/sockets/php-fastcgi-2.socket-1",
"broken-scriptfilename" => "enable"
)
)
)
}
Diese Konfiguration passt ihr bitte nach euren Bedürfnissen an!
Wir erstellen nun eine Domain: example.com Der Webbenutzer lautet: example (wählt bitte bessere und von der Domain abweichende Benutzer.)
Dieser wird mitglied der Gruppe example und nur Mitglied in dieser Gruppe. Der lighttpd wird ebenfalls Mitglied in der Gruppe example, damit dieser die Logfiles schreiben kann und der Anwender diese ggf. abrufen kann. Wichtig, der Login wird disabled also shell /bin/false!
Die Fastcgi Dienste werden über eigene Scripte gestartet. Die Beispielkonfiguration für die Domain sieht wie folgt aus:
/srv/www/conf/vhosts/example.com.conf
# Ein redirect, damit sich google nicht über dublicate content stört und die # Domain sowohl als http://example.com als auch http://www.example.com erreichbar ist $HTTP["host"] == "example.com" { url.redirect = ("^/(.*)$" => "http://www.example.com/$1") } # $HTTP["host"] == "www.example.com" { var.server_name = "example.com" server.name = server_name server.document-root = vhosts_dir + "/example.com/httpdocs" index-file.names = ("index.xhtml", "index.html", "index.htm", "default.htm", "index.php") url.access-deny = ( "~", ".inc" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".scgi" ) accesslog.filename = "/srv/logs/example.com/example.com-access.log" error_log = "/srv/logs/example.com/example.com.err" server.errorfile-prefix = "/srv/logs/example.com/errordocs/e" fastcgi.server = ( ".php" => ( ( "socket" => "/srv/sockets/example/example.socket", "broken-scriptfilename" => "enable" ) ) ) #Sofern wir z.B. per webalizer unsere Webstatistik generieren und bereit stellen wollen: alias.url += ( "/stats" => vhosts_dir + "/example.com/stats" ) #Alternativ noch ein perl Interpreter für eigene statistik Scripte. $HTTP["url"] =~ "^/stats" { cgi.assign = ( ".pl" => "/usr/bin/perl" ) }
}
Natürlich legen wir jetzt noch die Notwendigen Verzeichnisse für unser Webrpojekt an.
mkdir -p /srv/www/vhosts/example.com/httpdocs mkdir -p /srv/www/vhosts/example.com/errordocs mkdir -p /srv/www/conf/php/example/ mkdir -p /srv/logs/example.com mkdir -p /srv/temp/example/session mkdir -p /srv/sockets/example/
und ändern die Berechtigungen entsprechend ab.
chown -R example:example /srv/www/vhosts/example.com chown -R example:example /srv/www/conf/php/example chown -R example:example /srv/www/sockets/example chown -R example:example /srv/temp/example
Danach ein chmod 770 auf die obigen Verzeichnisse, jemand anderer als Gruppenmmitglieder und der Webbenutzer selbst, haben dort nichts zu suchen!
Jetzt fehlt noch der fastcgi PHP Prozess. Hierfür legen wir ein simples shell Script unter: /srv/www/bin/start-fcgi-example.com.sh an. Diese Script kann nur root starten:
#!/bin/sh ## Absoluter Pfad zur spawn-fcgi binary SPAWNFCGI="/usr/bin/spawn-fcgi" ## Absoluter Pfad zur PHP binary FCGIPROGRAM="/srv/www/cgi-bin/php5" ## bind to tcp-port on localhost FCGISOCKET="/srv/sockets/example/example.socket" ## uncomment the PHPRC line, if you want to have an extra php.ini for this user ## store your custom php.ini in /var/www/fastcgi/iza/php.ini ## with an custom php.ini you can improve your security ## just set the open_basedir to the users webfolder ## Example: (add this line in you custom php.ini) ## open_basedir = /var/www/vhosts/iza/html ## PHPRC="/srv/conf/php/example" ## number of PHP childs to spawn in addition to the default. Minimum of 2. ## Actual childs = PHP_FCGI_CHILDREN + 1 PHP_FCGI_CHILDREN=5 ## number of request server by a single php-process until is will be restarted PHP_FCGI_MAX_REQUESTS=300 ## IP adresses where PHP should access server connections from FCGI_WEB_SERVER_ADDRS="127.0.0.1" # allowed environment variables sperated by spaces ALLOWED_ENV="PATH USER" ## if this script is run as root switch to the following user USERID=example GROUPID=example ## no config below this line if test x$PHP_FCGI_CHILDREN = x; then PHP_FCGI_CHILDREN=5 fi export PHP_FCGI_MAX_REQUESTS export FCGI_WEB_SERVER_ADDRS export PHPRC ALLOWED_ENV="$ALLOWED_ENV PHP_FCGI_MAX_REQUESTS FCGI_WEB_SERVER_ADDRS PHPRC" # copy the allowed environment variables E= for i in $ALLOWED_ENV; do E="$E $i=$(eval echo "$$i")" done env - $E $SPAWNFCGI -s $FCGISOCKET -f "$FCGIPROGRAM -c $PHPRC" -u $USERID -g $GROUPID -C $PHP_FCGI_CHILDREN chmod 770 $FCGISOCKET
Damit dieser beim start des Webserver mit gestartet wird, muss das Initscript des Webservers erweitert werden:
In der Startroutine des Server fügen wir folgende Zeile ein:
for i in /srv/bin/start-fcgi*.sh; do $i; done
In der Stoproutine kommt folgendes Script: !Acthung, das stoppen lässt sich sicherlich eleganter lösen. Für meine Zwecke wars ok, weil es keine anderen cgi Dienste mehr gibt.
for i in `ps -ef | grep "/cgi-bin/php5" |grep -v lighttpd | grep -v grep| cut -b 10-14 `; do kill -9 $i; done
Dieses Script räumt alle offenen PHP5 fastcgi Prozesse ab.
Fehlt lediglich noch die Kundenspezifische php.ini
/srv/www/conf/php/example/php.ini
error_log = /srv/logs/example.org/php_errors.log user_dir = /srv/temp/example session.save_path = "/srv/temp/dbaportal/session"
Den Rest der PHP Konfiguration müsst ihr selbst einstellen und hängt stark von euren speziellen Bedürfnissen ab.
Soweit zum Webserver.
FTP
Ich habe mich explizit für vsftp entschieden, einfach zu konfigurieren, sicher und leistungsstark.
die /etc/vsftpd.conf
anonymous_enable=NO chroot_local_user=YES write_enable=yes guest_enable=no guest_username=ftp #hide_ids=YES listen=YES local_enable=YES max_clients=100 max_per_ip=5 nopriv_user=ftp pam_service_name=vsftpd pasv_max_port=65535 pasv_min_port=64000 session_support=NO use_localtime=YES user_config_dir=/etc/vsftp/users userlist_enable=YES userlist_file=/etc/ftpusers virtual_use_local_privs=YES xferlog_enable=YES async_abor_enable=YES connect_from_port_20=YES dirlist_enable=YES download_enable=YES local_umask=0027 listen_address=<deine externe ip>
Inhalt der /etc/ftpusers
adabas amanda at bin cyrus daemon dbmaker db2fenc1 db2inst1 db2as db4web dpbox empress fax firewall fnet games gdm gnats irc informix ingres ixess lnx lp mail man mdom mysql named news nobody nps oracle perforce pop postfix postgres root sapdb skyrix squid uucp virtuoso vscan wnn wwwrun yard zope # End.
Kruzum, in dieser Datei befinden sich alle Benutzernamen, die ftp nicht nutzen dürfen. In Deinem Fall, alle Benutzer aus ggf. /etc/passwd abzüglich example.
Kommunikation Email
Auch das ist im wesentlich sehr einfach. Wir installieren auf dem Webserver postfix und erlauben das relaying.
Im prinzip reicht die /etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name (Postfix)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
readme_directory = no
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc packageor
# information on enabling SSL in the smtp client.
# Entsprechend nach euren vorgaben die Hostnamen anpassen!
myhostname = nodea.de
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = mail.example.com
mydestination = localhost
relayhost = 10.10.0.10
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
# Bei der anderen Node anpassen!
inet_interfaces = 10.10.0.12
## OS "spezifische" Parameter
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix
data_directory = /var/lib/postfix
mail_owner = postfix
mail_spool_directory = /var/mail
mailq_path = /usr/bin/mailq
newaliases_path = /usr/bin/newaliases
program_directory = /usr/lib/postfix
queue_directory = /var/spool/postfix
sendmail_path = /usr/sbin/sendmail
setgid_group = maildrop
Damit der Mailserver auch keine Emails von extern an nimmt die master.conf Zeile 1 anpassen.
10.10.0.12:smtp inet n - n - - smtpd
Soweit zum Webserver.
Nachtrag
Die Fastcgi Prozesse habe ich so definiert, dass je vollständige Domain nur ein Fastcgi Daemon läuft. Dieser gilt auch für die Subdomains. Daher wurde das temp dir, die Sockets auf den Benutzer zusammen geführt. Aus Sicherheitsgründen kann man natürlich jede Anwendung / Subdomain im eigenen Benutzerkontext betreiben. Dies erfordert jedoch eine erhebliche Anpassung der Konfiguration.
Vsftp autentifiziert sich gegen den Server / pam. Eleganter kann man dies über den Datenbankserver lösen. Dann lässt sich ein passwort auch über ein eigen geschaffenes Benutzerinterface ändern. Diese Konfiguration plane ich noch ein.
Die Sessiondaten und Logs liegen lokal mauf dem Server, hier riskiert man den Verlusst der Sessions, wenn der Server ausfällt. Sicherer wäre die Sessions in der Datenbank zu pflegen, allerdings geht dies, wie das belassen der Sessions auf dem gesyncten Storage, zu lasten der Performance. Es gibt aber einen Trick, wie man Transaktionen über den Loadbalancer an eine physische Node bindet. Damit war das Risiko aus meiner sicht reduziert genug. Dazu kommen wir später noch.
Mit dem Einsatz des Postfix zum relayen geht man kein nennenswertes Risiko ein, denn wenn es ein Angreifer schafft einen Host zu missbrauchen, ist es im prinzip egal, ob der Host direkt die Emails versendet oder über den vorgelagerten Mailserver. Es lässt sich mit dieser Lösung aber besser kontrollieren.