boum.org a besoin d'argent :
Web sites hosting
Related pages :
Misc TODO
UID and permissions
Every website has its own UID + GID.
PHP
suPHP is used so that PHP scripts run under their website's UID/GID, instead of
www-data. The Apache user (
www-data) then needs to be able to list every directory where PHP scripts are stored, in order to decide, based on their ownership information, under which UID/GID they shall be run.
TODO : the choice to use suPHP is not really made yet, see
WebSiteDiscussion for the state of the art.
noexec ?
Les petits malins qui auraient pu penser que monter /var/www en noexec pouvait vraiment servir à quelque chose face à des méchant-e-s, et ben ils se trompent. Pour exécuter un keutru sur une partition noexec, il suffit de le passer à /bin/ld, et l'exécutable s'exécutera sans problème. Un setup plus mieux pourrait être envisagé avec /var/www en noexec + des applis chrootées dans un répertoire sans 'ld' ou avec un 'ld' modifié pour l'occasion; cela dit, ce second setup fait reposer sa sécurité sur la sécurité de chroot, donc comme d'hab', si quelqu'un-e obtient le compte root dans la chroot, c'est fini.
Apache
Static files (html pages, pictures, etc.) are served by Apache, running as the
www-data user in the web VServer. This user must then be allowed to read any file in
web:/var/www.
Conclusion
A few simple ACLs are enough to ensure the Apache user is allowed to read what it needs to :
- reset the ACL on
/var/www : setfacl -bR /var/www
-
chmod -R go-rwx /var/www
-
chmod 0755 /var/www /var/www/*
- initialize the ACL on existing files :
setfacl -R -m user:www-data:rX /var/www/*/*
- set this as the default for newly created files :
setfacl -R -m d:user:www-data:rX /var/www/*
And thus these permissions are ok :
- directories :
rwx------+ (i.e. 700 + ACL) siteuid sitegid
- files :
rw-------+ (i.e. 600 + ACL) siteuid sitegid
A hourly cronjob takes care that every website's root directory has the correct permissions (especially o-rwx). See /etc/cron.hourly/frangipane-web-fix_www_perms
The only issue comes with the sftp
chmod command : most sftp clients try to preserve file permissions by default, and thus a 600 file gets permissions 600 on the server once uploaded, and no effective read rights for
www-data... because of the mask being removed by
chmod 600. We solve this by applying the
http://sftpfilecontrol.sourceforge.net/ patch to
webmasters VServer's openssh and adding the following directives to
webmasters:/etc/ssh/sshd_config :
SftpUmask 0022
SftpPermitChmod no
SftpPermitChown no
This way, all files uploaded with sftp are given the right permissions.
fixme : but php can create files with custom permissions, thus breaking the ACL, and then the webmaster can't fix these wrong permissions with sftp... a possible solution would be :
- only give www-data access to the sites' directories :
chmod 700 /var/www/*/* ; setfacl -m user:www-data:rX /var/www/*/*
- default permissions for created files inside sites' directories :
setfacl -m d:group:rX /var/www/*/* ; setfacl -m d:other:rX /var/www/*/*
- set
SftpUmask 0022 and SftpPermitChmod yes
- set suPHP umask to 0022
- tell the webmasters to :
- either give 755 permissions to the files they upload
- or to configure their sftp client so that it does not preserve uploaded files permissions, relying on the default umask
Paths and URLs
On construit le répertoire, base_url et le nom de la base MySQL dynamiquement,
histoire de ne pas les stocker en dur ds la base :
- '/var/www/' || domains.name || '/' || dns_records.hostname || '/' || (web_sites.name OU 'root')
- fixme : 'http://' || dns_records.hostname || '/' || domains.name || '/' || web_sites.nom
TODO : en fait, en passant en mode SITE.DOMAINE.TLD, de bons bouts de ce qui a déjà été pensé est à revoir... qui plus est, une arborescence du type
/var/www/TLD/DOMAIN/SITE serait p't'être plus propre
Il ne faut jamais créer de site dont le nom est aussi celui d'un répertoire déjà
existant dans la racine de son domaine. Réciproquement, il ne faut jamais créer
un répertoire dans la racine correspondant au nom d'un site de ce domaine. Pour
résoudre ce problème ennuyeux, on n'a qu'à dire qu'on ne crée des sites!=racine
que sous les domaines pour lesquels
nous nous occupons de la racine (boum.org,
et c'est tout), et que :
- Nous vérifions la première condition dans la méthode de création de site.
- Nous faisons attention de respecter la seconde condition, manuellement.
TODO Les trucs chelous (racine de boum.org, mail, phpMyAdmin, etc.) sont-ils
rangés dans la base (pas interdit, en l'état actuel des choses) ? Dans
l'arborescence std ? De toute façoon, va falloir les faire rentrer ds le moule,
en les traitant en cas particuliers ds les
PerlSections.
TODO : pour l'instant, il est possible de créer des sites en sous-répertoire
d'un FQDN pour lequel aucun site racine n'existe ; le seul cas où ça peut se
présenter, c'est boum.org, et donc la réponse à cette question dépend de la
précédente.
Performance
Configurable setting : Apache
mod_deflate & PHP
zlib.output_compression enabled if, and only if CPU power is more affordable than bandwidth.
The web VServer
This VServer is dedicated to run Apache, PHP and MySQL for hosted websites. Webmasters are not given access to it.
General configuration
-
/etc/adduser.conf is setup with DIR_MODE=0700, so that any home directory is chmod'ed 0700 on site's creation.
Apache
Apache fait générer sa conf par un programme Perl, grâce aux
PerlSections.
https & certificats
Les VirtualHost en https sont générés par les
PerlSections, et n'ont donc rien à faire dans la base.
Les
PerlSections s'occupent d'interroger le FS pour voir s'il y a un certificat au bon nom.
Il est maintenant possible d'obtenir des certificats CAcert correspondant chacun à
plusieurs noms de domaines, sur la même IP :
Virtual hosts configuration
- disable option Indexes
- disable option Multiviews (
-Multiviews)
- AllowOverride :
- autoriser
AuthConfig pour qu'il soit possible par défaut d'utiliser une authentification par .htpasswd
- autoriser
FileInfo pour autoriser les RewriteRule
- forbid everything else, especially :
-
Options : else, any webmaster can set suPHP_ConfigPath by .htaccess
Logs
Apache
Every VirtualHost sends its logs to the host system syslog-ng :
ErrorLog "| /usr/bin/logger -t SITE.DOMAIN.TLD -p local2.info"
CustomLog "| /usr/bin/logger -t SITE.DOMAIN.TLD -p local1.info" noip
Same for the global error log, in
/etc/apache2/conf.d/frangipane :
ErrorLog "| /usr/bin/logger -t apache -p local2.info"
The host system syslog-ng writes the VirtualHosts' logs to
/var/log/frangipane/web/SITE.DOMAIN.TLD/apache/{access,error}.log, and the global error log in
/var/log/vservers/web/apache/.
PHP
Every
php.ini contains the
error_log = syslog stanza, so that all PHP logs are sent to the global syslog-ng daemon, that writes :
- the host system php log to
/var/log/php/error.log
- the hosted web sites' php log to
/var/log/vservers/web/php/error.log.
PHP
Enabling / disabling PHP
TODO : It would be great to enable PHP only on sites that actually need it. This can be done in several ways :
- add a flag to the website table in the DB, used by PerlSections
- disable PHP by default and allow the webmasters to enable it via
.htaccess
Configuration
Par ailleurs, pour assurer la sécurité, même avec suPHP, il faut un fichier
php.ini pour chacun des sites hébergés. Surtout pour que la variable
open_basedir soit identique au
DocumentRoot du site. Comme on ne veut pas générer ces fichiers très peu différents les uns des autres à la main, il faudrait que les
PerlSections les génère à la volée au chargement d'Apache. (rock n'roll!)
- disable
display_errors
- enable
safe_mode
- disable
magic_quotes_gpc, since it breaks some webapps and has been removed anyway from PHP6
- make PHP
session_save_path and upload_tmp_dir site-specific (cf https://wiki.boum.org/Frangipane/WebSiteDiscussion#PHP%20working%20directories). Should we mix it with ACLs?
- Follow guidelines from :
- ini_set(), php_value et php_flag dans les htaccess: Les users peuvent modifier certaines variables php dans leur script grace a ini_set() et leur .htaccess. Ca pourrait pas etre trop grave, certaines des plus sensibles sont protegees (cf http://fr3.php.net/manual/fr/ini.php#ini.list) sauf que y'a session.save_path par ou include_path par exemple qui le sont pas. Le seul moyen d'eviter que nos utilisateurices puissent jouer avec c'est de pas mettre AllowOverride? a Options dans la conf apache (pour la parti .htaccess) et interdire ini_set() dans les php.ini. Le hic c'est que beaucoup de cms utilisent ini_set, genre pour regle la zlib_compression...
suPHP
If we end up using suPHP... :
- suPHP: set
min_uid and min_gid to a reasonable value (10000)
-
SetEnv PHPRC in .htaccess does not work, which is desired, but why ?
Suhosin
The
Suhosin PHP extension, that now replaces the Hardened PHP patch, is now available as a
Debian package. A small patch still has to be applied to the PHP packages to get the "Engine protection" feature, see
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=397179, but all other features already work out of the box on Etch.
TODO : test this with suPHP.
The webmasters VServer
The webmasters are given sftp access to this dedicated VServer, running no service apart of sshd with the sftpfilecontrol patch applied, in order to force an upload umask and to deny chmod ; this is the best way we found to prevent webmasters from messing up the ACLs setup.
libnss-pgsql is used to provide the login, home and password information.
Bind mounts
From the host system, we bind-mount :
-
web:/var/www on webmasters:/var/www (read-write)
- every website log directory (
host:/var/log/frangipane/web/DOMAIN.TLD/SITE) inside this website home directory (webmasters:/home/webmasters/DOMAIN.TLD/SITE/log) (read-only)
Home directories
A site's
$HOME directory is located in
/home/webmasters/DOMAIN.TLD/SITE.
/etc/adduser.conf is setup with DIR_MODE=0700, so that any home directory is chmod'ed 0700 on site's creation. Such a directory contains :
- a
www link to /var/www/DOMAIN.TLD/SITE
- a
log read-only directory
and optionnaly :
- a
backup read-only directory where automated backups could be performed, thus allowing webmasters to download them to a safe place
- a
pics read-write offline directory, which might be used some day to generate a static online pictures gallery
Quotas
Ils sont gérés au niveau FS, mais on les stocke qd même ds la BDD, pour pouvoir en faire de jolis trucs. Les deux sont donc synchronisés :
WebSiteScripts#Quotas.
TODO : comment Apache et ssh/scp gèrent les quotas FS ?
MySQL
One web site = one MySQL database.
- BASE_ID : ((dns_records.hostname || domains.name) s/./_) || ( ("_" || site) OR '' if root)
- MySQL database and user names (since MySQL supports neither usernames longer than 16 chars, nor database names long enough) :
echo -n BASE_ID | sha1sum | head -c 16
Examples :
echo -n boum_org_panoptique | sha1sum | head -c 16
echo -n les-renseignements-genereux_org | sha1sum | head -c 16
The example page
http://boum.org/id_mysql.php allows to compute this online.
DNS
L'enregistrement DNS parent d'un site web est de type A.
Limitations du modèle
franGiPane ne permet pas d'héberger de site correspondant à un CNAME externe (dont les DNS sont gérées ailleurs) et pointant vers nous.