Configurare Plone dietro Apache e Varnish
Semplice howto per configurare plone 3.3.x dietro Apache 2.2 e Varnish 2.1.x
Premessa
Di seguito verrà utilizzata soltanto una macchina per la gestione del
front end con apache e varnish e del back end con l'istanza di plone,
ma comunque le possibili implementazioni permettono una divisione in
quante macchine si vuole in modo tale da aumentare la scalabilità del
sistema ed anche la sicurezza separando i layer di accesso. Ad esempio
si potrà avere un'architettura basata su quattro macchine dove sulla
prima si installerà apache per la gestione delle connessioni http e
https e relative rewrite rule, sulla seconda si potrà installare Varnish
come http accelerator, sulla terza si potranno installare le componenti
ZEO client e sulla quarta macchina si potrà installare la componente
ZEO Server. Di seguito i dettagli identificativi che verranno utilizzati
in questo howto e che dovranno essere sostituiti con i valori corretti:
- nome host: server01
- ip: 1.1.1.1
- numero porta tcp per il servizio http: 80
- numero porta tcp per il servizio https: 443
- numero porta tcp per il servizio http di varnish: 8080
- numero porta tcp per i servizi di ZEO Client: 8011, 8012
- numero porta tcp per il servizio ZEO Server: 8100
- nome dominio: example.com
Di seguito si presume che la macchina abbia preinstallato plone con i due ZEO Client in ascolto sulle porte 8011 e 8012 e che apache sia configurato per accettare connessioni sia sulla porta 80 che sulla 443, ovvero che siano state configurate le chiavi ssl per la cifratura.
Architettura
Di seguito verrà illustrato come configurare il CMS Plone 3.3.x dietro Apache 2.2 che gestisce le connessioni di front end e Varnish 2.1.x che verrà utilizzato come HTTP accelerator. Di seguito un semplice schema architetturale:

Apache
Bisogna configurare apache in modo che punti verso HTTP Accelerator di Varnish
Definizione delle porte in ascolto
Per prima cosa configurare le porte in ascolto di Apache, su ubuntu il file da editare sarà ports.conf sotto /etc/apache2 ed immettere statement come:
Listen 80
NameVirtualHost 1.1.1.1:80
<IfModule mod_ssl.c>
Listen 443
NameVirtualHost 1.1.1.1:443
</IfModule>
Riscrittura del VirtualHost
A questo punto bisogna riscrivere i due VirtualHost, uno per la connessione non autenticata (porta 80) e l'altro per le sessioni autenticate, che verranno automaticamente redirette su http over ssl (porta 443). Su ubuntu si dovranno editare i file "default" e "default-ssl" sotto /etc/apache2/sites-available.
File default
<VirtualHost 1.1.1.1:80>
ServerAdmin webmaster@example.com
ServerName example.com
ServerAlias www.example.com
ErrorLog /var/log/apache2/error.log
LogLevel warn
CustomLog /var/log/apache2/access.log combined
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteLog /var/log/apache2/rewrite-default.log
#RewriteLogLevel 3
#Redirect su https nel caso sia una sessione autenticata o si
#stia cercando di autenticarsi
RewriteCond %{HTTP_COOKIE} __ac=.[[:alnum:]]+.[;]? [OR]
RewriteCond %{REQUEST_URI} /login_.* [OR]
RewriteCond %{REQUEST_URI} /logged_out$ [OR]
RewriteCond %{REQUEST_URI) /login_success$ [OR]
RewriteCond %{SERVER_NAME} ^secure\.example\.com$
RewriteRule ^(.*) https://%{SERVER_NAME}$1 [R,L]
RewriteCond %{SERVER_NAME} ^example\.com$ [OR]
RewriteCond %{SERVER_NAME} ^www\.example\.com$
RewriteRule ^/(.*) \
http://localhost:8080/VirtualHostBase/http/%{SERVER_NAME}:80/example/VirtualHostRoot/$1 [P,L]
</IfModule>
</VirtualHost>
File default-ssl:
<IfModule mod_ssl.c>
<VirtualHost 1.1.1.1:443>
ServerAdmin webmaster@example.com
ServerName example.com
ServerAlias www.example.com
ErrorLog /var/log/apache2/error.log
LogLevel warn
CustomLog /var/log/apache2/ssl_access.log combined
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteLog /var/log/apache2/rewrite.log
#RewriteLogLevel 3
# se si tenta di accedere alla pagina di login -> ssl
RewriteCond %{SERVER_NAME} example\.com$
RewriteCond %{REQUEST_URI} /login_.* [OR]
RewriteCond %{REQUEST_URI} /logged_out$ [OR]
RewriteCond %{REQUEST_URI} /logged_in
RewriteRule ^/(.*) \
http://localhost:8080/VirtualHostBase/https/%{SERVER_NAME}:443/example/VirtualHostRoot/$1 [P,L]
# se __ac non e' settato allora redirect su http
RewriteCond %{HTTP_COOKIE} __ac=.deleted. [OR]
RewriteCond %{HTTP_COOKIE} !__ac=.*
RewriteRule ^/(.*) http://%{SERVER_NAME}/$1 [R,L]
# in ultimo si proxa su varnish
#RewriteCond %{REMOTE_PORT} 443
RewriteCond %{SERVER_NAME} ^example\.com$ [OR]
RewriteCond %{SERVER_NAME} ^www\.example\.com$
RewriteRule ^/(.*) \
http://localhost:8080/VirtualHostBase/https/%{SERVER_NAME}:443/example/VirtualHostRoot/$1 [P,L]
</IfModule>
...(configurazione dell'ssl da immettere qui)
</VirtualHost>
</IfModule>
Caricamento dei moduli e restart
Una volta configurati i VirtualHost si dovrà verificare che i moduli richiesti siano caricati. Su ubuntu si potranno caricare nel seguente modo:
a2enmod ssl a2enmod proxy_http a2enmod rewrite a2ensite default-ssl
Per verificare che la configurazione sia corretta eseguire il comando
apache2ctl configtest
Ora riavviare il server http
sudo /etc/init.d/apache2 restart
Varnish
Installazione
Per prima cosa bisognerà preparare l'ambiente per poter compilare il codice sorgente creando l'utente varnish negandogli però la possibilità di effettuare login
useradd -d /home/varnish -m -s /bin/false varnish
A questo punto si potrà scaricare il codice sorgente per procedere con la sua compilazione ed installazione. Con un'utenza non privilegiata ma che possa effettuare login collegarsi in console e scaricare il sorgente:
wget http://sourceforge.net/projects/varnish/files/varnish/2.1.3/varnish-2.1.3.tar.gz/download tar -xzvf varnish-2.1.3.tar.gz cd varnish* sh autogen.sh sh configure --prefix=/usr/local/varnish make
Ora con un'utenza privilegiata bisognerà eseguire il comando d'installazione che su ubuntu potrà essere:
sudo make install
Configurazione
Una volta che varnish 2.1 è installato si potrà procedere alla sua configurazione. Di seguito viene presentanto il contenuto del file example.vcl
# definizione dei backend di istanze ZEO Clients
backend b1 {
.host = "localhost";
.port = "8011";
.probe = {
.request =
"GET / HTTP/1.1"
"Host: 127.0.0.1"
"Connection: close";
}
} # end b1
backend b2 {
.host = "localhost";
.port = "8012";
.probe = {
.request =
"GET / HTTP/1.1"
"Host: 127.0.0.1"
"Connection: close";
}
} # end b2
# director
director director_0 random {
.retries = 5;
{
.backend = b1;
.weight = 1;
}
{
.backend = b2;
.weight = 1;
}
} # end director_0
acl purge {
"localhost";
} # end acl purge
### richiamato dai client alla connessione
sub vcl_recv {
# grace time
set req.grace = 300s;
# impostazione del director di default
set req.backend = director_0;
### oggetti da immettere nella cache
# javascript
if (req.request == "GET" && req.url ~ "\.(js)") {
return(lookup);
}
# immagini
if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|img|tga|wmf)$") {
return(lookup);
}
# css + html
if (req.request == "GET" && req.url ~ "\.(css|html)$") {
return(lookup);
}
# multimedia
if (req.request == "GET" && req.url ~ "\.(svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv)$") {
return(lookup);
}
# xml
if (req.request == "GET" && req.url ~ "\.(xml)$") {
return(lookup);
}
### da non immettere nella cache queste regole
if (req.request != "GET" && req.request != "HEAD") {
return(pipe);
}
if (req.http.Authenticate || req.http.Authorization) {
return(pass);
}
### sessioni autenticate
# da verificare il cookie di plone
if (req.http.Cookie && req.http.Cookie ~ "__ac.*") {
return(pipe);
}
### gestione dei purge
if (req.request == "PURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
return(lookup);
}
### gestione dell'encoding
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unkown algorithm
remove req.http.Accept-Encoding;
}
} # end encoding
} # end vcl_recv
### vcl_hit richiamato quando l'oggetto risiede nella cache
sub vcl_hit {
if (req.request == "PURGE") {
set obj.ttl = 0s;
error 200 "Purged.";
}
if (!obj.cacheable) {
return(pass);
}
return(deliver);
} # end vcl_hit
### vcl_miss richiamato quando l'oggetto non viene trovato in cache
sub vcl_miss {
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
} # end vcl_miss
### vcl_fetch richiamato quando l'oggetto viene prelevato dal backend
sub vcl_fetch {
## restart se ricevo codici di errore
if (beresp.status != 200 && beresp.status != 403 && beresp.status
!= 404 && beresp.status != 301 && beresp.status != 302) {
restart;
}
set beresp.ttl = 300s;
set beresp.grace = 300s;
if (beresp.status == 404) {
set beresp.ttl = 0s;
}
if (beresp.status >= 500) {
set beresp.ttl = 0s;
}
if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|img|tga|wmf)$") {
set beresp.ttl = 300s;
}
## css + html
if (req.request == "GET" && req.url ~ "\.(css|html)$") {
set beresp.ttl = 300s;
}
## javascript
if (req.request == "GET" && req.url ~ "\.(js)$") {
set beresp.ttl = 300s;
}
## xml
if (req.request == "GET" && req.url ~ "\.(xml)$") {
set beresp.ttl = 300s;
}
## multimedia
if (req.request == "GET" && req.url ~ "\.(svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv)$") {
set beresp.ttl = 300s;
}
if (!beresp.cacheable) {
set beresp.http.X-Cacheable = "NO:Not-Cacheable";
return(pass);
}
if (beresp.http.Set-Cookie) {
return(pass);
}
if (req.request == "HEAD") {
set beresp.http.head = "yes";
}
set beresp.http.X-Cacheable = "YES";
return(deliver);
} # end vcl_fetch
Nell'esempio sopra esposto è stata scelta come durata della cache un valore di 300 secondi, ma è possibile modificare questo valore con quello che si ritiene più opportuno, come ad esempio 600 secondi equivalenti a 10 minuti.
Start
Non ci resta che provare ad effettuare lo start di Varnish da riga di comando per verificare che tutto sia a posto. Eseguire con una utenza privilegiata il comando:
sudo /usr/local/varnish/sbin/varnishd -f /usr/local/varnish/etc/varnish/example.vcl -P \
/usr/local/varnish/var/varnish/varnish.pid -a 127.0.0.1:8080 \
-s malloc,1G -T 127.0.0.1:2500 -u varnish
dove nello specifico
- con "-f /usr/local/varnish/etc/varnish/example.vcl" si indica il file di configurazione precedentemente preparato;
-
con "-P /usr/local/varnish/var/varnish/varnish.pid" si indica a varnish
di creare il pid file varnish.pid, utile per effettuare lo stop dello
stesso demone;
- con "-a 127.0.0.1:8080" si indica a varnish di porsi in ascolto sulla porta 8080 dell'interfaccia di loopback;
- con "-s malloc,1G" si indica a varnish di effettuare in RAM il caching per un contenuto massimo di 1G;
-
con "-T 127.0.0.1:2500" si indica a varnish di porsi in ascolto sullo
porta 2500 dell'interfaccia di loopback per l'eventuale gestione;
- con "-u varnish" si indica a varnish di utilizzare l'utente varnish per l'esecuzione del demone
Conclusioni
Se è stato fatto tutto bene e se le due istanze ZEO Client sono in ascolto sulle porte 8011 e 8012 il nostro plone sarà configurato come back end alle spalle di apache che gestirà lo switch tra connessioni http e http over ssl a seconda che si navighi su pagine e contenuti pubblici oppure che si effettui la login al nostro sito, dove in questo caso si passerà su connessione cifrata; il tutto sfruttando la capacità di Varnish come HTTP accelerator per effettuare il caching delle risorse senza andare a richiederle ogni volta al nostro plone.
