djbdns - Un'alternativa (sicura) a BIND

di Luca Morettoni <luca at morettoni dot net>
Ultimo aggiornamento: $Date: 2006/01/27 16:34:07 $, versione $Revision: 1.4 $

La versione aggiornata del presente documento è disponibile su: http://morettoni.net/docs/djbdns.html

E' finalmente disponibile il libro "djbdns, un'alternativa (sicura) a BIND", edito dal Gruppo Editoriale Infomedia, Pisa - codice ISBN: 8881500205.
Il libro si propone come naturale completamento di questo documento, è stato arricchito con molte sezioni riguardanti le configurazioni, la programmazione e molto altro!
Potete acquistare una copia online qui!.

Supporta questo progetto:

L'autore si riserva tutti i diritti e ne ammette la pubblicazione in qualsiasi forma solo con autorizzazione.


Indice


1. Premessa

Il presente articolo è stato redatto per poter presentare in maniera rapida l'utilizzo e le funzionalità di djbdns, una raccolta di programmi per la gestione di un DNS. Tutte le informazioni riportate sono frutto dell'esperienza personale e di una continua ricerca su Internet. Ogni critica, commento o segnalazione sono ben accetti in modo da poter rendere questo testo sempre più completo ed utile a chiunque. In caso di utilizzo degli esempi o comandi riportati nel testo, nè l'autore, nè chi pubblica il presente documento, potrà in alcun modo essere ritenuto responsabile di qualsiasi conseguenza possa scaturirne.


2. Introduzione

Sicuramente molti di voi avranno già sentito parlare di qmail (http://cr.yp.to/qmail.html), un MTA alternativo a Sendmail che sta prendendo sempre più piede su Internet, sviluppato da Dan Bernstein con un principio fondamentale: avere un software sicuro. Un altro software "interessante" sviluppato da Bernstein è djbdns, un ottimo sistema per DNS. Anche djbdns è stato sviluppato per essere sicuro e l'autore ha addirittura messo in palio un premio di 500 dollari per chiunque trovi un baco nel sistema (per i dettagli consiglio di leggere: http://cr.yp.to/djbdns/guarantee.html) e, visti i recenti problemi di sicurezza di BIND, direi che solo questo è già un buon motivo per iniziare a valutarne le caratteristiche.


3. Cos'è un DNS?

Prima di iniziare a vedere cos'è e come funziona djbdns è importante chiarire (a grandi linee ovviamente) come funziona un DNS. Esistono due tipologie principali di servizi DNS: gli "authoritative DNS" e i "resolver DNS" (o recursive resolver). Vediamo di capire cosa e quali sono le loro funzioni con degli esempi, partendo dai "resolver DNS".
E' fondamentale capire la netta distinzione tra questi due tipi di servizi, anche perchè purtroppo BIND integra entrambe le modalità e troppo spesso i suoi utenti tendono a confondersi, mentre djbdns li mantiene nettamente separati.

3.1. Resolver DNS

I resolver DNS sono quei sistemi che permettono di "risolvere" il nome a dominio (es. www.gufi.org) in indirizzo IP della macchina da contattare attraverso un meccanismo di "interrogazioni" che andremo ad analizzare.
Quando dal nostro PC ad esempio apriamo l'home page del GUFI (Gruppo Utenti FreeBSD Italia), il nostro sistema ottiene dal file /etc/resolv.conf l'indirizzo del resolver da utilizzare e provvede, tramite la porta UDP 53, a richiedergli di risolvere l'indirizzo "www.gufi.org".
Il nome del dominio viene diviso in campi, ognuno separato dal carattere . (punto), e ogni campo viene analizzato da destra verso sinistra; il nome così come è stato scritto e come siamo normalmente abituati a farlo non è completo, infatti la corretta notazione richiederebbe anche un punto finale, quindi il nome corretto sarebbe "www.gufi.org.".
Ora campo per campo (ricordo sempre da destra verso sinistra) vedremo come il resolver svolge il suo lavoro:
il primo campo trovato è il solo punto, che sta ad indicare la radice, ovvero il punto di partenza della ricerca; il resolver contatta una serie di server conosciuti (i root server) chiedendo appunto a quale IP corrisponda l'indirizzo www.gufi.org. La risposta che otterrà non sarà quella che ci si attendeva, infatti i root server sicuramente non sono a conoscenza degli indirizzi IP per tale dominio, tuttavia ci viene fornito insieme alla risposta negativa un prezioso indizio, ovvero quale (o quali) server contattare per richiedere informazioni sulla zona "org".
Il resolver sfrutta subito questo suggerimento, rifacendo nuovamente la medesima interrogazione al server della zona "org", tuttavia anche in questo caso otterrà sempre una risposta parziale, ovvero il server indicherà solamente un elenco di server a cui richiedere le informazioni, questa volta per la zona "gufi".
Reinoltrando la richesta all'ennesimo server questa volta otterremo l'IP associato al nome www.gufi.org. visto che finalmente siamo riusciti a contattare il server di tipo authoritative per la zona "gufi".
Questi "indizi" che il nostro resolver riceve di volta in volta dai server che interroga hanno un nome specifico, i glue records che incontreremo e/o definiremo nel nostro eventuale DNS autoritativo.
E' ovvio che il resolver per ogni interrogazione non eseguirà di nuovo tutta la trafila vista prima, infatti tutte le risposte ottenute interrogando i vari nameserver vengono poste in cache, in modo da essere immediatamente disponibili per le richieste successive. Ad esempio se volessimo risolvere l'indirizzo IP per il dominio mail.freebsd.org, il nostro resolver già conosce il server authoritative per la zona "org" quindi eviterà di interrogare i root server risparmiando un passaggio; se invece la mia richiesta fosse cvs.gufi.org, il server authoritative contattato sarebbe quello della zona "gufi" (in questo caso i passaggi risparmiati sarebbero due).

3.2. Authoritative DNS

Nel paragrafo precedente, parlando dei resolver, abbiamo tirato in ballo i server DNS di tipo Authoritative che rappresentano le macchine che contengono le informazioni sui domini. In queste macchine andremo esclusivamente a mappare i vari nomi (zone) con i relativi indirizzi IP.
Ora dovrebbe essere più chiaro il ruolo dei due tipi di server DNS, gli authoritative che "custodiscono" le informazioni dei nomi a dominio (zone) e i resolver che vanno ad individuare quali sono i server di tipo authoritative per il dominio/zona cercato e ne ottengono le informazioni.


4. Cos'è djbdns?

Ora che abbiamo più chiaro il funzionamento dei servizi di DNS andiamo ad analizzare gli strumenti che djbdns ci mette a disposizione. Il pacchetto è costituito in realtà da diversi programmi che operano insieme, questo sia per dare una maggiore sicurezza all'intero sistema sia per rendere il tutto estremamente modulare; in più djbdns non necessita di girare come utente root e ogni suo programma in esecuzione lavora in chroot in una cartella ben definita. Un'altra caratteristica interessante di djbdns è il formato dei suoi file di configurazione, i quali potrebbero sembrare alquanto criptici per chi proviene da BIND, ma una volta entrati nella logica utilizzata dall'autore se ne trarranno molti benefici. Questi file sono infatti strutturati per essere facilmente manipolati via software e/o generati automaticamente. Esistono diversi script che permettono di mantenere le proprie zone in un database e di "esportarle" rapidamente nel formato adatto a djbdns. Ultima nota prima di tuffarci alla scoperta di questo potente strumento: per poter funzionare correttamente djbdns ha bisogno di daemontools e ucspi-tpc, due software sempre realizzati da Bernstein (consiglio di leggere le informazioni che trovate sul sito, oppure il capitolo 6. Programmi correlati del presente documento, dedicato a loro). Nel resto dell'articolo darò per scontato che sono ambedue installati correttamente, e che daemontools sia configurato per leggere la directory dei servizi in /var/services.

4.1. Installazione

djbdns viene distribuito sotto forma di sorgenti da compilare nel proprio computer, quindi occorre innanzi tutto scaricare il pacchetto con i sorgenti disponibili all'indirizzo http://cr.yp.to/djbdns/djbdns-1.05.tar.gz e salvarlo in una directory di lavoro dove andremo a scompattarlo e compilarlo con i seguenti comandi:

	tar xfz djbdns-1.05.tar.gz
	cd djbdns-1.05
	make
Terminata la fase di compilazione, la cui durata dipenderà dalle risorse e dalla velocità del vostro computer, come utente root daremo:
	make setup check
Nel caso in cui il vostro sistema sia un *BSD consiglio di utilizzare l'installazione tramite ports, ad esempio in FreeBSD con l'albero dei ports dovutamente installato e aggiornato basterà digitare (sempre da root):
	cd /usr/ports/dns/djbdns
	make install clean
A questo punto sara possibile configurare e utilizzare djbdns secondo le esigenze.

4.2. Authoritative DNS: tinydns

tinydns rappresenta il cuore di djbdns, essendo il tool che si occupa delle risposte di tipo "authoritative" per i domini che ha in gestione. Di per sè il lavoro svolto è relativamente semplice, tinydns rimane in attesa di richieste e, nel caso in cui la richiesta riguardi un dominio presente nel suo database, fornisce la risposta adeguata, altrimenti non ritorna alcuna informazione. Il cuore vero di tinydns è proprio questo database di configurazione (in BIND sarebbero i file di zona) che sicuramente a prima vista spaventerà per il suo formato alquanto "particolare"; tuttavia la scelta di questo formato è dovuta al fatto di poter rendere questo file facilmente editabile con strumenti informatici (ad esempio sed) o addirittura di poterlo generare con dati provenienti da fonti esterne (es. database).

4.2.1. Configurazione

Prima di entrare nei dettagli del database di tinydns andiamo ad installarlo, creando innanzi tutto due utenti con cui far girare il programma e il suo logger:

	useradd -d /nonexistent -s /nonexistent Gtinydns
	useradd -d /nonexistent -s /nonexistent Gdnslog
oppure:
	pw user add Gtinydns -d /nonexistent -s /nonexistent
	pw user add Gdnslog -d /nonexistent -s /nonexistent
I nomi ovviamente possono essere scelti a proprio piacimento, tuttavia il consiglio è quello di utilizzare quelli "standard" consigliati da Bernstein, compresa la G maiuscola iniziale per rendere i nomi assolutamente univoci nel sistema. Ora non resta altro da fare che creare la struttura di base per tinydns con il comando:
	tinydns-conf Gtinydns Gdnslog /usr/local/etc/tinydns indirizzo_IP
in questo modo avremo in /usr/local/etc/tinydns lo scheletro della configurazione pronto ad essere aggiunto alla lista dei servizi di daemontools con:
	ln -s /usr/local/etc/tinydns /var/service
tuttavia prima di far partire il servizio consiglierei di modificare lo script log/run (tutti i path si intendono relativi a quello di configurazione del servizio, nel nostro esempio /usr/local/etc/tinydns), in quanto tutti i log verranno memorizzati in log/main. Per spostare il tutto in /var/log/tinydns useremo:
	mkdir /var/log/tinydns
	chown -R Gdnslog /var/log/tinydns
e modificheremo lo script log/run in modo tale che al posto di ./main ci sia la direcotry sopra creata: seguendo l'esempio il nostro log/run conterrà:
	#!/bin/sh
	exec setuidgid dnslog multilog t /var/log/tinydns

4.2.2. Le zone

Ora che il nostro authoritative DNS è funzionante, dobbiamo iniziare a creare le varie zone. Come accennato precedentemente il tutto è controllato tramite un file: root/data. Questo file di testo in realtà non viene utilizzato da tinydns, infatti il programma effettua le letture dal file root/data.cdb, ovvero la versione "binaria" di data, il tutto per poter rendere più veloce il tempo di risposta alle interrogazioni. Ogni volta che dovremo modificare o aggiungere le informazioni di una zona andremo ad editare esclusivamente il file data, poi provvederemo alla "compilazione" con un semplice make (per i curiosi consiglio di dare un occhio al Makefile presente in root). Nel caso in cui non ci siano errori di sintassi, verrà generato il nuovo file data.cdb e le modifiche avranno effetto immediato, senza dover killare nessun processo. Cominciamo a vedere più nel dettaglio il formato del file di configurazione, facendo una comparazione con il formato della configurazione di BIND. Ricordo inoltre che nel paragrafo 5.1. Migrazione da BIND a djbdns vedremo come prelevare le informazioni da un server BIND e convertirle automaticamente in formato tinydns.
E' possibile inserire anche linee di commento, precedute dal carattere #, tutto il testo che segue verrà ignorato fino alla fine della riga.
In linea generale il formato dei dati di tinydns è così strutturato:

	<tipo>parametro_1[:parametro_2][:parametro_3]...[:parametro_n]
dove <tipo> rappresenta il tipo di informazione che andiamo a descrivere. mentre gli eventuali parametri verranno separati dal carattere : (due punti). Quasi tutti i tipi di dati che andremo a descrivere hanno un parametro che indicherò con local e che non descriverò, per poi tornarne a parlare al termine del paragrafo.

Cominciamo con:
	.dominio:indirizzo_ip:nome:ttl:timestamp:local
questa riga crea le informazioni principali per il nameserver di "dominio", ovvero "traducendo" in formato BIND otterremo:
La cosa che forse potrà sembrarci strana è la generazione automatica del nameserver primario in nome.ns.dominio e che potrebbe creare difficoltà se vogliamo utilizzare dns1.dominio, basterà indicare:
	.dominio:indirizzo_ip:dns1.dominio:ttl:timestamp:local
Come è facile intuire dal nome, ttl rappresenta il "Time To Live", ovvero il tempo (in secondi) che l'informazione indicata può rimanere memorizzata in cache. Se si omette questo valore sarà assegnato automaticamente da tinydns, anche se normalmente si usa 86400 secondi (24 ore).
Il valore di timestamp invece ha un doppio significato, a seconda del valore di ttl: se ttl è pari a zero, il timestamp rappresenta la scadenza dell'informazione, mentre se ttl è diverso da zero oppure viene omesso, rappresenta la data di inizio validità dell'informazione.
Questo comando informa tinydns che è lui il server autoritativo per la zona dominio, di conseguenza verranno generati anche i "glue records" per la zona stessa.

Una versione più limitata del comando precedente è:
	&dominio:indirizzo_ip:nome:ttl:timestamp:local
questa volta verranno create esclusivamente le informazioni per:
anche in questo caso per non ottenere l'aggiunta automatica di "ns" tra nome e dominio basterà inserire il nome completo da dare al nameserver (vedi esempio precedente).
Attenzione, in questo caso non vengono generati i glue records per il dominio, di conseguenza è consigliabile utilizzare questo comando solo per delegare una sottozona ad un altro DNS.

Infine, se fosse necessario creare solo il record SOA potremmo ricorrere a:
	Zdominio:ns_primario:contact_address:seriale:refresh_time:retry_time:expire_time:minimum_time:ttl:timestamp:local
sicuramente in questo record sono facilmente individuabili tutte le informazioni che eravamo abituati ad indicare nel SOA della zona. Evito di spiegarne il relativo significato, tuttavia è possibile omettere i valori dei seguenti campi (in parentesi ho indicato il valore assegnato per default da tinydns):
Avete letto bene, il numero seriale della zona viene generato ed aggiornato automaticamente da tinydns, ogni volta che andiamo ad apportare delle modifiche al nostro database, comodo vero? Tuttavia se stiamo migrando (come spero) da un server BIND prima di poter sfruttare questa caratteristica è necessario aggiustare manualmente il seriale al primo aggiornamento delle zone (in fondo al testo troverete i passaggi da effettuare).

Un'altra informazione fondamentale per un dominio sono i record MX, ovvero i Mail Exchanger, dichiarabili con:
	@dominio:indirizzo_ip:nome:n_precedenza:ttl:timestamp:local
che creerà le informazioni per:
Come per i record NS, se si vuole attribuire un nome arbitrario al mail exchanger (ad esempio mail.dominio) andrà indicato per esteso nel campo nome.

infine:
	=nome:indirizzo_ip:ttl:timestamp:local
creerà:
nel caso in cui non fosse necessario creare il record PTR useremo:
	+nome:indirizzo_ip:ttl:timestamp:local
Per dovere di cronaca esistono anche altri tipi di record meno utilizzati, vediamoli rapidamente:
	Cnome1:nome2:ttl:timestamp:local
crea un record CNAME in modo che nome1 faccia riferimento a nome2
	'dominio:testo:ttl:timestamp:local
crea un record TXT associato a dominio con contenuto "testo"
	^dominio:nome:ttl:timestamp:local
crea un record PTR, facendo puntare "dominio" a "nome"

Moltissime persone dicono che tinydns non supporta tutti i record DNS gestiti da BIND, questo per fortuna non è vero, infatti grazie ad un comando è possibile creare "manualmente" qualsiasi record non previsto dall'autore.
Con una riga del tipo:
	:dominio:codice:data:ttl:timestamp:local
e settando opportunamente i campi codice e data è possibile personalizzare le risposte generate da tinydns.
Se ad esempio volessi aggiungere un record RP (Responsible Person, ovvero il responsabile per un dominio) aggiungeremo la seguente linea al nostro file data:
	:morettoni.local:17:\004luca\011morettoni\005local\000\000:
In questo esempio la nostra entry RP per il dominio morettoni.local sarà la stringa "luca@morettoni.local" (il carattere @ viene sostituito dal punto). Ovviamente nella sezione data vanno impostati i dati così come devono essere restituiti al client, di conseguenza se dobbiamo inserire valori non ASCII possiamo ricorrere alla notazione ottale come nell'esempio.
Su Internet possiamo trovare numerosi programmi che ci permettono di generare in automatico le stringhe da aggiungersi al file data, in particolare vorrei segnalare:
Per un approfondimento sul formato e sul significato di quanto visto finora, consiglio di leggere attentamente la descrizione "ufficiale" del formato del file di configurazione, reperibile su: http://cr.yp.to/djbdns/tinydns-data.html.

Riprendiamo ora la discussione sospesa inizialmente sul significato del campo local presente su tutti i record (ricordo che questo campo è opzionale). In pratica local rappresenta un tag di tipo testo (verranno presi in considerazione solo i primi due caratteri), e con uno speciale comando è possibile definire uno o più contesti in cui tale tag è valido, ovvero dei casi dove poter includere o escludere determinati record del DNS; cerchiamo di chiarire meglio il funzionamento con un esempio pratico:
	%in:192.168.0
	%ex

	=luca.dominio:192.168.0.35:::in
	=luca.dominio:213.1.1.68:::ex
le prime due righe definiscono due tag, il primo in è valido per tutti i client con indirizzo 192.168.0.x mentre tutti gli altri avranno il tag ex, in questo modo luca.dominio verrà "risolto selettivamente" in funzione del tag associato al client (quindi del suo IP).
In generale la sintassi per l'assegnazione dei tag è:
	%local:network_ip

4.2.3. I log

I log generati da tinydns purtroppo non sono immediatamente leggibili, tuttavia a colpo d'occhio è possibile individuare possibili anomalie del nostro server autoritativo. Il primo &guot;ostacolo" che ci troviamo ad affrontare durante un'analisi di questi log è il formato esadecimale utilizzato per loggare gli IP (e le porte) di provenienza delle query. Infatti tinydns loggherà le richieste proveninenti dall'IP 192.168.0.1 con la stringa c0a80001 (per risalire alla versione decimale basta scomporre la stringa nei quattro ottetti c0.a8.00.01 e ricorventirli singolarmente in decimale).
Ogni richiesta viene loggata su una singola riga che conterrà i seguenti campi:

	timestamp ip:porta:id stato tipo richiesta
In questa tabella trovate elencati alcuni tipi di record e il relativo valore loggato da tinydns:
RecordValore
A0001
NS0002
CNAME0005
SOA0006
PTR000c
MX000f
TXT0010
AXFR00fc
ANY00ff
Riporto infine un estratto da un file di log di tinydns usato per una rete privata.
	@400000004061b79c158d632c starting tinydns
	@400000004061b7a3212a035c c0a8000a:7cb8:6498 + 0001 ibm.morettoni.local
	@400000004061b7a61534df04 c0a8000a:cee5:9ec7 + 001c ibm.morettoni.local
	@400000004061b7a70cfad67c c0a8000a:2512:462c + 000c 1.0.168.192.in-addr.arpa
	@400000004061b7a7263b9edc c0a8000a:b6b2:ed58 + 0001 home.morettoni.local
	@400000004061b7a72a40660c c0a8000a:ab96:b849 + 001c home.morettoni.local
	@40000000406213a316f714ec c0a8000a:5a99:6db4 + 001c build.morettoni.local
	@40000000406213a3176f84f4 c0a8000a:4539:bcef + 0001 build.morettoni.local
	@40000000406221540f50094c c0a8000a:8867:f801 + 000f home.morettoni.local
	@4000000040623d763420d74c c0a8000a:f58a:2d4f - 0001 warez.morettoni.local

4.3. Resolver DNS: dnscache

dnscache è la parte di djbdns che si occupa di rispondere ESCLUSIVAMENTE alle interrogazioni sulle risoluzioni dei nomi, e le risposte che fornirà ai client saranno esclusivamente quelle ritenute sicure. Queste informazioni vengono prelevate da nameserver di tipo authoritative che vengono chiamati in causa come spiegato nell'introduzione.

4.3.1. Configurazione

Prima di iniziare a configurare dnscache è necessario creare due utenti nel sistema con i quali verranno eseguiti dnscache stesso ed il suo logger, quindi come root impartiremo i comandi:

	useradd -d /nonexistent -s /nonexistent Gdnscache
	useradd -d /nonexistent -s /nonexistent Gdnslog
oppure:
	pw user add Gdnscache -d /nonexistent -s /nonexistent
	pw user add Gdnslog -d /nonexistent -s /nonexistent
la G maiuscola che precede i due nomi è stata scelta esclusivamente per rendere univoci globalmente i nomi utente, è chiaro che siete liberi di scegliere una qualsiasi combinazione di nomi valida.
Ora non resta altro che creare lo scheletro di configurazione con il comando dnscache-conf, nel nostro caso è sufficiente:
	dnscache-conf Gdnscache Gdnslog /usr/local/etc/dnscache indirizzo_IP
in questo modo verrà creata una struttura di configurazione nella directory /usr/local/etc/dnscache e il servizio si metterà in ascolto delle richieste sull'indirizzo specificato con indirizzo_IP, se si fosse omesso questo parametro, dnscache avrebbe utilizzato l'indirizzo 127.0.0.1.
Prima di dare il via al servizio è bene effettuare alcuni controlli sulla configurazione appena creata. In particolar modo nella sottodirectory log andiamo ad analizzare il file run, per default i log infatti verranno memorizzati in /usr/local/etc/dnscache/log/main, è buona norma invece tenerli memorizzati ad esempio in /var/log, basterà quindi modificare il contenuto di log/run sostituendo ./main con il path alla directory da noi desiderata (ovviamente dovremmo crearla e assegnarla all'utente scelto per i log, nel nostro caso Gdnslog), ad esempio nella mia configurazione il file log/run ha il seguente contenuto:
	#!/bin/sh
	exec setuidgid Gdnslog multilog t /var/log/dnscache
ora non resta che linkare la directory di configurazione alla directory dei servizi di daemontools con:
	ln -s /usr/local/etc/dnscache /var/service
e in pochi secondi il nostro resolver sarà completamente funzionante! Prima però di andare ad aggiornare tutti i resolv.conf degli altri client della rete dobbiamo ancora una volta effettuare alcune modifiche alla configurazione di base di dnscache, infatti per default dnscache-conf soddisferà solo le richieste provenienti dall'indirizzo IP 127.0.0.1. Tutte le configurazioni di dnscache risiedono nella directory root (sempre relativa alla directory creata da dnscache-conf), all'interno troviamo altre due cartelle: ip e servers. Servers contiene un solo file chiamato @ in cui è memorizzata la lista dei root server che dnscache andrà ad interrogare, mentre la directory ip conterrà dei file di lunghezza zero, e i nomi rappresenteranno gli indirizzi IP o le reti da cui potranno pervernire le richieste di interrogazione; per default troveremo solo un file chiamato 127.0.0.1 appunto. Se volessimo configurare il tutto in modo che risponda alle interrogazioni provenienti solo dall'ip 192.168.0.50 daremo:
	touch /usr/local/etc/dnscache/root/ip/192.168.0.50
mentre se intendiamo accettare le interrogazioni provenienti dagli IP 192.168.0.x il comando sarà:
	touch /usr/local/etc/dnscache/root/ip/192.168.0
Un'altra cartella che contiene files interessanti è env, dentro la quale troviamo diversi file contenenti varie impostazioni di dnscache, tra questi c'è da segnalare il più importante: CACHESIZE, dal cui nome si intuisce che si tratta della dimensione massima (in byte) della cache utilizzabile. Un'altra variabile utilizzata è DATALIMIT che impone la dimensione massima del segmento dati di dnscache (ovviamente deve essere maggiore di CACHESIZE).

4.3.2. La cache

E' chiaro che non è facile stabilire in modo generico di quanta cache abbiamo bisogno, tuttavia su http://cr.yp.to/djbdns/cachesize.html Bernstein spiega un metodo di analisi dei log per stabilire un limite di cache in funzione del traffico effettivo sul DNS.
dnscache scrive nei log una riga contrassegnata dal tag stats, seguita da quattro numeri che stanno a rappresentare:

il valore che a noi interessa è cache motion, ovvero il secondo, tale numero indica il numero di byte scritti in cache da quando è stato attivato il servizio. Per il nostro calcolo occorrerà memorizzare tale numero e poi ricontrollarlo dopo 24 ore. Per facilitare tale operazione consiglio di utilizzare un file di log separato per i valori stats modificando lo script log/run nel seguente modo:
	#!/bin/sh
	exec setuidgid Gdnslog multilog t /var/log/dnscache/ '-*' '+* stats * * * *' /var/log/dnscache/stats
In questo modo il file /var/log/dnscache/stats/current conterrà solo le linee che ci interessano permettendo un'analisi rapida. Ricavata quindi la differenza tra i due valori di cache motion basterà dividere la dimensione della cache impostata con il risultato della differenza e se il numero che avremo si approssimerà a questi valori: Visto che il TTL medio delle zone si aggira intorno ai due/tre giorni sarà il caso di alzare il limite di memoria di CACHESIZE per valori inferiori al tre.
Un settaggio interessante di dnscache è quello di poter indicare quali siano i server authoritative per alcune zone evitando così le interrogazioni viste prima. Con i seguenti comandi:
	echo "192.168.0.28" > /var/service/dnscache/root/servers/morettoni.net
	svc -t /var/service/dnscache
ogni interrogazione che dnscache riceverà per il dominio morettoni.net e le sue sottozone (es. mail.morettoni.net) verrà richiesta al server authoritative all'indirizzo 192.168.0.28, mentre tutte le altre seguiranno il normale funzionamento.

4.4. Le liste RBL: rbldns

djbdns può essere utile per creare delle liste basate su indirizzi IP, come ad esempio le liste RBL utili per controllare il traffico di posta proveniente da IP indesiderati (o autorizzati). Questo compito puo' essere svolto perfettamente da tinydns, tuttavia se abbiamo la necessità di gestire liste di IP possiamo ricorrere ad una versione "alleggerita" che è rbldns.

4.4.1. Configurazione

Anche per rbldns è necessario creare due utenti, uno per il servizio stesso e l'altro per i log, da root daremo:

	useradd -d /nonexistent -s /nonexistent Grbldns
	useradd -d /nonexistent -s /nonexistent Gdnslog
oppure:
	pw user add Grbldns -d /nonexistent -s /nonexistent
	pw user add Gdnslog -d /nonexistent -s /nonexistent
La struttura per il servizio verrà creata grazie al comando rbldns-conf, i parametri che occorrerà indicare sono:
	rbldns-conf Grbldns Gdnslog /usr/local/etc/rbldns indirizzo_IP base
in questo modo verrà creata una struttura di configurazione nella directory /usr/local/etc/rbldns e il servizio si metterà in ascolto delle richieste sull'indirizzo specificato con indirizzo_IP, e utilizzerà base come zona per le interrogazioni in arrivo.
Anche per questo servizio i log per default verranno scritti nella directory ./main, se si desidera memorizzarli altrove sarà sufficiente modificare il contenuto dello script log/run come abbiamo visto per i servizi precedenti.
Ora non resta che linkare la directory di configurazione alla directory dei servizi di daemontools con:
	ln -s /usr/local/etc/rbldns /var/service
e in pochi secondi il nostro DNS per liste RBL sarà completamente funzionante!
Il sistema si metterà in ascolto sulla porta 53 dell'indirizzo IP indicato in fase di configurazione, le interrogazioni che accetterà saranno del tipo 4.3.2.1.base (dove base è la stringa scelta precedentemente), e la risposta sarà positiva se l'indirizzo IP 1.2.3.4 è presente nel database di rbldns.

4.4.2. La lista RBL

Ora che abbiamo visto il funzionamento generale di rbldns vediamo di scoprire come gestire la lista degli indirizzi IP. Fondamentalmente i dati che possiamo inserire nel file data, presente nella directory root sono tre:

Saltiamo la parte che riguarda i commenti e andiamo subito a preparare la nostra lista di indirizzi IP; per primo definiamo i valori di base della lista, ovvero un indirizzo IP da restituire per tutte le interrogazioni che arriveranno e un testo da restituire per le interrogazioni di tipo TXT, normalmente questo testo descriverà brevemente la lista oppure il motivo per cui l'indirizzo IP è presente in essa. La sintassi è:
	:indirizzo_ip:testo
normalmente come indirizzo_ip si utilizza un valore arbitrario (tipo 127.0.0.2) mentre il testo può essere un breve testo descrittivo come accennato precedentemente, in più se al termine della nostra stringa inseriamo un simbolo $ questo verrà sostituito con l'indirizzo IP che è eventualmente presente in lista. Passiamo ora ad inserire gli indirizzi IP veri e propri, per inserire un IP basta semplicemente indicarlo:
	1.2.3.4
Oltre al singolo indirizzo IP è possibile indicare un'intera classe di indirizzi, se ad esempio volessi mettere in lista tutti gli indirizzi che vanno da 192.168.0.1 fino a 192.168.0.255 basterà aggiungere la linea:
	192.168.0.0/24
Vediamo ora un esempio reale di lista RBL utilizzata per bloccare lo spam in arrivo da alcuni indirizzi conosciuti, definiamo la lista inserendo nel file root/data queste righe:
	# lista RBL privata
	:127.0.0.2:tu sei uno spammer $

	192.168.0.0/24
	10.0.0.1
ultimata la fase di editing daremo il comando make in modo da aggiornare il file data.cdb (come succedeva per le zone di tinydns), ora la nostra lista RBL è pronta, e conterrà tutti gli indirizzi IP della classe C 192.168.0.x e l'indirizzo 10.0.0.1, vediamo come arrivano (e se arrivano) le risposte utilizzando i client per le interrogazioni ai DNS. Partiamo con dnsq (che fa parte dei vari tool di djbdns) e facciamo una interrogazione (per la sintassi del comando consiglio di leggere la relativa manpage, presto integrerò) el documento anche una breve descrizione di questi comandi)
	home:~% dnsq txt 1.0.168.192.rbl.local 127.0.0.1
	16 1.0.168.192.rbl.local:
	82 bytes, 1+1+0+0 records, response, authoritative, noerror
	query: 16 1.0.168.192.rbl.local
	answer: 1.0.168.192.rbl.local 2048 16 \036tu\040sei\040uno\040spammer\040192.168.0.1

	home:~% dnsq a 1.0.168.192.rbl.local 127.0.0.1
	1 1.0.168.192.rbl.local:
	55 bytes, 1+1+0+0 records, response, authoritative, noerror
	query: 1 1.0.168.192.rbl.local
	answer: 1.0.168.192.rbl.local 2048 A 127.0.0.2
nella prima interrogazione ho richiesto al server in ascolto su 127.0.0.1 se esisteva un record di tipo TXT dell'indirizzo IP 192.168.0.1 ed ho ottenuto una risposta positiva (ovvero l'IP è presente in lista) con il relativo testo descrittivo (notate come gli spazi vengono scritti in ottale), con la seconda richiesta invece chiediamo solo se esite un record di tipo A, e se ciò è vero otterremo il valore 127.0.0.2. Come avrete potuto notare, gli indirizzi che vado di volta in volta a controllare vengono inviati scritti al rovescio, seguiri da nome che ho scelto per la mia lista (parametro base di rbldns-conf visto precedentemente), quindi se volessi controllare l'indirizzo IP:
	A.B.C.D
nella lista rbl.pippo.it dovrei inviare una richiesta di tipo A oppure TXT per il nome:
	D.C.B.A.rbl.pippo.it
Per concludere vediamo le due richieste precenti fatte questa volta con un altro strumento forse più conosciuto, ovvero dig:
	home:~% dig @127.0.0.1 txt 1.0.168.192.rbl.local

	; <<>> DiG 8.3 <<>> @127.0.0.1 txt 1.0.168.192.rbl.local 
	; (1 server found)
	;; res options: init recurs defnam dnsrch
	;; got answer:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4
	;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
	;; QUERY SECTION:
	;;      1.0.168.192.rbl.local, type = TXT, class = IN

	;; ANSWER SECTION:
	1.0.168.192.rbl.local.  34m8s IN TXT    "tu sei uno spammer 192.168.0.1"

	;; Total query time: 2 msec
	;; FROM: home.morettoni.local to SERVER: 127.0.0.1  127.0.0.1
	;; WHEN: Mon Nov 24 09:42:13 2003
	;; MSG SIZE  sent: 39  rcvd: 82

	home:~% dig @127.0.0.1 a 1.0.168.192.rbl.local

	; <<>> DiG 8.3 <<>> @127.0.0.1 a 1.0.168.192.rbl.local 
	; (1 server found)
	;; res options: init recurs defnam dnsrch
	;; got answer:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4
	;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
	;; QUERY SECTION:
	;;      1.0.168.192.rbl.local, type = A, class = IN

	;; ANSWER SECTION:
	1.0.168.192.rbl.local.  34m8s IN A      127.0.0.2

	;; Total query time: 1 msec
	;; FROM: home.morettoni.local to SERVER: 127.0.0.1  127.0.0.1
	;; WHEN: Mon Nov 24 09:42:17 2003
	;; MSG SIZE  sent: 39  rcvd: 55

4.5. Zone transfer: axfrdns

Fin'ora abbiamo analizzato il funzionamento dei due componenti principali di djbdns, tuttavia abbiamo trascurato un meccanismo importantissimo per i server di tipo authoritative, ovvero lo zone transfer. Rapidamente diciamo che ogni dominio deve avere un DNS di tipo authoritative primario e almeno uno secondario, ogni volta che vengono apportate delle modifiche nel server primario alle informazioni riguardanti il dominio, queste devono essere "comunicate" anche a tutti i secondari, appunto attraverso il meccanismo dello "zone transfer".
Esistono due casi da tenere in considerazione: sia il primario che il secondario utilizzano tinydns oppure uno o più secondari usano BIND.
La soluzione al primo caso è decisamente semplice e sicura, infatti le informazioni sulle zone vengono passate dal primario al secondario attraverso canali sicuri come ad esempio ssh o rsync, semplicemente copiando il file data.cdb usato da tinydsn. E' possibile modificare il Makefile presente nella directory root di tinydns inserendo un nuovo target del tipo:

	slaves: data.cdb
		scp data.cdb ip_secondario:/usr/local/etc/tinydns/root/data.cdb.new
		ssh ip_secondario mv /usr/local/etc/tinydns/root/data.cdb.new /usr/local/etc/tinydns/root/data.cdb
oppure è possibile ricorrere a rsync (specie se abbiamo un file data abbastanza grande) per trasmettere esclusivamente le modifiche da apportare al file, in questo caso il nostro Makefile (sempre del primario) diventerà:
	slaves: data.cdb
		rsync -az -e ssh data.cdb ip_secondario:/usr/local/etc/tinydns/root/data.cdb
E' anche consigliabile modificare (o addirittura cancellare) il Makefile negli slave in modo tale da impedire la generazione in locale del database.
Ora andiamo ad affrontare il secondo caso, ovvero effettuare lo zone transfer da tinydns a BIND; normalmente BIND rimane in attesa sulla porta TCP 53 in attesa di richieste da parte degli slave per dare inizio ad un aggiornamento di zona. Nel pacchetto djbdns è disponibile axfrdns il quale si occupa appunto di monitorare e soddisfare queste richieste, prelevando le informazioni sulle zone dal database di tinydns. Per installare il programma è necessario prima di tutto creare i soliti due utenti (uno per il programma e uno per il logger) con:
	useradd -d /nonexistent -s /nonexistent Gaxfrdns
	useradd -d /nonexistent -s /nonexistent Gdnslog
oppure:
	pw user add Gaxfrdns -d /nonexistent -s /nonexistent
	pw user add Gdnslog -d /nonexistent -s /nonexistent
e poi creare la configurazione di base con:
	axfrdns-conf Gaxfrdns Gdnslog /usr/local/etc/axfrdns /usr/local/etc/tinydns indirizzo_IP
come vedete è stato necessario indicare anche la directory in cui è presente tinydns in modo da poter accedere alla sua configurazione. Ora non resta altro da fare che attivare il servizio aggiungendo la direcotry a quella di daemontools con:
	ln -s /usr/local/etc/axfrdns /var/service
Anche per questo servizio i log vengono memorizzati in main, se si desidera averli ad esempio in /var/log consiglio di seguire le istruzioni riportate per i servizi visti prima.
Le connessioni TCP alla porta 53 vengono monitorate da tcpserver, un programma appartenente al pacchetto ucspi-tcp. Per default axfrdns è configurato per rifiutare tutte le connessioni, quindi dovremo modificare il file tpc aggiungendo:
	indirizzo_ip:allow
dove indirizzo_ip è l'indirizzo del secondario a cui consentiamo la connessione. Ovviamente è possibile indicare diversi IP ognuno su righe differenti, oppure è possibile specificare un'intera sottorete con:
	192.168.0.:allow
Quando effettuiamo un cambiamento in qualche zona è opportuno notificare immediatamente a tutti i secondari senza attendere che siano questi a richiederci la nuova zona. djbdns non prevede nessuno strumento per inviare le notifiche ai server slave, per questo ho realizzato un piccolo programma per ovviare a tale mancanza. Potete scaricarlo all'indirizzo http://morettoni.net/bsd/zonenotify-0.1.tar.gz per installarlo basta dare i comandi:
	tar xfz zonenotify-0.1.tar.gz
	cd zonenotify
	make install strip
a questo punto, ogni volta che aggiorneremo una zona basterà dare il comando:
	zonenotify zona slave_server [slave_server2] [...slave_serverN]
e i server slave_server (e anche slave_server2 fino a slave_serverN) riceveranno la notifica.
In alternativa è disponibile il seguente script perl scritto da Jos Backus (trovate il tutto su http://tinydns.org/dnsnotify), la sintassi è:
	./dnsnotify dominio slave [slave ...]
dove dominio è il nome del dominio che ha subito le modifiche, seguito dagli indirizzi degli slave da notificare.
Ultima nota riguardante axfrdns: il programma può essere utilizzato anche per trasferire le risposte di tinydns che superano la dimensione di 512 bytes, quindi se incorriamo in tale necessità è opportuno installare axfrdns anche se non prevediamo zone transfer (o i nostri secondari usano tinydns) e in tcp inseriremo la seguente regola:
	:allow,AXFR=""


5. Esempi di utilizzo

In questo capitolo ho cercato di raccogliere alcuni esempi pratici di utilizzo o configurazioni del pacchetto djbdns, nel caso in cui troviate bug oppure avete configurazioni interessanti da aggiungere sarò ben lieto di poterle integrare con le mie!

5.1. Migrazione da BIND a djbdns

Se state leggendo questo paragrafo significa che le informazioni che vi ho fornito sono state sufficienti per convincervi a passare da BIND a djbdns!
E' chiaro che la fase che richiede il maggior lavoro durante la transizione è l'aggiornamento dei file di configurazione, tuttavia in djbdns è disponibile un programma che permette di prelevare le informazioni delle zone (attraverso il meccanismo dello zone transfer visto sopra) e di convertirle in automatico nel formato previsto da tinydns. Basterà infatti:

	tcpclient bind_nameserver 53 axfr-get nome_zona zona.txt zona.tmp
dove bind_nameserver è il nome del nameserver su cui gira BIND (assicuratevi di consentire lo zone transfer da quel server verso la macchina da cui state impartendo il comando), mentre nome_zona è il dominio che intendete "prelevare". In questo modo avremo nel file zona.txt le informazioni nel formato tinydns, pronte per essere aggiunte al file root/data.
Come accennato precedentemente, tinydns è in grado di aggiornare automaticamente la versione del record SOA, tuttavia, nel caso in cui stiamo trasferendo domini già esistenti, non è possibile utilizzare immediatamente questa funzionalità a causa di possibili conflitti di versione.
Di norma infatti in BIND la versione nel record SOA viene impostata nel formato anno mese giorno seriale (esempio 2002050703: versione 03 del giorno 7 maggio 2002) mentre tinydns utilizza (nel formato automatico ovviamente) il timestamp dell'ultima modifica effettuata sul file data. Per poter "allineare" le due modalità è sufficiente eseguire le seguenti operazioni:
In fondo non è poi così complesso, vero?

5.2. root server privati

Un servizio di authoritative DNS spesso può servire anche all'interno di reti intranet, in modo da avere una gerarchia di nomi da associare a vari servizi o macchine; con pochi settaggi è possibile "adattare" tinydns e dnscache a questo scopo.
Iniziando da tinydns, andremo a configurarlo in modo tale che sia authoritative per la zona .local, siete liberi anche qui di scegliere il suffisso che più vi piace, tuttavia sconsiglio di usare suffissi legali come .net o .it in modo da evitare "sovrapposizioni" con domini reali, in più se intendete non utilizzare nessun suffisso per i nomi locali basterà sostituire nell'esempio la stringa local con il carattere . (punto).

	cd /var/service/tinydns/root
	echo ".local:INDIRIZZO_IP:a:259200" >> data
	echo "@local:192.168.0.50:mailhub:10:259200" >> data
	echo "+local:192.168.0.25:server:259200" >> data
	make
dove INDIRIZZO_IP è l'indirizzo della macchina in cui gira tinydns, mentre gli altri IP che ho indicato sono solo esempi.
Per dnscache basterà impostare l'indirizzo IP del server authoritative per le zone .local in modo "forzato" con i seguenti comandi:
	cd /var/service/dnscache/root/servers
	echo INDIRIZZO_IP > local
	chmod 644 local
	svc -t /var/service/dnscache
dove INDIRIZZO_IP è l'indirizzo del server su cui gira tinydns configurato prima, e local è il suffisso di "zona privata" scelto precedentemente. Fatto questo tutte le interrogazioni ad indirizzi del tipo server.local oppure mailhub.local verranno risolte dal server interno, fornendo gli indirizzi IP associati nel data di tinydns, mentre le richieste "normali" verranno risolte contattando i root server via Internet.
Nella sezione esempi di tinydns troverete un esempio più dettagliato su una ipotetica zona privata.

5.3. dnscache su connessioni lente

Purtroppo non tutti hanno a disposizione connessioni Internet veloci e/o a basso costo, di conseguenza ottimizzare il traffico diventa una necessità, comprese le query verso i DNS. Anche per gestire questa situazione djbdns ci viene in aiuto mettendo a nostra disposizione una modalità di dnscache chiamata "forward only". In questa modalità dnscache utilizza gli IP presenti in root/servers/@ per effettuare le query di risoluzione, memorizzando i risultati nella propria cache; così facendo gran parte della risoluzione degli indirizzi (quindi il traffico) avverrà nella macchina esterna. Se ad esempio INDIRIZZO_IP è l'indirizzo del resolver messo a disposizione dal nostro provider potremmo settare dnscache con:

	echo "INDIRIZZO_IP" > /var/service/dnscache/root/server/@
	echo 1 > /var/service/dnscache/env/FORWARDONLY
	svc -t /var/service/dnscache
Così tutti i nomi prima verranno cercati in cache, altrimenti in caso negativo verranno cercati dalla macchina che risponde a INDIRIZZO_IP.

5.4. Le zone di tinydns

In questa sezione verranno riportati alcuni estratti di configurazioni reali, tuttavia prima di utilizzarle in un server in produzione vanno analizzate e testate con molta attenzione!
Questa è un estratto della zona del mio nameserver principale:

	# Indirizzi dei Nameserver primario e secondario
	.morettoni.net:192.168.0.1:a
	.morettoni.net:192.168.100.1:b

	# Mail exchange per il dominio
	@morettoni.net:192.168.0.2:a:0:
	@morettoni.net:192.168.100.1:b:1:

	# Mail exchange per il dominio delle mailing list
	@list.morettoni.net:192.168.0.3:a::

	# Un alias di comodo...
	Cmail.morettoni.net:a.mx.morettoni.net:

	# Host principale
	=morettoni.net:192.168.0.5:
	=luca.morettoni.net:192.168.0.5:

	# Altri host...
	+www.luca.morettoni.net:192.168.0.5:
	+www.morettoni.net:192.168.0.5:
	+ftp.morettoni.net:192.168.0.5:
	+cvs.morettoni.net:192.168.0.5:

	# il dominio home.morettoni.net viene gestito
	# da un'altra macchina, con IP dinamico
	&home.morettoni.net:10.0.0.1:a:300
Per la gestione delle zone .it occorre fare attenzione alle specifiche richieste del NIC riguardo al settaggio dei tempi nel SOA (se vengono utilizzati come server secondari quelli messi a disposizione dal NIC). Consiglierei anche la lettura del documento How to receive a delegation from .it, anche se personalmente non ho mai avuto la necessità di creare il record ns.nomedominio.it come indicato.
Una tipica zona per i domini .it potrebbe essere:
	# Creiamo un SOA con i tempi imposti dal NIC
	Znomedominio.it:a.ns.mantainer.it.:hostmaster.mantainter.it.::86400:3600:604800:86400:86400

	# Definiamo NS primario e secondario per il dominio
	.nomedominio.it:192.168.0.100:a.ns.mantainer.it.:86400
	.nomedominio.it:193.205.245.8:dns2.nic.it:86400

	# E ora i record che ci interessano
	@nomedominio.it:192.168.0.10:mail.nomedominio.it.:10:86400
	+nomedominio.it:192.168.0.10:86400
	Cwww.nomedominio.it:nomedominio.it:86400
	Cftp.nomedominio.it:nomedominio.it:86400


6. Programmi correlati

6.1. daemontools

daemontools (http://cr.yp.to/daemontools.html) è un insieme di programmi, sempre sviluppati da Bernstein, che permettono di gestire agevolmente servizi e processi trattandoli come "demoni". Per chi non fosse a conoscenza di cosa sia un demone è presto detto: un demone non è altro che un programma che gira in background. Per colloquiare con i demoni esistono vari metodi, il più famoso è tramite l'invio di segnali (consiglio di leggere la man page del comando kill(1)), per facilitare la gestione dei segnali, e non solo, è stato creato daemontools, i servizi che ci vengono messi a disposizione sono:

6.1.1. Installazione

L'installazione di daemontools è estremamente semplice, una volta scaricato il pacchetto con i sorgenti all'indirizzo http://cr.yp.to/daemontools/daemontools-0.76.tar.gz basterà digitare (da utente root):

	tar xvfz daemontools-0.76.tar.gz
	cd admin/daemontools-0.76
	package/install
Così facendo daemontools verrà installato in modo da cercare i servizi da lanciare nella directory /service, per cambiare tale impostazione è necessario modificare gli script presenti nella directory package, anche se a breve sarà disponibile una mia patch per automatizzare la modifica. Durante l'installazione al vostro sistema viene anche aggiunto un file di startup per lanciare svscan al momento del boot (i template utilizzati sono boot.inittab o boot.rclocal e vengono scelti in base al sistema operativo che avete).

6.1.2. I servizi

Ora che daemontools è correttamente installato e funzionante dobbiamo creare le directory che vanno a definire i servizi da lanciare. Come già detto precedentemente per tutti gli esempi supporrò che la cartella di servizio di daemontools sia /var/service. Nel caso in cui ne aveste scelta un'altra, adattare gli esempi è comunque molto semplice. La struttura della directory di servizio è altrettanto semplice:

	nome_servizio/
	nome_servizio/run
e opzionalmente:
	nome_servizio/log/
	nome_servizio/log/run
Come vedete, nella cartella principale del servizio è richiesto solo il file run, mentre opzionalmente è possibile definire un "sotto servizio" per la gestione dei log di cui parleremo approfonditamente più avanti. Il programma svscan ogni cinque secondi controlla tutte le sottodirectory della cartella di servizio e per ognuna di esse (se non è già in esecuzione) esegue lo script run, quando questo termina viene rilanciato automaticamente. Ora che il programma è in esecuzione possiamo avere delle informazioni sulla sua esecuzione con il comando:
	# svstat /var/service/nome_servizio
	/var/service/nome_servizio: up (pid 9583) 79700 seconds
L'output ci conferma che il servizio è in esecuzione (stato up) con il pid 9583 da 79700 secondi, mentre se lo volessimo "manipolare" useremo:
	svc [opzioni] /var/service/nome_servizio
dove le opzioni disponibili sono:

6.1.3. I log

Un'altra caratteristica interessante di daemontools è la possibilità di gestire i log dei servizi. I log sono in realtà dei "sottoserviti" (quindi è possibile controllarli con i comandi svstat e svc) e anch'essi vengono lanciati attraverso lo script run della directory log. Lo standard output del servizio principale viene rediretto verso il sottoservizio di log, se volessimo memorizzare anche lo standard error occorrerà inserire nel file run del processo principale il comando exec 2>&1. Uno script standard per il servizio di log potrebbe assomigliare a questo:

	#!/bin/sh
	exec multilog [comandi]
I comandi utilizzabili sono: Vediamo insieme alcuni esempi per chiarire meglio il funzionamento dei log di daemontools.
	multilog t /var/log/service
In questo caso nella directory /var/log/service troveremo i seguenti file:
	multilog t n5 s1048576 '-* stat *' /var/log/service
In questo caso multilog ruoterà il file current ogni volta che la dimensione supererà un Megabyte, conservando i cinque file più recenti. In più nel file current non verranno memorizzate le righe che subito dopo il timestamp riportano la stringa stat. Se invece volessimo memorizzare solo tali righe i comandi sarebbero:
	multilog t n5 s1048576 '-*' '+* stat *' /var/log/service
Il primo pattern -* esclude tutte le linee, mentre +* stat * provvede ad includere solo quelle desiderate.

6.1.4. Programmi aggiuntivi

daemontools è costituito da numerosi programmi, in questo paragrafo vedremo due dei più utilizzati negli impieghi reali: setuidgid e envdir.
setuidgid permette di lanciare un processo figlio assumendo UID e GID di un particolare utente: la sintassi è semplice:

	setuidgid user processo
dove user è il nome dell'account, mentre processo è il path e gli eventuali argomenti del programma da lanciare.
envdir invece permette di lanciare un processo, utilizzando i file contenuti in una directory passata come parametro per settare le variabili di ambiente. La sintassi del comando è:
	envdir directory processo
Ogni file presente in directory diventerà una variabile di ambiente il cui valore sarà il contenuto del file stesso. Se ad esempio in directory troviamo un file DATASIZE, il cui contenuto è 1024, quando processo viene lanciato da envdir troverà la variabile di ambiente DATASIZE settata a 1024.

6.2. UCSPI-TCP

UCSPI-TCP (http://cr.yp.to/ucspi-tcp.html) sta per Unix Client Server Program Interface for TCP e non è altro che un insieme di programmi per gestire le connessioni TCP sia lato client che lato server. Grazie a questi tools è possibile rimpiazzare inetd offrendo maggiore sicurezza e robustezza, nonchè un minore consumo di risorse.

6.2.1. Installazione

Anche per questo pacchetto l'installazione non comporta grosse difficoltà, una volta scaricato il file http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz daremo i seguenti comandi:

	tar xfz ucspi-tcp-0.88.tar.gz
	cd ucspi-tcp-0.88
	make
e come utente root:
	make setup check
a questo punto UCSPI-TCP è stato installato.

6.2.2. tcpserver

Tra i principali tool che compongono UCSPI-TCP c'è tcpserver, il quale si occupa di rimanere in attesa su una determinata porta e in caso di connessione esegue un programma. Vediamo la sintassi generale del comando:

	tcpserver [opzioni] indirizzo_ip porta_tcp programma [opzioni_programma]
Le opzioni di tcpserver essendo numerose le vedremo successivamente, mentre gli altri parametri sono:
Tornando alle opzioni di tcpserver, vediamo le principali: Per terminare, tcpserver setta una serie di variabili di ambiente utili al programma lanciato dopo la connessione:

6.2.3. tcpclient

Come è facile intuire tcpclient svolge il lavoro inverso di tcpserver effettuando la connessione questa volta da lato client. La sintassi del comando è:

	tcpclient [opzioni] indirizzo_ip porta_tcp programma [opzioni_programma]
Il significato dei parametri è lo stesso visto precedentemente, solo che in questo caso indirizzo_ip e porta_tcp indicano l'indirizzo e la porta del server a cui connettersi; a connessione avvenuta viene lanciato il programma indicato il cui descrittore di file numero sei riceverà i dati dal server, mentre dal numero sette potrà inviarli.
Le opzioni previste da tcpclient sono: Una volta stabilita la connessione tcpclient setterà le stesse variabili di ambiente viste precedentemente.

6.2.4. tcprules

E' possibile indicare a tcpserver, per mezzo dell'opzione -x, quali server possano accedere ad un determinato servizio. Gli accessi vengono stabiliti in base all'indirizzo IP del client che si sta connettendo, come succede con il file /etc/hosts.allow. Il formato di queste regole è semplice:

	# riga di commento
	regola:azione[,variabile]
La parte a sinistra dei due punti rappresenta la regola vera e propria e può avere i seguenti formati: Le opzioni che richiedono l'identificazione dell'host verranno verificate solo se tcpserver è stato lanciato con l'opzione -p, mentre per l'identificazione dell'utente è necessario utilizzare l'opzione -r.
E' possibile anche indicare solo una parte di un indirizzo IP in una regola, ad esempio 192.168.0. comprenderà tutti gli indirizzi IP che vanno dal 192.168.0.1 al 192.168.0.255, è importante indicare il punto finale negli indirizzi IP parziali. In più è possibile anche indicare dei range di indirizzi, come ad esempio 192.168.0.10-20 (tutti gli IP che vanno dal 10 al 20 compresi).
La seconda parte della regola (dopo il carattere due punti) indica l'azione da intraprendere per le connessioni che arrivano dall'IP o dall'host indicato; le azioni possibili sono due: deny e allow seguite eventualmente da una o più variabili di ambiente da assegnare. Vediamo alcuni esempi di regole:
	# Accetta le connessioni dalla rete locale
	192.168.0.:allow

	# L'utente luca può connettersi da un'altra sottorete
	luca@10.0.0.20-30:allow

	# Tutti gli IP che riusciamo ad identificare accedono
	# ma settiamo la variabile READONLY a YES
	=:allow,READONLY=YES

	# Tutti gli altri vengono bloccati
	:deny
Una volta ultimato (o modificato) il nostro set di regole è necessario ricompilarlo visto che tcpserver utilizza una versione binaria di tale file per ottimizzare le prestazioni. Il comando che useremo è:
	tcprules file_binario file_temporaneo < file_testo
Dove: Terminata la compilazione tcpserver utilizzerà le nuove regole all'arrivo di una nuova connessione senza bisogno di riavvio.

6.2.5. Programmi aggiuntivi

I programmi che compongono il pacchetto sono numerosi,due dei quali molto importanti: recordio e tcpcat.
Il primo, recordio, intercetta tutto il traffico in entrata e in uscita dal server e lo invia al descrittore due. Questa caratteristica è spesso utile in fase di debug di un server o per controllare il traffico sensibile. La modalità di utilizzo con tcpserver è semplice:

	tcpserver [opzioni] recordio programma
Il funzionamento del programma e delle connessioni non verrà alterato, ma tutto il traffico verrà visualizzato in questo modo:
	PID DIR Dati...
dove PID indica il numero di processo del programma server che è stato lanciato, DIR indicherà con i simboli > o < la direzione dei dati (> per i dati in transito dal server verso il client, e < per il contrario).
tcpcat è invece utile per controllare il traffico in arrivo da un particolare server: l'output del programma è l'esatta copia dei dati che riceve, la sintassi è:
	tcpcat SERVER PORTA_TCP
dove SERVER è il nome o l'indirizzo IP del computer a cui richiedere la connessione, mentre PORTA_TCP è la porta di connessione.

6.3. Patch

Pur essendo un prodotto completo e ben funzionante djbdns nel corso del tempo è stato esteso e ampliato con numerose patch, di seguito troverete le indicazioni sul funzionamento di alcune di queste. E' comunque opportuno ricordare che queste patch non sono ufficiali, quindi non c'è nessuna garanzia sul loro funzionamento (quindi decade l'eventuale premio messo in palio da Dan) e possono essere distribuite solo separatamente dal pacchetto djbdns originale (vedi licenza).

6.3.1. dumpcache

La cache utilizzata da dnscache viene mantenuta esclusivamente in memoria, questo significa chiaramente che in caso di riavvio del servizio (o della macchina) tutti i dati in cache vengono persi e tutte le interrogazioni ripartiranno dai root server. Per evitare questo è disponibile la patch "dumpcache" sviluppata da Florent Guillaume la quale permette appunto di salvare lo stato di dnscache e ripristinarlo al suo riavvio. La patch è disponibile su http://efge.free.fr/djbdns/patch-dnscache-dumpcache-v4.txt, mentre all'indirizzo http://efge.free.fr/djbdns/ troverete una breve spiegazione sul suo funzionamento. L'installazione, una volta scaricata la patch dall'indirizzo indicato è semplice:

	tar xfz djbdns-1.05.tar.gz
	cd djbdns-1.05
	patch -p1 < /tmp/patch-dnscache-dumpcache-v4.txt
	make
ultimata la compilazione, come utente root:
	make setup check
a questo punto dnscache utilizzerà tre nuove variabili: DUMPCACHE, DUMPCACHETMP e SLURPCACHE e la directory root/dump. Come è facile intuire nella cartella dump verrà memorizzato il file con i dati della cache, DUMPCACHE conterrà il nome del file di cache (per default dumpcache) mentre DUMPCACHETMP sarà il nome del file temporaneo utilizzato durante il processo di scrittura. L'ultima variabile, SLURPCACHE, invece è il nome del file che verrà letto all'avvio di dnscache.
Al momento dell'avvio di dnscache, se il nome del file indicato con SLURPCACHE esiste, il suo contenuto viene posto in cache, altrimenti viene utilizzata una cache vuota (come farebbe dnscache di default). L'avvenuta lettura della cache su disco viene confermata da una linea nel log file con un contenuto simile:
	@400000003e76d38421d114e4 slurp 38
dove 38 indica il numero di informazioni lette dalla cache su disco (se il numero fosse stato zero significa che il file non era disponibile o non conteneva dati validi).
Per scrivere invece la cache su disco è necessario inviare un segnale ALRM a dnscache, con:
	svc -a /var/service/dnscache
in questo modo il file DUMPCACHE conterrà l'immagine della cache in quel momento, l'avvenuta memorizzazione verrà confermata con la seguente linea nel file di log:
	@400000003e76d29b04a5df6c dump err 0
lo zero dopo dump err sta ad indicare la scrittura avvenuta in modo corretto, mentre nel caso in cui ci fossimo dimenticati di settare le variabili DUMPCACHE o DUMPCACHETMP avremmo 9999. Nel caso in cui abbiamo lanciato il comando dnscache-conf dopo la patch, noteremo che i valori di default delle variabili SLURPCACHE e DUMPCACHETMP sono diversi (rispettivamente dump/dumpcache e slurpcache), questo significa che in caso di riavvio il sistema ripartirà comunque con la cache vuota, sarà quindi opportuno sistemare questi due valori in modo che puntino allo stesso file, oppure prima di far ripartire dnscache sarà necessario copiare il file salvato in modo che venga riletto.
Visto che dnscache non salva autonomamente il contenuto della cache, se si vuole automatizzare questa operazione si dovrà inserire nel crontab il comando visto prima. Inoltre, al momento del salvataggio dnscache trasformerà il TTL dei record da un valore in secondi alla effettiva data di scadenza.


A. Link utili

Di seguito riporto alcuni link utili dove potrete reperire maggiori informazioni riguardanti djbdns. Ringrazio anticipatamente chiunque voglia segnalarmi altri link, o eventuali problemi con quelli esistenti.