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!.
L'autore si riserva tutti i diritti e ne ammette la pubblicazione in qualsiasi forma solo con autorizzazione.
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.
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.
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.
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).
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.
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.
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 makeTerminata la fase di compilazione, la cui durata dipenderà dalle risorse e dalla velocità del vostro computer, come utente root daremo:
make setup checkNel 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 cleanA questo punto sara possibile configurare e utilizzare djbdns secondo le esigenze.
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).
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 Gdnslogoppure:
pw user add Gtinydns -d /nonexistent -s /nonexistent pw user add Gdnslog -d /nonexistent -s /nonexistentI 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_IPin 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/servicetuttavia 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/tinydnse 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
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.
.dominio:indirizzo_ip:nome:ttl:timestamp:localquesta riga crea le informazioni principali per il nameserver di "dominio", ovvero "traducendo" in formato BIND otterremo:
.dominio:indirizzo_ip:dns1.dominio:ttl:timestamp:localCome è 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).
&dominio:indirizzo_ip:nome:ttl:timestamp:localquesta volta verranno create esclusivamente le informazioni per:
Zdominio:ns_primario:contact_address:seriale:refresh_time:retry_time:expire_time:minimum_time:ttl:timestamp:localsicuramente 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):
@dominio:indirizzo_ip:nome:n_precedenza:ttl:timestamp:localche creerà le informazioni per:
=nome:indirizzo_ip:ttl:timestamp:localcreerà:
+nome:indirizzo_ip:ttl:timestamp:localPer dovere di cronaca esistono anche altri tipi di record meno utilizzati, vediamoli rapidamente:
Cnome1:nome2:ttl:timestamp:localcrea un record CNAME in modo che nome1 faccia riferimento a nome2
'dominio:testo:ttl:timestamp:localcrea un record TXT associato a dominio con contenuto "testo"
^dominio:nome:ttl:timestamp:localcrea un record PTR, facendo puntare "dominio" a "nome"
:dominio:codice:data:ttl:timestamp:locale settando opportunamente i campi codice e data è possibile personalizzare le risposte generate da tinydns.
: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.
%in:192.168.0 %ex =luca.dominio:192.168.0.35:::in =luca.dominio:213.1.1.68:::exle 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).
%local:network_ip
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
Record | Valore |
---|---|
A | 0001 |
NS | 0002 |
CNAME | 0005 |
SOA | 0006 |
PTR | 000c |
MX | 000f |
TXT | 0010 |
AXFR | 00fc |
ANY | 00ff |
@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
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.
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 Gdnslogoppure:
pw user add Gdnscache -d /nonexistent -s /nonexistent pw user add Gdnslog -d /nonexistent -s /nonexistentla 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.
dnscache-conf Gdnscache Gdnslog /usr/local/etc/dnscache indirizzo_IPin 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.
#!/bin/sh exec setuidgid Gdnslog multilog t /var/log/dnscacheora non resta che linkare la directory di configurazione alla directory dei servizi di daemontools con:
ln -s /usr/local/etc/dnscache /var/servicee 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.50mentre se intendiamo accettare le interrogazioni provenienti dagli IP 192.168.0.x il comando sarà:
touch /usr/local/etc/dnscache/root/ip/192.168.0Un'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).
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:
#!/bin/sh exec setuidgid Gdnslog multilog t /var/log/dnscache/ '-*' '+* stats * * * *' /var/log/dnscache/statsIn 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:
echo "192.168.0.28" > /var/service/dnscache/root/servers/morettoni.net svc -t /var/service/dnscacheogni 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.
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.
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 Gdnslogoppure:
pw user add Grbldns -d /nonexistent -s /nonexistent pw user add Gdnslog -d /nonexistent -s /nonexistentLa 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 basein 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.
ln -s /usr/local/etc/rbldns /var/servicee in pochi secondi il nostro DNS per liste RBL sarà completamente funzionante!
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:
:indirizzo_ip:testonormalmente 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.4Oltre 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/24Vediamo 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.1ultimata 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.2nella 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.Dnella lista rbl.pippo.it dovrei inviare una richiesta di tipo A oppure TXT per il nome:
D.C.B.A.rbl.pippo.itPer 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
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.cdboppure è 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.cdbE' anche consigliabile modificare (o addirittura cancellare) il Makefile negli slave in modo tale da impedire la generazione in locale del database.
useradd -d /nonexistent -s /nonexistent Gaxfrdns useradd -d /nonexistent -s /nonexistent Gdnslogoppure:
pw user add Gaxfrdns -d /nonexistent -s /nonexistent pw user add Gdnslog -d /nonexistent -s /nonexistente poi creare la configurazione di base con:
axfrdns-conf Gaxfrdns Gdnslog /usr/local/etc/axfrdns /usr/local/etc/tinydns indirizzo_IPcome 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/serviceAnche 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.
indirizzo_ip:allowdove 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.:allowQuando 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 stripa 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.
./dnsnotify dominio slave [slave ...]dove dominio è il nome del dominio che ha subito le modifiche, seguito dagli indirizzi degli slave da notificare.
:allow,AXFR=""
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!
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.tmpdove 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.
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 makedove INDIRIZZO_IP è l'indirizzo della macchina in cui gira tinydns, mentre gli altri IP che ho indicato sono solo esempi.
cd /var/service/dnscache/root/servers echo INDIRIZZO_IP > local chmod 644 local svc -t /var/service/dnscachedove 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.
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/dnscacheCosì tutti i nomi prima verranno cercati in cache, altrimenti in caso negativo verranno cercati dalla macchina che risponde a INDIRIZZO_IP.
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:300Per 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.
# 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
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:
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/installCosì 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).
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/rune opzionalmente:
nome_servizio/log/ nome_servizio/log/runCome 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 secondsL'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_serviziodove le opzioni disponibili sono:
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:
multilog t /var/log/serviceIn questo caso nella directory /var/log/service troveremo i seguenti file:
multilog t n5 s1048576 '-* stat *' /var/log/serviceIn 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/serviceIl primo pattern -* esclude tutte le linee, mentre +* stat * provvede ad includere solo quelle desiderate.
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 processodove user è il nome dell'account, mentre processo è il path e gli eventuali argomenti del programma da lanciare.
envdir directory processoOgni 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.
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.
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 makee come utente root:
make setup checka questo punto UCSPI-TCP è stato installato.
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:
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.
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:
# 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 :denyUna 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_testoDove:
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 programmaIl 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 SERVER PORTA_TCPdove SERVER è il nome o l'indirizzo IP del computer a cui richiedere la connessione, mentre PORTA_TCP è la porta di connessione.
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).
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 makeultimata la compilazione, come utente root:
make setup checka 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.
@400000003e76d38421d114e4 slurp 38dove 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).
svc -a /var/service/dnscachein 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 0lo 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.
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.