logo_header

icon_bubbles Forum

icon_bubbles Wiki

icon_bubbles Planet

RootForum Community » Wiki

Aufbau Webserver

From RootForum Community » Wiki

Jump to:navigation, search

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)

  1. Sofern wir kein IPv6 nutzen kann dies abgeschaltet werden.

server.use-ipv6 = "enable"

  1. Server soll nicht von extern erreichbar sein.

server.bind = "10.10.0.12"

  1. 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/"

  1. Ganz am Ende anfügen:

include_shell "cat /srv/conf/vhosts/*.conf"

  1. Sofern wie z.B. Plattformübergreifend standard Subdomains verwenden wollen
  2. können wir dies mit folgender Zeite und nachfolgender Konfiguration erreichen;
  3. 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.