Sikker opsætning af Apache på webserver med flere domæner. Fcgid / Fastcgi / Suexec

Jeg havde en kunde der skulle bruge en relativt simpel opsætning med 3 webhoteller på en server, og pludselig kom der et spørgsmål jeg ikke havde forventet. “Hvad nu hvis en af mine sider bliver hacket, bliver de andre så også automatisk hacket?” og nej det gør de selvfølgelig ikke, det er logik … for en som mig. Men hvis man har brugt discount udbydere der afvikler alle websites med de samme privilegier og har set dem blive hacket et par gange kan jeg selvfølgelig godt forstå at man tror det er tilfældet.

Men det er det heldigvis ikke, en korrekt opsat webserver tilader ikke de individuelle brugere at tilgå hinandens data, så derfor besluttede jeg mig at skrive dette indlæg / denne guide til atsætte apache op.

En kort disclaimer er vel på sin plads: der er ikke nogen enkelt handling der kan gøre noget “sikkert”, ligesom sikkerhed ikke er absolut, alting udvikler sig og der skal laves cost-benefit analyser osv.osv. Men den her metode er ihvertfald et rigtigt godt udgangspunkt.

Kort beskrevet vil jeg sætte en Debian 6.0 op med Apache2, PHP, Fcgi, Suexec, Proftpd, Mysql og Phpmyadmin. Ud ad til vil det for en ordinær webudvikler ligne et ganske almindeligt webhotel, så brugeren behøver ikke tilegne til yderligere evner end dem han i forvejen har. Vi starter ud med en frisk installeret Debian 6.0 med SSH installeret, hvis ikke du selv kan finde ud af at lave den del så bør du finde en anden guide og starte der, det bliver ihvertfald ikke gennemgået her.

Grundlæggende opsætning af Apache med virtualhosts:

Den her del er det de fleste typisk laver når de sætter Apache op, så den går vi hurtige igennem. Jeg vil oprette site1.lab1.mikjaer.com og site2.lab1.mikjaer.com og så et default-webdir til sider der ikke matcher de to ovenstående.

Først installerer jeg apache og vim

root@lab1:~# apt-get install apache2 vim

Herefter bør webserveren køre, det kan bekræftes ved at besøge http://lab1.mikjaer.com nu mangler vi bare at opsætte et par virtuelle hosts, Debians initscripts er opbygget således at det faktisk er dejlig nemt, i /etc/apache2/sites-available ligger en fil der hedder default, den kopierer jeg til en ny fil der hedder site1:

root@lab1:~# cd /etc/apache2/sites-available/
root@lab1:/etc/apache2/sites-available# cp default site1

Derefter åbner jeg filen i min favoriteditor (vim), den ser således ud:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "/usr/lib/cgi-bin">
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

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

        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Først retter jeg i linie 4 så DocumentRoot nu bliver /var/www/site1/htdocs dernæst i linie 9 så der står <Directory /var/www/site1/htdocs> og så retter jeg linie 24 og 30 så der står site1- foran log-filnavnene, så sletter jeg linie 16-22 og tilføjer “ServerName site1.lab1.mikjaer.com” lige efter linie 2, så ser filen således ud:

<VirtualHost *:80>
         ServerAdmin webmaster@localhost
         ServerName site1.lab1.mikjaer.com

         DocumentRoot /var/www/site1/htdocs
         <Directory />
                 Options FollowSymLinks
                 AllowOverride None
         </Directory>
         <Directory /var/www/site1/htdocs>
                 Options Indexes FollowSymLinks MultiViews
                 AllowOverride None
                 Order allow,deny
                 allow from all
         </Directory>

         ErrorLog ${APACHE_LOG_DIR}/site-error.log

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

         CustomLog ${APACHE_LOG_DIR}/site-access.log combined
 </VirtualHost>

Gem filen, og åben derefter /etc/apache2/sites-available/site1 som ser således ud:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "/usr/lib/cgi-bin">
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        </Directory>

        ErrorLog ${APACHE_LOG_DIR}/error.log

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

        CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Tilføj lige efter linie 2 linien “ServerName lab1.mikjaer.com”, gem filen og opret herefter mappen /var/www/site1/htdocs med en midlertidig index.html fil i, aktiver det nye site og genstart apache således:

root@lab1:~# mkdir -p /var/www/site1/htdocs
root@lab1:~# echo Dette er site1 > /var/www/site1/htdocs/index.html
root@lab1:~# a2ensite site1
Enabling site site1.
Run '/etc/init.d/apache2 reload' to activate new configuration!
root@lab1:~# /etc/init.d/apache2 reload
Reloading web server config: apache2.

Nu burde du kunne verificere at opsætningen indtil videre virker ved at gå ind på http://lab1.mikjaer.com og http://site1.lab1.mikjaer.com og konstatere at de to URL’s viser noget forskelligt. (du skal selvfølgelig have sat DNS op selv og sørge for at de subdomæner du har valgt peger på din servers IP Adresse).

Apache vælger VirtualHost efter først-til-mølle princippet, den første dekleration der matcher den url brugeren har skrevet bliver den aktuelle. Dvs hvis du først har *.domæne.dk og sidenhen har test.domæne.dk vil test.domæne.dk altså aldrig blive vist. Hvis ikke nogen matcher vises den første virtualhost, det er derfor at default config filen er prefixet med 000, netop fordi at så loades den altid først. Typisk ønsker du at erstatte den enten med dit primære site, eller en splash-screen der fortæller at siden ikke findes.

Privilege seperation med fcgi og suexec

Vi starter med at opgradere det setup vi har ligenu, vi skal bruge en php fortolker og de to Apache moduler, fcgid og suexec, kommandoen kommer ikke bag på de fleste:

root@lab1:~# apt-get install libapache2-mod-fcgid apache2-suexec-custom php5-cgi

Og så skal de aktiveres:

root@lab1:~# a2enmod fcgid suexec actions
Module fcgid already enabled
Enabling module suexec.
Enabling module actions.
Run '/etc/init.d/apache2 restart' to activate new configuration!

Og fcgid skal konfigureres, opret filen /etc/apache2/conf.d/php5-fcgid.conf og indsæt flg i den:

AddType application/x-httpd-php .php

AddHandler php-fcgi .php
Action php-fcgi /fcgi-bin/php5-fcgi

Alias /fcgi-bin/ /var/www/

<Location /fcgi-bin/>
        SetHandler fcgid-script
        Options +ExecCGI
</Location>

Filen /var/www/php5-fcgi oprettes ligeledes med flg. indhold:

#!/bin/sh
exec /usr/bin/php5-cgi

Den første fil indeholder standard værdier for fcgid og php5-fcgi er den wrapper som bruges til at udføre php scripts med, det er også her det f.eks. er muligt at inkludere en specialtilpasset php.ini fil.

Så oprettes den bruger der skal eje og udføre site1’s filer og php-scripts:

root@lab1:~# adduser site1
Adding user `site1' ...
Adding new group `site1' (1001) ...
Adding new user `site1' (1001) with group `site1' ...
Creating home directory `/home/site1' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for site1
Enter the new value, or press ENTER for the default
  Full Name []:
  Room Number []:
  Work Phone []:
  Home Phone []:
  Other []:
Is the information correct? [Y/n] y

Brugeren skal have fjernet muligheden for at logge ind, dette gøres ved at sætte dens shell til /sbin/nologin og homedir skal sættes til /var/www/site1, fcgid wrapperen skal flyttes ind i brugerens homedir og til sidst skal brugeren tildeles rettighederne over /var/www/site1 som nu er dens homedir:

root@lab1:~# usermod site1 -s /sbin/nologin
root@lab1:~# usermod site1 -d /var/www/site1
root@lab1:~# cp /var/www/php5-fcgi /var/www/site1/
root@lab1:~# chown -R site1:site1 /var/www/site1/
root@lab1:~# chmod 755 /var/www/php5-fcgi

Til sidst skal vi lige tilføje et par linier i /etc/apache2/sites-available/site1 for at aktivere suexec pluginet for site1’s virtualhost, det drejer sig om linie 17 og linie 18:

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        ServerName site1.lab1.mikjaer.com

        DocumentRoot /var/www/site1/htdocs
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        </Directory>
        <Directory /var/www/site1/htdocs>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride None
                Order allow,deny
                allow from all
        </Directory>

        Alias /fcgi-bin/ /var/www/site1/
        SuexecUserGroup site1 site1

        ErrorLog ${APACHE_LOG_DIR}/site-error.log

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

        CustomLog ${APACHE_LOG_DIR}/site-access.log combined
</VirtualHost>

Til sidst for at teste slette vi den gamle index.html …

root@lab1:~# rm /var/www/site1/htdocs/index.html

… og opretter en index.php i /var/www/site1/htdocs/index.php

<? system("id"); ?>

Genstart apache:

root@lab1:~# /etc/init.d/apache2 restart
Reloading web server config: apache2.

og naviger ind på http://site1.lab1.mikjaer.com/, resultatet skulle gerne se nogenlunde sådan her ud:

uid=1001(site1) gid=1001(site1) groups=1001(site1)

Hvis det ikke er tilfældet kan det hjælpe at kigge i /var/log/apache2/suexec.log som fortæller dig hvad der går galt, typisk handler det om forkerte rettigheder.

Nu kører dine php scrips med en specifik Unix bruger og du kan tildele en unik unix bruger pr. vhost, så har du faktisk klaret selve seperationen. Nu mangler du kun lidt brugervenligshed …

Opsæt ProFtpd, Mysql og Phpmyadmin
Igen, vi kører debian så no big deal her:

root@lab1:~# mysql -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 51
Server version: 5.1.61-0+squeeze1 (Debian)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database site1;
Query OK, 1 row affected (0.00 sec)

mysql> grant all privileges on site1.* to "site1"@localhost identified by "site1kode";
Query OK, 0 rows affected (0.00 sec)

mysql> quit
Bye

Du kan teste database adgangen med:

root@lab1:~# mysql -usite1 -psite1kode site1
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 52
Server version: 5.1.61-0+squeeze1 (Debian)

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| site1              |
+--------------------+
2 rows in set (0.00 sec)

mysql> quit
Bye

Til sidst mangler vi at sætte ftp op, først tildeler vi brugeren en kode:

root@lab1:~# passwd site1
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

Og verificerer at koden IKKE virker til SSH Login:

root@lab1:~# ssh site1@localhost
The authenticity of host 'localhost (127.0.0.1)' can't be established.
RSA key fingerprint is c9:74:e4:8d:20:22:3d:94:96:41:b8:62:c7:37:2d:43.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
site1@localhost's password:
Permission denied, please try again.

For at tillade ftp login redigerer vi i /etc/proftpd/proftpd.conf, og fjerner udkommenteringen (#-mærket) ved linie 37 så der står:

#
# /etc/proftpd/proftpd.conf -- This is a basic ProFTPD configuration file.
# To really apply changes reload proftpd after modifications.
# 

# Includes DSO modules
Include /etc/proftpd/modules.conf

# Set off to disable IPv6 support which is annoying on IPv4 only boxes.
UseIPv6                         on
# If set on you can experience a longer connection delay in many cases.
IdentLookups                    off 

ServerName                      "Debian"
ServerType                      standalone
DeferWelcome                    off 

MultilineRFC2228                on
DefaultServer                   on
ShowSymlinks                    on  

TimeoutNoTransfer               600
TimeoutStalled                  600
TimeoutIdle                     1200

DisplayLogin                    welcome.msg
DisplayChdir                    .message true
ListOptions                     "-l"

DenyFilter                      \*.*/

# Use this to jail all users in their homes
# DefaultRoot                   ~   

# Users require a valid shell listed in /etc/shells to login.
# Use this directive to release that constrain.
RequireValidShell             off 

# Port 21 is the standard FTP port.
Port                            21

(bemærk, kun en del af filen er vist her)

Genstart proftp og test login via ftp:

root@lab1:~# /etc/init.d/proftpd restart
Stopping ftp server: proftpd.
Starting ftp server: proftpd.
root@lab1:~# ftp localhost
Connected to localhost.
220 ProFTPD 1.3.3a Server (Debian) [::ffff:127.0.0.1]
Name (localhost:root): site1
331 Password required for site1
Password:
230 User site1 logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful
150 Opening ASCII mode data connection for file list
drwxr-xr-x   2 site1    site1        4096 May  7 16:44 htdocs
-rwxr-xr-x   1 site1    site1          33 May  7 16:35 php5-fcgi
226 Transfer complete
ftp>

Til sidst kan du teste at phpmyadmin kører ved at gå ind på http://site1.lab1.mikjaer.com/phpmyadmin/ , standard indstillingen i Debian er at phpmyadmin kan tilgåes på alle domæner ved at skrive /phpmyadmin bagefter.

Jeg håber du kan bruge guiden, jeg ved at jeg tager 5-7.000kr for at lave sådan en opsætning, så hvis du kan selv har du da ihvertfald sparet det 🙂

Har du nogen spørgsmål eller noget du gerne vil have uddybet? Så skriv en kommetar 🙂

Dette indlæg blev udgivet i Apache, Knowledge Base, Old Base. Bogmærk permalinket.

Skriv et svar