|
Apache è un potente WEB server multipiattaforma.
Le statistiche dicono che è il più utilizzato al mondo,
e tutto lascia prevedere che lo sarà ancora per qualche anno.
Apache è disponibile nel sito ufficiale www.apache.org,
insieme ai sorgenti e a tutta la documentazione. Tutte le distribuzioni
di GNU/Linux lo includono come pacchetto, tipicamente già
compilato e configurato: è sufficiente una installazione
base del sistema operativo, ed anche l'utilizzatore più sprovveduto
si ritrova con un WEB server installato (magari a sua insaputa...).
Le cose vanno diversamente quando si desidera configurare Apache
per uso di produzione: diventa allora difficile orientarsi tra la
miriade delle sue opzioni di configurazione, e la documentazione
ufficiale non aiuta il neofita. In particolare in questo articolo
vogliamo configurare Apache per supportare la multiutenza e raggiungere
i seguenti obiettivi: gestire vari domini WEB; isolare i programmi
CGI dei vari siti tra di loro; dotare gli utenti ciascuno della
propria home page.
Queste note sono il frutto di alcuni mesi di lavoro passati a spulciare
nella documentazione del programma e a fare prove su varie distribuzioni
GNU/Linux, principalmente Red Hat 6.2 e Red Hat 7.1, che includono
la versione 1.3 di Apache. I suggerimenti e le soluzioni che descrivo
si applicano ovviamente anche ad altre distribuzioni, che potranno
differire al più per la scelta dei path di installazione
e per il diverso packaging dei vari componenti (modulo base, moduli
supplementari, ecc.).
Non ho certo intenzione di scrivere un tutorial per Apache o un
ennesimo manuale. Si tratta semplicemente della descrizione di esperienze
che possono interessare gli iniziati che, una volta eseguita con
successo l'installazione base di Apache, desiderano approfondirne
alcune funzionalità avanzate.
Non ci inoltreremo qui nelle procedure di ricompilazione del sorgente
del programma, nè faremo altre cose più tediose e
"difficili": ci limiteremo a modificare il file di configurazione
di Apache ed eventualmente interverremo su qualche altro file di
sistema usando pratiche consuete di configurazione. Riporremo la
nostra fiducia nel rivenditore della distribuzione, che di solito
ha cura di rendere disponibile sul suo sito WEB l'ultima versione
di Apache corretta rispetto ai bug e ai problemi di sicurezza. Non
ci interessa invece installare l'ultimissima versione, poiché
ci concentreremo sugli aspetti generali della gestione del server.
Ultima annotazione prima di cominciare: teoricamente il system
administrator e il WEB administrator sono due ruoli distinti. In
realtà l'intreccio di competenze è tale che le due
figure finiscono per coincidere. E' molto probabile che la configurazione
del WEB server coinvolga parecchi settaggi del sistema, della posta
elettronica, del DBMS, dei permessi e degli utenti, sicchè
separare le due figure diventa davvero difficile. Perciò
qui userò i due termini indifferentemente.
Per cominciare
Assicuriamoci innanzitutto che Apache sia installato ed in esecuzione.
E' interessante nonché divertente per il sistemista conoscere
alcuni modi alternativi per fare ciò:
1. Ordine e disciplina - Seguire la via maestra
GNU/Linux segue le convenzioni System V per l'inizializzazione del
sistema. Quindi dovrebbe essere disponibile un piccolo script di
shell che si incarica di avviare, fermare e verificare lo stato
del server. Lo script esegue una di queste funzioni a seconda che
sia stato invocato con il parametro start, stop oppure status; esiste
poi anche il parametro restart che ha l'ovvio significato. Sulla
distribuzione Red Hat 6.2 il comando è
# /etc/rc.d/init.d/httpd status
httpd (pid 9702 9701 9698) is running...
mentre per la Red Hat 7.1 il path differisce leggermente:
# /etc/init.d/httpd status
httpd (pid 9702 9701 9698) is running...
L'output conseguente dovrebbe confortarci sul fatto che Apache
sia regolarmente in esecuzione. Il messaggio esatto dipende da come
è stato scritto questo script, che invito ad andare a vedere.
2. Il Riduzionista - In Unix è tutta una questione di file
e di processi
Sappiamo che Unix, GNU/Linux e gli altri sistemi operativi affini
sono concepiti con la filosofia del meccano: pochi strumenti, ma
buoni. File e processi sono il fondamento del sistema, il resto
è il sale e il pepe. Il sistemista-riduzionista non si fida
degli script automatici, ma vuole andare all'origine delle cose,
e va a guardare direttamente i processi in esecuzione:
# ps ax | grep httpd
9698 ? S 0:00 /usr/sbin/httpd parametri vari
9701 ? S 0:00 /usr/sbin/httpd parametri vari
9702 ? S 0:00 /usr/sbin/httpd parametri vari
9706 pts/2 S 0:00 grep httpd
Il sistemista-riduzionista è previdente: sa che sulla sua
macchina oberata di lavoro sono in esecuzione centinaia di processi,
per cui inserisce un sano grep che seleziona solo le righe che contengono
la stringa httpd. Guardacaso, queste righe sono proprio quelle relative
ai processi di Apache.
3. L'hacker che c'è in noi - Emerge il lato oscuro
Ragioniamo: se un server di rete fornisce servizi in rete, il modo
migliore per verificarne il funzionamento è provare direttamente
questi servizi. Apriamo allora il nostro browser WEB preferito e
puntiamolo sull'URL http://localhost. Il bravo sistemista/hacker
con tendenze riduzioniste usa Telnet come browser WEB:
# telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
GET / HTTP/1.0 qui premi ENTER due volte
HTTP/1.1 200 OK
Date: Mon, 10 Dec 2001 21:50:38 GMT
Server: Apache/1.3.19 (Unix) (Red-Hat/Linux)
Last-Modified: Sat, 11 Aug 2001 19:58:38 GMT
ETag: "49183-20e-3b758e6e"
Accept-Ranges: bytes
Content-Length: 526
Connection: close
Content-Type: text/html
<HTML>
la pagina HTML che non ci interessa
</HTML>
Connection closed by foreign host.
#
Come al solito, solo il comando evidenziato in grassetto deve essere
impartito a tastiera, il resto sono le risposte del sistema. Notiamo
che il comando GET deve essere scritto proprio in lettere maiuscole,
e che occorre premere due volte il tasto ENTER prima che il server
risponda: la riga vuota è il modo in cui il client (cioè
il nostro Telnet) spiega al server che ha concluso la sua domanda.
Infatti, vedremo che il client ha la possibilità di specificare
vari altri parametri su altrettante righe.
Come è facile immaginare, il comando GET serve per richiedere
un documento; a seguire bisogna specificare la parte di path file
dell'URL del documento, essendo per convenzione / la richiesta della
home page, o pagina default, o pagina indice del sito. Con lungimiranza,
il protocollo HTTP richiede anche di specificare esplicitamente
il protocollo e la versione.
Trucco
Se il terminale che usi non supporta lo scrolling, sarà difficile
riuscire a leggere le prime righe, che sono le più interessanti.
Rimedi possibili: usare script; richiedere una pagina deliberatamente
errata al posto di /, ad esempio /xxx; infine, usare un comando
come questo:
# (echo -ne "GET / HTTP/1.0\n\n"; sleep 2) \
> | telnet localhost 80 | head -20
In questo modo appariranno solo le prime 20 righe del dialogo con
il server, indipendentemente dalla effettiva lunghezza della risposta.
Usando lo stesso principio si può andare ad esplorare in
giro i vari server, e verificare di persona quali sono i server
più usati: interessante, vero?
A questo punto dovremmo avere la certezza che l'installazione di
base di Apache è a posto. Per ora Apache ritorna la pagina
di default predefinita dalla distribuzione, ma questo ci va bene
perché stiamo per cambiare un po' tutto...
Host virtuali basati sull'indirizzo IP
Di base il server Apache risponde ad un unico indirizzo IP e ad
un unico dominio, cioè gestisce un solo sito WEB del tipo
www.azienda.it o una cosa del genere. Gli ISP hanno invece la necessità
di gestire parecchi domini. Le aziende più avanzate informaticamente
sul fronte Internet possono voler creare siti WEB distinti per le
diverse aree funzionali, come support.azienda.it, oppure desiderano
ospitare siti WEB delle ditte affiliate. Con il protocollo HTTP
versione 1.0 era necessario dotare ogni sito WEB di un indirizzo
IP distinto, anche se poi questo indirizzo si riferiva alla stessa
macchina fisica. In questo consiste il meccanismo di host virtuale
basato sull'indirizzo IP.
Le cose funzionavano (e possono funzionare tuttora) così:
se un navigatore si collegava all'URL www.azienda.it, il browser
risolveva il nome in un certo numero IP 11.22.33.44, ed è
a questo server che il browser inviava la richiesta; quando il navigatore
si collegava a support.azienda.it, il browser risolveva il nome
in un indirizzo IP diverso 55.66.77.88 al quale rispondeva magari
la stessa macchina, ma lì il server poteva discriminare il
sito WEB richiesto in base all'indirizzo IP.
Host virtuali basati sul nome
Per ridurre lo spreco di preziosi indirizzi IP e tutte le inevitabili
complicazioni tecniche, burocratiche e di costi, il protocollo HTTP
versione 1.1 ha aggiunto la funzionalità di host virtuale
basato sul nome. Il meccanismo è abbastanza semplice: il
browser segue la stessa trafila di prima, ma nella richiesta della
pagina specifica anche per esteso il nome del dominio richiesto.
Il server WEB, anche se dotato di un solo indirizzo IP, ha così
modo di riconoscere quale sito WEB tra quelli ospitati è
stato effettivamente richiesto. In questo consiste il meccanismo
di host virtuale basato sul nome. E' ovvio che questo sistema richiede
la collaborazione del browser per funzionare. Comunque, tutti i
browser moderni supportano HTTP 1.1, tanto che il meccanismo degli
host virtuali è ampiamente utilizzato. Forse tutto questo
discorso suona un po' ingarbugliato, ma vedremo subito degli esempi
molto concreti che dovrebbero chiarire bene come vanno le cose.
Cominciamo la configurazione
Dunque, Apache include il supporto per gli host virtuali basati
sul nome di dominio, previsti dal protocollo HTTP versione 1.1.
Questo ci permette di ospitare sulla nostra macchina un numero arbitrario
di siti WEB distinti utilizzando sempre un solo indirizzo IP. Le
specifiche tecniche dell'implementazione che andremo a fare sono
le seguenti:
The "Casa" ISP Specs
IP Address: 127.0.0.1
WEB Sites: Domain DocumentRoot
localhost.localdomain /home/httpd/html
www.tizio.casa /home/tizio/public_html
www.caio.casa /home/caio/public_html
L'indirizzo IP non è scelto a caso: si tratta dell'indirizzo
di loop-back previsto per scopi di test e disponibile su ogni macchina.
Altro aspetto positivo di questa scelta: non servono schede di rete
e non serve essere connessi ad Internet.
PASSO 1: Dichiarare i nuovi domini
Per quanto riguarda i domini gestiti, sono ovviamente nomi di fantasia
che possiamo inserire nell'elenco degli host name del file /etc/hosts,
che andiamo subito a modificare in modo che appaia così:
127.0.0.1 localhost.localdomain localhost
127.0.0.1 www.tizio.casa
127.0.0.1 www.caio.casa
In genere la prima riga è già predisposta, mentre
le altre due sono quelle appena aggiunte. A questo punto i processi
che avvieremo saranno in grado di risolvere i nuovi nomi di dominio.
PASSO 2: predisporre gli spazi WEB
La strategia che seguiremo comporta la creazione di un normale account
per ciascun dominio gestito. Vogliamo insomma che ogni dominio sia
gestibile da un utente distinto, che vi può accedere in Telnet
o FTP come di consueto. Nella home directory, ciascun utente sarà
libero di gestire il suo sito in totale libertà.
Per creare l'utente Tizio facciamo questo:
# useradd tizio -g users
# passwd tizio
Changing password for user tizio
New UNIX password: ******
Retype new UNIX password: ******
passwd: all authentication tokens updated successfully
# chmod u=rwx,g=,o=x /home/tizio
# mkdir /home/tizio/public_html
# chown tizio:users /home/tizio/public_html
# chmod u=rwx,g=,o=x /home/tizio/public_html
Per i più pigri, spiego a parole quello che abbiamo fatto:
il nuovo utente possiede una normale home directory posta in /home/tizio;
dentro a questa directory questo signore potrà lavorare normalmente
come un qualsiasi utente. In più abbiamo creato una sotto-directory
di nome public_html che conterrà la parte pubblica del sito
WEB del sig. Tizio. In altri termini, andremo a configurare Apache
in modo che gli URL del tipo http://www.tizio.casa/x vengano tradotti
nel pathfile /home/tizio/public_html/x.
Ci sono però due aspetti molto importanti da considerare:
Abbiamo assegnato esplicitamente il nuovo utente al gruppo users:
questo devia dallo schema generale della distribuzione Red Hat,
che per default crea un nuovo gruppo per ogni utente.
Abbiamo ampliato i diritti di accesso alla home directory dell'utente
con o+x per consentire ad Apache di attraversarla per raggiungere
le pagine WEB dentro alla subdirectory public_html. I permessi assegnati
sono:
Permessi di /home/tizio
UID:
tizio GID:
users others:
r w x - - - - - x
Conviene spendere due parole sul significato di quanto fatto riguardo
ai permessi di accesso, perché l'argomento è un po'
delicato, essendo coinvolti i problemi di sicurezza e riservatezza.
Fintanto che il server WEB è al servizio di un'unica azienda,
la separazione degli spazi di lavoro semplifica la gestione e riduce
le interferenze. Tuttavia se il server deve essere reso disponibile
ad altri soggetti sui quali non si ha pieno controllo, allora le
cose vanno studiate più attentamente.
Spieghiamo innanzitutto cosa intendiamo con il termine identità
di un processo. Ogni processo ha varie caratteristiche: ad esempio
una è il PID (process identifier), il numero univoco che
identifica il processo nel sistema. Altre due caratteristiche molto
importanti sono l'UID e il GID del processo, cioè il numero
dell'utente e il numero del gruppo che contraddistinguono il processo.
Sono questi valori che il kernel esamina prima di dare il permesso
a un processo di accedere ad una risorsa, come un file o un device.
Richiamiamo brevemente i criteri seguiti dal kernel per decidere
se autorizzare o meno una certa operazione richiesta da un processo
su di un file:
L'UID del processo è uguale all'UID del file.
In questo caso si applicano i tre flag dei permessi di utente del
file. Se la combinazione dei flag non permette di eseguire l'operazione
richiesta dal processo (lettura, scrittura, esecuzione), l'operazione
viene negata indipendentemente da tutti gli altri flag di gruppo
e others.
Gli UID del processo e del file differiscono, ma i GID sono uguali.
In tal caso si applicano i tre flag dei permessi di gruppo del file.
Anche qui, se la combinazione dei flag non permette di eseguire
l'operazione richiesta dal processo, l'operazione viene negata indipendentemente
da tutti gli altri flag di others.
L'UID e il GID del processo non corrispondono a quelli del file.
In tal caso si applicano i tre flag dei permessi di others.
Ricordo che Apache gira con l'identità UID:GID = nobody:nobody
su Red Hat 6.2, e con l'identità apache:apache su Red Hat
7.1. Questi parametri vengono impostati nella configurazione predefinita
con le direttive User e Group nel file di configurazione httpd.conf.
La conseguenza di tutto questo discorso è che Apache potrà
entrare nelle directory degli utenti Tizio e Caio grazie al permesso
o+x; inoltre Apache potrà leggere tutti e soli i file accessibili
ad "other". Viceversa, gli altri utenti del sistema, i
cui processi operano con GID = users, si scontrano con il secondo
criterio di validazione, e non possono entrare nella directory.
ATTENZIONE!
Il modello di sicurezza per i permessi dei file e delle directory
non è l'unico possibile, ma forse è il più
semplice da applicare e da gestire. Lascia tuttavia qualche perplessità
il dover rendere accessibile a "other" la propria home
directory.
Esiste almeno un'altra soluzione che si basa sulla creazione di
un gruppo per ciascun utente, paradigma scelto per default dalla
distribuzione Red Hat. Le home directory degli utenti vanno poi
rese attraversabili dal proprio gruppo. L'utente del server (nobody
oppure apache) deve poi essere inserito nel gruppo di ciascun utente.
Questa, in estrema sintesi, la ricetta alternativa.
PASSO 3: Configuriamo Apache
E finalmente arriviamo alla configurazione di Apache. Il file di
configurazione è httpd.conf, che nella distribuzione Red
Hat 6.2 e 7.1 si trova in /etc/httpd/conf/httpd.conf. Raccomando
a questo punto di fare una bella copia del file prima che ci mettiamo
su le manacce: è utile avere come riferimento un file di
configurazione che sappiamo essere funzionante.
Al termine del file httpd.conf c'è la sezione destinata
agli host virtuali. Assicuriamoci che sia presente la direttiva
NameVirtualHost 127.0.0.1:80
che attiva la funzionalità degli host virtuali basati sul
nome sulla solita porta 80 del nostro server. Creiamo il virtual
host per il dominio www.tizio.casa:
<VirtualHost 127.0.0.1:80>
ServerName www.tizio.casa
DocumentRoot /home/tizio/public_html
ServerAdmin tizio@localhost
CustomLog logs/www.tizio.casa-access_log common
ErrorLog logs/www.tizio.casa-error_log
</VirtualHost>
Per gli altri domini virtuali creiamo altrettanti utenti e altre
sezioni VirtualHost similari, facendo attenzione a modificare di
conseguenza il dominio, il path e l'indirizzo di posta, evidenziati
in grassetto nell'esempio. Come ServerAdmin ho indicato in realtà
l'utente tizio invece che il vero WEB administrator: di solito i
problemi legati a un sito WEB sono locali a quel sito (pagine mancanti,
CGI non funzionanti, ecc.) piuttosto che responsabilità del
WEB administrator.
PASSO 4: Creiamo le home page
Questa è la parte facile e rilassante: # su - tizio
$ echo "<html><body><h1>Benvenuti in www.tizio.casa</body></html>"
> public_html/index.html
$ [CTRL-D]
# su - caio
$ echo "<html><body><h1>Benvenuti in www.caio.casa</body></html>"
> public_html/index.html
$ [CTRL-D]
...e così via per gli altri dominii.
PASSO 5: Verifica degli host virtuali
Non ci resta che riavviare Apache. Il comando da dare su Red Hat
6.2 è
# /etc/rc.d/init.d/httpd restart
mentre su Red Hat 7.1 è
# /etc/init.d/httpd restart
Se qualcosa è andato storto, c'è sempre il log file
in /var/log/httpd. Mentre si fanno queste prove consiglio di tenere
sempre aperta una finestra dedicata alla visualizzazione dei messaggi
di log tramite il comando tail -f /var/log/httpd/*.
Se è tutto ok, puntiamo il WEB browser sull'URL http://www.tizio.casa
e poi su http://www.caio.casa. Ecco quello che io ho ottenuto:
$ (echo -ne "GET / HTTP/1.1\nHost: www.tizio.casa\n\n"
> sleep 2) | telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
HTTP/1.1 200 OK
Date: Wed, 11 Dec 2001 21:33:20 GMT
Server: Apache/1.3.19 (Unix) (Red-Hat/Linux)
Last-Modified: Mon, 10 Dec 2001 07:55:23 GMT
ETag: "fa7b-43-3c146a6b"
Accept-Ranges: bytes
Content-Length: 67
Content-Type: text/html
<html><body>Benvenuto in www.tizio.casa</body></html>
Connection closed by foreign host.
Esperimenti interessanti
Prova questo comando:
# (echo -ne "GET / HTTP/1.0\n\n"; sleep 2) \
> | telnet localhost 80 | head -20
esso dovrebbe ritornare il primo virtual host (che al momento dovrebbe
essere quello del sig. Tizio). Possiamo provare anche ad aggiungere
il virtual host localhost.localdomain come primo virtual host e
ripetere la prova.
Adesso sfruttiamo il protocollo HTTP v. 1.1 per ritornare gli altri
virtual host. Per il sig. Caio si tratta di aggiungere la riga Host:
www.caio.casa nella nostra interrogazione:
# (echo -ne "GET / HTTP/1.0\nHost: www.caio.casa\n\n";
sleep 2) \
> | telnet localhost 80 | head -20
Le stesse prove si possono fare anche da un browser WEB "normale",
ma si perde tutto il gusto...
Con questo abbiamo concluso il nostro tour sul meccanismo dei domini
virtuali. In una situazione reale avremo che i domini www.tizio.casa
e www.caio.casa saranno registrati su un qualche DNS (e non nel
nostro file /etc/hosts), e l'indirizzo IP da assegnare ai VirtualHost
sarà l'indirizzo pubblico assegnato alla nostra macchina
(e non l'indirizzo di loopback 127.0.0.1).
WEB dinamico e programmi CGI
Il WEB sarebbe ben poca cosa senza la possibilità di generare
dinamicamente le pagine richieste e senza quella di interagire con
il client remoto. Il protocollo HTTP da una parte, e le tecnologie
CGI e SSI di Apache dall'altra sono gli strumenti che forniscono
queste possibilità. Apache viene corredato anche con vari
interpreti incorporati, come ad esempio PERL, che permettono una
esecuzione molto efficiente di codice di scripting incorporato all'interno
delle pagine WEB.
Spendiamo due parole per chiarire cos'è un programma CGI:
si tratta appunto di un programma, realizzabile con un qualsiasi
linguaggio di programmazione o di scripting, che può essere
invocato come una pagina WEB o come gestore di un form HTML; il
WEB server, anziché ritornare al client il file del programma,
lo esegue. L'output generato da questo programma è poi quello
che il server restituirà al client, sia esso una pagina HTML,
un'immagine GIF o qualsiasi altro tipo di informazione. Le specifiche
dell'interfaccia CGI dicono anche come il programma CGI possa ricevere
input dal client, chiudendo quindi il cerchio. Rimandiamo al prossimo
articolo la discussione dettagliata della tecnologia CGI, delle
possibilità che essa offre, e dei possibili utilizzi pratici.
Tuttavia, nuove possibilità significano nuovi problemi per
l'amministratore del sistema. Cerchiamo di capire meglio il problema
introducendo l'importantissima questione della identità dei
processi CGI.
Abbiamo detto che il server Apache è un processo avviato
con l'identità UID:GID = nobody:nobody nella distribuzione
Linux Red Hat 6.2, oppure con l'identità UID:GID = apache:apache
nella distribuzione Linux Red Hat 7.1. Con altre distribuzioni di
GNU/Linux questa identità può cambiare, ma resta il
fatto che Apache funziona con questa unica identità. L'identità
del processo impone anche i limiti per quanto riguarda l'accesso
alle risorse del sistema:
attraversamento di directory;
elencazione del contenuto delle directory;
creazione e cancellazione di file in una data dir.;
lettura/scrittura/esecuzione dei file;
accesso a un DBMS;
accesso alle altre risorse del sistema.
I programmi CGI avviati da Apache ereditano l'identità di
processo di quest'ultimo anche quando appartengono ad utenti diversi.
Lo stesso discorso vale per gli interpreti incorporati. Quindi,
ad esempio, le funzioni per gestire i file richiedono di impostare
opportunamente i permessi e la proprietà della directory
dove il file risiede, oltre che i permessi e la proprietà
del file stesso. Non è possibile creare file in directory
non accessibili al server, né è possibile leggere
file non leggibili per il server.
Supponiamo ora che i programmi CGI prodotti dal sig. Tizio e dal
sig. Caio abbiano necessità di svolgere varie funzioni: esattamente
come qualsiasi altro programma, avranno bisogno di leggere e scrivere
file, accedere a un DB e altre cose del genere. Purtroppo, siccome
essi verranno eseguiti con la stessa identità, saremo costretti
ad assegnare diritti di accesso in comune a tutti i CGI. Per essere
più espliciti: a livello di CGI gli utenti Tizio e Caio potranno
pasticciare liberamente sugli stessi file. Il sig. Tizio non potrà
leggere o scrivere un suo file riservato senza che questo non sia
leggibile anche al sig. Caio. Il Sig. Tizio vorrà accedere
a un DBMS riservato specificando una password, ma se usa un linguaggio
di scripting il suo programma deve necessariamente essere leggibile
ai programmi CGI degli altri utenti, che potranno così carpirgli
facilmente la password. Il sig. Tizio non potrà leggere o
scrivere file nella propria home directory (al di fuori di public_html)
senza dover pericolosamente rilassare i diritti di accesso al suo
account. In altri termini, abbiamo un sistema multiutente che, a
livello del server WEB, diventa monoutente...
Pensiamo invece a quanto sarebbe logicamente bello e praticamente
utile se i programmi CGI di un utente fossero eseguiti con l'identità
dell'utente stesso: il programma avrebbe pieno accesso al suo account
e potrebbe leggere e scrivere file; i CGI non sarebbero leggibili
nè copiabili dagli altri utenti, anche quando si trattasse
di script; l'autenticazione al DBMS potrebbe essere automatica perchè
basata sulla identità del processo; ecc. Tutte queste possibilità
offrono agli utenti molte opportunità di sfruttare in modo
creativo le tecnologie del WEB.
Esiste anche un rovescio della medaglia: l'esecuzione di programmi
CGI qualora siano realizzati come script richiede più tempo
per l'avviamento rispetto agli stessi programmi eseguiti dagli interpreti
incorporati in Apache. Del resto, se un sito WEB comporta realmente
un grande flusso di traffico, forse vale la pena di dedicare ad
esso un server completo, per cui l'obiezione è valida solo
in parte.
SUEXEC - Ovvero del braccio destro del Demonio
La documentazione di Apache è estremamente prudente in proposito:
SUEXEC è uno strumento potente, ma altrettanto potentemente
insidioso per la sicurezza dalle intrusioni. L'esperienza del passato
ha lasciato alcune vittime, ma gli sviluppatori di Apache hanno
studiato contromosse altrettanto diaboliche. Rimando perciò
a questa fonte per la discussione della questione. Noi sfideremo
intrepidi il rischio, ansiosi di esplorare le nuove dimensioni che
si dischiuderanno.
Il famigerato SUEXEC di Apache non è altro che un programmino
di una decina di KB che si occupa di avviare i CGI per conto di
Apache, conferendo però al processo così avviato l'identità
del suo proprietario. Quindi, ad esempio, i CGI del sig. Tizio verranno
eseguiti con l'identità UID:GID = tizio:users. Il SUEXEC
si preoccupa di garantire che il processo avviato sia legittimo.
La tabellina qui sotto riassume le identità dei vari processi
nel caso di un programma CGI mio.cgi avviato per conto del sig.
Tizio:
Processo UID GID
httpd nobody (RH6.2)
apache (RH7.1) nobody (RH6.2)
apache (RH7.1)
suexec root nobody (RH6.2)
apache (RH7.1)
mio.cgi tizio users
La distribuzione Red Hat viene con il programma suexec già
pronto, ma disattivato. Inoltre, il programma prevede, tra gli infiniti
controlli, che il CGI avviato si trovi dentro alla directory /home/httpd
(o in altra directory prevista dalla distribuzione), sicché
non è adatto alla nostra situazione.
Se la tua distribuzione non include il programma /usr/sbin/suexec
(è infatti questo il nome al secolo dell'innominabile), allora
dovrai proprio procedere alla compilazione dei sorgenti di tutto
Apache, oltre che scaricare la patch SUEXEC dal sito ufficiale.
In questo caso seguire scrupolosamente le indicazioni fornite, eccetto
che il pathfile della directory dei CGI dovrà essere /home.
Buon lavoro e buona fortuna. Ci rivediamo al prossimo paragrafo
sugli ScriptAlias.
Se invece sei un pigro, troverai più veloce ed indolore
la ricetta che segue, valida per le distribuzioni Red Hat 6.2 e
7.1.
suexec è abilitato per eseguire i CGI solo nella directory
/home/httpd/ (RH6.2) o nella dir. /var/www (RH7.1): per abilitare
il meccanismo nelle dir. degli utenti /home è necessario
ricompilare il programma suexec cambiando opportunamente la configurazione
hard-coded. Poiché ci secca di intraprendere questo lungo
e impervio cammino, scegliamo una scorciatoia sporca: andremo a
cambiare il codice del programma con un editor binario! Io ho usato
l'editor del potente Midnight Commander:
Apri il file /usr/sbin/suexec da mc, e usa l'editor binario integrato
di Midnight Commander.
Ora dobbiamo cambiare il path dove suexec consente di eseguire CGI
col meccanismo del suexec. Per la RH6.2, individua la stringa /home/httpd/html
e inserisci uno zero al posto del secondo slash: in questo modo
per il programma la stringa risulterà troncata a solo /home.
Per la RH7.1 invece, individua la stringa /var/www/ e modificarla
in /home, ricordando sempre di mettere un byte zero in fondo, e
sempre senza cambiare la lunghezza complessiva del file. In entrambi
i casi, i caratteri d'avanzo dopo il byte nullo inserito sono irrilevanti.
Un errore in questa fase renderebbe inutilizzabile il file suexec.
Ne hai già fatto una copia, vero?
Nel file suexec è hard-coded anche il nome dell'utente sotto
il quale viene eseguito Apache. Nella RH6.2 è nobody, mentre
nella RH7.1 è apache. Se nella tua configurazione personalizzata
hai modificato l'identità del server Apache, dovrai individuare
questa stringa e modificarla di conseguenza, sempre nello stesso
modo. Di norma non dovrebbe però essere necessario.
Per la RH6.2 e RH7.1 è sufficiente attivare il bit SUID del
programma suexec, e magari restringere il set di permessi inutilmente
ampi che la distribuzione attribuisce al file. Per la RH6.2:
# chown root:nobody /usr/sbin/suexec
# chmod u=xs,g=x,o= /usr/sbin/suexec
mentre per la RH7.1 cambia solo il nome del gruppo proprietario:
# chown root:apache /usr/sbin/suexec
# chmod u=xs,g=x,o= /usr/sbin/suexec
Adesso il suexec è a posto: controlla che l'identità
e i permessi del file suexec appaiano così nella RH6.2: #
ls -l /usr/sbin/suexec
---s--x--- 1 root nobody 9488 Aug 12 14:19 /usr/sbin/suexec
mentre nella RH7.1 dovrebbero apparire così: # ls -l /usr/sbin/suexec
---s--x--- 1 root apache 10976 Aug 12 13:05 /usr/sbin/suexec
Se non corrispondono, agire con chown e chmod opportunamente.
Non rimane che riavviare Apache. Nel log file /var/log/httpd/access_log
dovrebbe apparire: [notice] caught SIGTERM, shutting down
[notice] Apache/1.3.19 (Unix) (Red-Hat/Linux) configured
-- resuming normal operations
[notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
La riga che ci interessa è l'ultima, e ci rassicura sul fatto
che Apache abbia digerito il nostro suexec!
ScriptAlias - Definire la/le directory dei CGI
Il nome di questa direttiva di Apache è fuorviante, perché
lascia pensare che riguardi solo gli script. In realtà si
tratta di una regola di matching che permette ad Apache di stabilire
se un certo file va considerato come documento o come programma
CGI. Nel primo caso Apache tenterà di riconoscerne il tipo
e lo restituirà al client che lo ha richiesto. Nel secondo
caso Apache lancerà il programma come CGI; siccome abbiamo
appena attivato il supporto SUEXEC, il programma assumerà
l'identità del proprietario. Esiste anche ScriptAliasMatch,
che permette di utilizzare anche espressioni regolari: ne vedremo
degli esempi.
Le direttive ScriptAlias e ScriptAliasMatch possono apparire nella
configurazione globale del server, e anche nelle sezioni dei VirtualHost.
La scelta più tradizionale prevede di collocare i CGI nella
directory /cgi-bin della root directory del sito. In tal caso come
direttiva globale metteremo ScriptAlias /cgi-bin/ /home/httpd/cgi-bin/
Dato che abbiamo già fatto la configurazione degli host virtuali,
nelle sezioni dei VirtualHost metteremo qualcosa come <VirtualHost
127.0.0.1:80>
ServerName www.tizio.casa
User tizio
Group users
DocumentRoot /home/tizio/public_html
ServerAdmin tizio@localhost
ScriptAlias /cgi-bin/ /home/tizio/public_html/cgi-bin/
CustomLog logs/www.tizio.casa-access_log common
ErrorLog logs/www.tizio.casa-error_log
</VirtualHost>
Le direttive User e Group dicono al programma suexec quale dovrebbe
essere l'identità del programma CGI: suexec esegue poi un
controllo stringente sulla proprietà del file e sui suoi
diritti di accesso prima di autorizzarne l'esecuzione con l'identità
stabilita.
La direttiva ScriptAlias dichiara dove si trova la directory dei
CGI, e mappa gli URL del tipo http://www.tizio.casa/cgi-bin/xxx
nel pathfile /home/tizio/public_html/cgi-bin/xxx.
ScriptAliasMatch - Un nome, un CGI
Più potente la direttiva ScriptAliasMatch: diventa possibile
utilizzare espressioni regolari per identificare i programmi CGI.
Per fare un esempio, possiamo istruire Apache a riconoscere come
programma CGI ogni file il cui nome termina con la stringa ".cgi".
A questo scopo basta una direttiva globale come questa: ScriptAliasMatch
/(.*)\.cgi /home/httpd/html/$1.cgi
Per i VirtualHost aggiungeremo invece: <VirtualHost 127.0.0.1:80>
ServerName www.tizio.casa
User tizio
Group users
DocumentRoot /home/tizio/public_html
ServerAdmin tizio@localhost
ScriptAlias /cgi-bin/ /home/tizio/public_html/cgi-bin/
ScriptAliasMatch /(.*)\.cgi /home/tizio/public_html/$1.cgi
CustomLog logs/www.tizio.casa-access_log common
ErrorLog logs/www.tizio.casa-error_log
</VirtualHost>
Questa volta la direttiva ScriptAliasMatch mappa URL del tipo http://www.tizio.casa/x/y/z.cgi
nel pathfile /home/tizio/public_html/x/y/z.cgi, ed istruisce anche
Apache sul fatto che questo file sia un CGI da eseguire.
Home page personali
Avrai visto qualche volta un URL del tipo
www.qualcosa.org/~tizio
dove tizio è un qualche individuo che rende disponibile
la sua home page pur senza disporre di un dominio registrato. Il
carattere tilde viene utilizzato per convenzione a questo scopo.
Sulle tastiere italiane si ottiene la tilde premendo ALT+126 (su
Windows) o, in alternativa, indicando nell'URL la sequenza %7e.
Nel primo caso, 126 è il codice ASCII della tilde espresso
nella base decimale; nel secondo caso è lo stesso numero
espresso nella base esadecimale e nella convenzione della codifica
degli URL.
Comunque sia, è pratica comune consentire agli utenti dei
sistemi Unix (e quindi anche GNU/Linux) di predisporre la propria
home page. Per attivare il meccanismo della tilde in Apache bisogna
inserire questa direttiva nella parte globale della configurazione:
UserDir public_html
il cui significato è il seguente: mappa gli URL della forma
http://localhost.localdomain/~utente nel pathfile /home/utente/public_html.
Si possono abilitare anche i CGI nel solito modo:
ScriptAliasMatch /~(.*)/(.*)\.cgi /home/$1/public_html/$2.cgi
Il pattern (.*) esegue il match col nome dell'utente, che viene
sostituito in $1; di conseguenza la stringa (.*) deve eseguire il
match con tutto ciò che resta, che viene sostituito in $2.
Il risultato di queste due sostituzioni fornisce ad Apache il pathfile
assoluto del CGI nel file system. Quindi, ad esempio, l'URL
http://localhost.localdomain/~tizio/x/y/z.cgi
produce gli assegnamenti $1=tizio, $2=x/y/z e quindi in definitiva
Apache invoca il CGI
/home/tizio/public_html/x/y/z.cgi
Proviamo il SUEXEC
Finalmente, siamo giunti alla conclusione. Sinceramente non credo
sia cosa sana tentare tutte le pratiche configuratorie viste fin
qua e ridursi solo ora a provare le cose. Più probabile che
tu abbia fatto le tue prove un po' alla volta modificando progressivamente
il file preconfezionato.
Comunque sia andata, adesso bisogna proprio verificare il funzionamento
di SUEXEC. A questo scopo conviene loggarsi come utente, per esempio
il sig. Tizio, in modo che tutti i file e le directory create abbiano
automaticamente l'identità giusta e facciano contento SUEXEC,
che così non si lamenterà. Crea uno script di test
come questo: #!/bin/bash
echo "Content-Type: text/plain"
echo ""
echo "Identita':"
id
chiamalo /home/tizio/public_html/test.cgi e rendilo eseguibile:
$ chmod u=rwx,go= test.cgi
Eseguilo localmente per vedere se funziona (./test.cgi), quindi
prova l'URL http://www.tizio.casa/test.cgi. Se hai configurato le
home page degli utenti puoi provare anche http://localhost/~tizio/test.cgi.
Sul browser dovrebbe apparire l'id tizio/users, prova provata che
il nostro programmino test.cgi è stato eseguito con l'identità
del suo proprietario. Ecco quello che vedo io:
$ (echo -ne "GET /test.cgi HTTP/1.0\nHost: www.tizio.casa\n\n"
> sleep 2) | telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.localdomain.
Escape character is '^]'.
HTTP/1.1 200 OK
Date: Tue, 11 Dec 2001 21:15:53 GMT
Server: Apache/1.3.19 (Unix) (Red-Hat/Linux)
Connection: close
Content-Type: text/plain
Identita':
uid=506(tizio) gid=100(users) groups=100(users)
Connection closed by foreign host.
La cosa importante sono le due paroline evidenziate in grassetto,
tizio e user: è per vederle comparire che abbiamo fatto tutta
questa trafila del SUEXEC. Congratulazioni, obiettivo centrato!
Se invece, come è probabile, salta fuori qualcos'altro,
allora non resta che ripercorrere tutti i passi della configurazione
fatta fin qui, tenendo sempre aperta una bella finestra con tail
-f dei file di log come abbiamo visto. Alcuni suggerimenti diagnostici:
Netscape mostra sempre lo stesso risultato comunque io agisca.
Bisogna premere il bottone Reload del browser mentre si tiene premuto
il tasto SHIFT, altrimenti Netscape restituisce quello che ha nella
cache.
Il browser risponde "Not Found" e sul log file appare
"unable to stat".
Apache ha risolto l'URL nel pathfile, come viene specificato nel
log; sfortunatamente, tale file non esiste, oppure i permessi delle
directory non permettono ad Apache (che in questa fase ha ancora
l'identità standard) di attraversare una o più delle
directory del pathfile. Controlla il corretto pathfile. Controlla
i permessi della directory home di tizio.
Il browser mostra che lo script ha identità diversa da tizio/users.
Non sta funzionando SUEXEC. Controlla nel log file se all'avvio
di Apache compare il messaggio esplicito della attivazione del SUEXEC.
Controlla la presenza delle direttive User e Group nella sezione
VirtualHost del dominio www.tizio.casa.
Il browser mostra il sorgente dello script.
Controlla la direttiva ScriptAliasMatch nella sezione VirtualHost
del dominio www.tizio.casa.
Infine, confronta il tuo file di configurazione con quello che riporto
qui più avanti.
Ritocchi finali
Rimane ora solo da disabilitare gli interpreti (ad esempio PERL
o PHP, se sono installati) incorporati in Apache. E' facile, basta
commentare tutte le righe di http.conf che si riferiscono a questi
linguaggi. Potremo sempre usare il PHP e il PERL nei nostri CGI,
ma nelle loro versioni stand-alone consuete.
La direttiva globale
DirectoryIndex index.html index.htm index.shtml index.cgi
istruisce il server su quale pagina ritornare quando il client
esegue una GET / o comunque un'altra directory, senza specificare
un file preciso all'interno di questa directory. In questi casi,
Apache tenta di ritornare i file specificati, nell'ordine. Le prime
voci sono quelle consuete, mentre l'ultima l'ho aggiunta per l'occasione.
Per inciso, se nessuno di questi file esiste, Apache ritorna l'elenco
dei file della directory se è presente la direttiva Option
Indexes, altrimenti dà un bel "Forbidden".
httpd.conf in tutta la sua gloria
Giunti alla conclusione di questo lungo cammino, ecco come appare
il mio httpd.conf. Raccomando, prima di procedere ad uno sbrigativo
copia-e-incolla, di fare una copia del vostro attuale file di configurazione,
e per due buoni motivi: 1) contiene molti utili commenti, qui omessi;
2) funziona.
Alcuni moduli opzionali li ho commentati, ma si possono re-inserire
secondo necessità. La loro omissione velocizza parecchio
l'avvio, e riduce il consumo di memoria.
Ho lasciato commentate anche alcune funzionalità che hanno
valore di commento per le personalizzazioni successive. Anche qui,
vedere la documentazione di Apache per il significato delle varie
direttive.
--------------------------------------------------------------------------------
####### httpd.conf: INIZIO
ServerType standalone
ServerRoot "/etc/httpd"
LockFile /var/lock/httpd.lock
PidFile /var/run/httpd.pid
#ScoreBoardFile /var/run/httpd.scoreboard
Timeout 300
KeepAlive On
MaxKeepAliveRequests 10
KeepAliveTimeout 60
MinSpareServers 2
MaxSpareServers 10
# Pochi processi --> risparmio memoria; riavvii + veloci
StartServers 2
MaxClients 10
MaxRequestsPerChild 100
# Carico solo quello che mi serve:
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
LoadModule env_module modules/mod_env.so
LoadModule config_log_module modules/mod_log_config.so
#LoadModule agent_log_module modules/mod_log_agent.so
LoadModule referer_log_module modules/mod_log_referer.so
LoadModule mime_module modules/mod_mime.so
#LoadModule negotiation_module modules/mod_negotiation.so
#LoadModule status_module modules/mod_status.so
#LoadModule info_module modules/mod_info.so
#LoadModule includes_module modules/mod_include.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule cgi_module modules/mod_cgi.so
#LoadModule asis_module modules/mod_asis.so
#LoadModule imap_module modules/mod_imap.so
#LoadModule action_module modules/mod_actions.so
LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
#LoadModule rewrite_module modules/mod_rewrite.so
LoadModule access_module modules/mod_access.so
LoadModule auth_module modules/mod_auth.so
#LoadModule anon_auth_module modules/mod_auth_anon.so
#LoadModule db_auth_module modules/mod_auth_db.so
#LoadModule digest_module modules/mod_digest.so
#LoadModule proxy_module modules/libproxy.so
#LoadModule expires_module modules/mod_expires.so
#LoadModule headers_module modules/mod_headers.so
#LoadModule usertrack_module modules/mod_usertrack.so
LoadModule setenvif_module modules/mod_setenvif.so
ClearModuleList
#AddModule mod_vhost_alias.c
AddModule mod_env.c
AddModule mod_log_config.c
#AddModule mod_log_agent.c
AddModule mod_log_referer.c
AddModule mod_mime.c
#AddModule mod_negotiation.c
#AddModule mod_status.c
#AddModule mod_info.c
#AddModule mod_include.c
AddModule mod_autoindex.c
AddModule mod_dir.c
AddModule mod_cgi.c
#AddModule mod_asis.c
#AddModule mod_imap.c
#AddModule mod_actions.c
AddModule mod_userdir.c
AddModule mod_alias.c
#AddModule mod_rewrite.c
AddModule mod_access.c
AddModule mod_auth.c
#AddModule mod_auth_anon.c
#AddModule mod_auth_db.c
#AddModule mod_digest.c
#AddModule mod_proxy.c
#AddModule mod_expires.c
#AddModule mod_headers.c
#AddModule mod_usertrack.c
AddModule mod_so.c
AddModule mod_setenvif.c
Listen 80
####### Red Hat 6.2:
User nobody
Group nobody
####### Red Hat 7.1:
#User apache
#Group apache
ServerAdmin root@localhost
# Permessi globali:
<Directory />
Options IncludesNOEXEC Indexes SymLinksIfOwnerMatch
AllowOverride None
</Directory>
UserDir public_html
# Permessi di base per le dir. degli utenti:
<Directory "/home/*/public_html">
AllowOverride All
Options Includes Indexes SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ScriptAliasMatch /~(.*)/(.*)\.cgi /home/$1/public_html/$2.cgi
ScriptAliasMatch /~(.*)/cgi-bin/(.*) /home/$1/public_html/$2
<Directory /home/*/public_html/cgi-bin>
AllowOverride All
Options ExecCGI
</Directory>
DirectoryIndex index.html index.htm index.shtml index.cgi _index
AccessFileName .htaccess
<Files ~ "^\.ht">
Order allow,deny
Deny from all
</Files>
UseCanonicalName On
TypesConfig /etc/mime.types
DefaultType text/plain
HostnameLookups Off
ErrorLog /var/log/httpd/error_log
LogLevel warn
#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
#LogFormat "%{Referer}i -> %U" referer
#LogFormat "%{User-agent}i" agent
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\""
mioformatolog
CustomLog /var/log/httpd/access_log mioformatolog
ServerSignature On
Alias /icons/ "/home/httpd/icons/"
<Directory "/home/httpd/icons">
Options Indexes MultiViews
AllowOverride None
Order allow,deny
Allow from all
</Directory>
IndexOptions FancyIndexing
AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
AddIconByType (TXT,/icons/text.gif) text/*
AddIconByType (IMG,/icons/image2.gif) image/*
AddIconByType (SND,/icons/sound2.gif) audio/*
AddIconByType (VID,/icons/movie.gif) video/*
AddIcon /icons/binary.gif .bin .exe
AddIcon /icons/binhex.gif .hqx
AddIcon /icons/tar.gif .tar
AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
AddIcon /icons/a.gif .ps .ai .eps
AddIcon /icons/layout.gif .html .shtml .htm .pdf
AddIcon /icons/text.gif .txt
AddIcon /icons/c.gif .c
AddIcon /icons/p.gif .pl .py
AddIcon /icons/f.gif .for
AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
AddIcon /icons/bomb.gif core
AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README
AddIcon /icons/folder.gif ^^DIRECTORY^^
AddIcon /icons/blank.gif ^^BLANKICON^^
DefaultIcon /icons/unknown.gif
ReadmeName README
HeaderName HEADER
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
AddEncoding x-compress Z
AddEncoding x-gzip gz tgz
AddLanguage en .en
AddLanguage fr .fr
AddLanguage de .de
AddLanguage da .da
AddLanguage el .el
AddLanguage it .it
# v. mod_negotiation
#LanguagePriority it en fr de
AddType text/html .shtml
AddHandler server-parsed .shtml
AddHandler imap-file map
BrowserMatch "Mozilla/2" nokeepalive
BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0
force-response-1.0
BrowserMatch "RealPlayer 4\.0" force-response-1.0
BrowserMatch "Java/1\.0" force-response-1.0
BrowserMatch "JDK/1\.0" force-response-1.0
NameVirtualHost 127.0.0.1:80
<VirtualHost 127.0.0.1:80>
ServerName localhost.localdomain
DocumentRoot /home/httpd/html
ServerAdmin root@casa.lan
ErrorLog logs/localhost.localdomain-error_log
CustomLog logs/localhost.localdomain-access_log common
</VirtualHost>
<VirtualHost 127.0.0.1:80>
ServerName www.tizio.casa
User tizio
Group users
DocumentRoot /home/tizio/public_html
ServerAdmin root@casa.lan
ErrorLog logs/www.tizio.casa-error_log
CustomLog logs/www.tizio.casa-access_log common
ScriptAlias /cgi-bin /home/tizio/public_html/cgi-bin
ScriptAliasMatch /(.*)\.cgi /home/tizio/public_html/$1.cgi
</VirtualHost>
<VirtualHost 127.0.0.1:80>
ServerName www.caio.casa
User caio
Group users
DocumentRoot /home/caio/public_html
ServerAdmin root@casa.lan
ErrorLog logs/www.caio.casa-error_log
CustomLog logs/www.caio.casa-access_log common
ScriptAlias /cgi-bin /home/caio/public_html/cgi-bin
ScriptAliasMatch /(.*)\.cgi /home/caio/public_html/$1.cgi
</VirtualHost>
#### httpd.conf: FINE!
|