Tavoitteena on sallia dynaamiset DNS-päivitykset zoneihin järjestelmässä jossa on käytössä uudempi BIND (9+ sallii dynaamiset päivitykset) sekä SELinux. Oletetaan että käytössä on Red Hat:in valmistamat nk. "targeted security" SELinux-säännöt (oletuksena päällä). Tässä tekstissä samalla käydään läpi eri työkalut liittyen SELinux-asetuksiin, koska samantyylisia ongelmia tulee vastaan muidenkin palvelujen yhteydessä. Tekstiä voi siis pitää ohjenuorana vastaavien ongelmien ratkonnassa.
Jotta BIND suostuu vastaanottamaan päivityspyyntöjä, pitää se erikseen sallia per zone-tiedosto. Alla on erikseen tehty ACL (access control list) jossa kerrotaan mistä IP-osotteista päivitykset otetaan vastaan ja sitten ACL:ää on käytetty zonessa. Tämä siis alkutilanne, joka ei tosin toimi SELinux:in kanssa (vielä). Jos käytössäsi ei ole SELinux:ia niin silloin nämä ohjeet soveltuvat vain osittain (voit ohittaa kaikki SELinux:iin liittyvät asiat).
Huomaathan tässä vaiheessa jo että päivitysmekanismi jota tässä rakennetaan ei ole "turvallinen", koska se perustuu pelkästään päivittäjän IP-osoitteeseen (ACL:ien vuoksi). "Oikea" tapa on käyttää DNSSEC:iä, mutta Windows:it eivät valitettavasti osaa sitä. Toisaalta BIND ei valitettavasti osaa Windows:ien käyttämään tunnistusta dynaamisissa päivityksissä.
acl luokkaverkko_acl { 193.64.22.0/23; }; zone "luokkaverkko.net" { type master; file "luokkaverkko.net.zone"; allow-update { luokkaverkko_acl; localhost; }; };
[ Named.conf jossa määritelty zone johon voi DDNS on sallittu ]
Huomattavaa ylläolevassa esimerkissä on että "localhost"-niminen ACL on BIND:iin jo sisäänrakennettu. Eli sallitaan päivitykset sekä verkosta 193.64.22.0/23 että osoitteesta 127.0.0.1. Zone-tiedosto itse sijaitsee directory-option osoittaman paikan alla koska file-parametrille annetaan suhteellinen polku. Eli RHEL4:ssä se olisi tiedostossa /var/named/luokkaverkko.net.zone.
Tässä tekstissä tehdään vain nk. "forward"-tyylinen zone, mutta reverse-zonet toteutetaan täysin samoin joten sen takia ne on jätetty tästä pois.
Testaus voidaan suorittaa lähettämällä omalta koneelta tietueiden lisäyspyyntöjä. Pyyntöjä voidaan lähettää nsupdate-nimisellä ohjelmalla,
[root@system ~]# nsupdate > server 127.0.0.1 > update add foo3.luokkaverkko.net 8000 A 2.3.4.5 > send > quit
[ Dynaamisen päivityksen testaus nsupdate-työkalulla ]
Yllä on yritetty lisätä foo.luokkaverkko.net-nimelle A-tietue (address). Eli suomeksi, haluttu lisätä IP-osoite nimelle foo. nsupdate on interaktiivinen ohjelma joka lukee syötteestä komentoja. Ohjaalmalla sen syöte jostain tiedostosta voidaan päivitykset ja poistot myös automatisoida (mitä ei tehdä tässä). Se mitä ylläolevasta ei nähdä on että päivätys epäonnistuu (nsupdate valittaisi oikeasti että palvelin ei hyväksynyt päivitystä).
Eli jostain syystä päivitys epäonnistui, vaikka se muuten onkin ihan järkevä päivitysyritys. Tutkikaamme BIND:in lokeja (oletuksena menevät syslog:in kautta /var/log/messages-tiedostoon Red Hat:eillä).
named[19731]: client 127.0.0.1#32805: updating zone 'luokkaverkko.net/IN': adding an RR named[19731]: journal file luokkaverkko.net.zone.jnl does not exist, creating it named[19731]: luokkaverkko.net.zone.jnl: create: permission denied named[19731]: client 127.0.0.1#32805: updating zone 'luokkaverkko.net/IN': error: journal open failed: unexpected error
[ BIND ei onnistu tiedostonluonnissa ]
Eli lokista voi lukea sen että päivityspyyntö on kyllä saatu. BIND on myös kohdentanut oikean zonen mihin päivitys kohdistuu. Kolmas rivi tähdentää varsinaisen ongelman, josta alla lisää.
Koska zone-tiedostot voivat sisältää suhteellisen monipuolistakin syntaksia ja kommenttikenttiä, ei BIND kirjoita zone tiedostoa uusiksi jokaisen päivityksen jälkeen. Tiedostojen tulkinta sekä uusiksikirjoitus olisi vain liian raskasta vähänkin isommassa ympäristössä. Tämän vuoksi BIND säilyttää zoneen kohdistuvat muutokset erillisessä tiedostossa. Tiedosto on binäärimuotoinen jotta BIND voi nopeasti tulkita sen sisältöä, mutta tämä tarkoittaa myös että muutoksia on vaikea nähdä "käsin". Ongelma kuitenkin ylempänä on nyt siinä että BIND päivityspyynnön saatuaan (ja hyväksyttyään) yrittää päivittää zoneen liittyvää kirjanpitotiedostoa ("journal file"). Koska /var/named/luokkaverkko.net.zone.jnl-nimistä tiedostoa ei ole olemassa, BIND yrittää luoda sellaisen ja siihen BIND-prosessilla ei ole oikeuksia. BIND-prosessi on käynnissä named UID:lla ja ilmeisesti hakemiston suojaukset estävät kirjoituksen sillä UID:lla.
BIND:ille ei valitettavasti voi erikseen säätää mihin se kirjanpitotiedostot kirjottaisi, vaan se yrittää aina automaagisesti käyttää zonen tiedostonimeä jatkettuna .jnl:llä.
Tarkistetaan seuraavaksi josko oikeasti oikeuksissa olisi toivomisen varaa. Samalla kokeilla vippaskonstia joka yleensä auttaa vastaaviin tilanteisiin, eli luodaan valmiiksi puuttuva tiedosto tyhjänä ja samalla korjataan sitten oikeudet siten että BIND oikeasti voi tiedostoja muuttaa. Myös zone-tiedoston oikeudet pitää korjata jotta BIND voi kirjoittaa kirjanpitotiedoston sisällön takaisin oikeaan zoneen.
[root@system ~]# cd /var/named [root@system named]# ls -l drwxrwx--- named named 4096 Apr 4 2003 data -rw-r--r-- named named 198 Apr 4 2003 localdomain.zone -rw-r--r-- named named 195 Apr 4 2003 localhost.zone -rw-r--r-- root root 240 Mar 1 16:04 luokkaverkko.net.zone -rw-r--r-- named named 415 Apr 4 2003 named.broadcast -rw-r--r-- named named 2518 Apr 4 2003 named.ca -rw-r--r-- named named 432 Apr 4 2003 named.ip6.local -rw-r--r-- named named 433 Apr 4 2003 named.local -rw-r--r-- named named 416 Apr 4 2003 named.zero drwxrwx--- named named 4096 Apr 4 2003 slaves [root@system named]# touch luokkaverkko.net.zone.jnl [root@system named]# chown named.named luokkaverkko* [root@system named]# ls -la luokkaverkko* -rw-r--r-- named named 240 Mar 1 16:04 luokkaverkko.net.zone -rw-r--r-- named named 0 Mar 1 16:12 luokkaverkko.net.zone.jnl
[ Oikeuksien tarkistus ja korjaus ]
Tämän jälkeen käynnistetään BIND uudestaan (service restart named) ja yritetään päivitystä uudelleen. Sen jälkeen katsotaan lokia (päivitys ei onnistu nimittäin vieläkään):
named[19731]: client 127.0.0.1#32806: updating zone 'luokkaverkko.net/IN': adding an RR named[19731]: luokkaverkko.net.zone.jnl: open: permission denied kernel: audit(1141229009.574:3): avc: denied { write } for pid=19733 comm="named" name="luokkaverkko.net.zone.jnl" dev=dm-0 ino=362477 scontext=root:system_r:named_t tcontext=root:object_r:named_zone_t tclass=file named[19731]: client 127.0.0.1#32806: updating zone 'luokkaverkko.net/IN': error: journal open failed: unexpected error
[ BIND ei onnistu vieläkään ]
Lokissa näkyy viesti ytimen AVC-logiikalta (SELinux:in oikeuksien testaus komponentti). AVC logiikka estää tällä kertaa write-operaation joka kohdistuu luokkaverkko.net.zone.jnl-tiedostoon ja samalla nähdään mikä on prosessin (named) tietoturvakonteksti (scontext) sekä mikä on tiedoston tietoturvakonteksti (tcontext).
Red Hat näyttää konfiguroineen SELinuxin siten että zone-tiedostojen kirjoitus on estetty named_t-kontekstista, ja tämän vuoksi BIND:in kirjoitusoperaatio päättyykin surullisesti unexpected error-viestiin.
Ennenkuin korjataan BIND:in ongelmat, niin olisi hyvä tietää jotain peruskomentoja SELinux:iin liittyen. Aloitetaan komennolla jolla voidaan tarkistaa onko SELinux päällä ja miten se on aktivoitu.
[root@system named]# cat /etc/redhat-release Red Hat Enterprise Linux ES release 4 (Nahant Update 2) [root@system named]# sestatus SELinux status: enabled SELinuxfs mount: /selinux Current mode: enforcing Mode from config file: enforcing Policy version: 18 Policy from config file:targeted Policy booleans: allow_ypbind inactive dhcpd_disable_trans inactive httpd_builtin_scripting active httpd_disable_trans inactive httpd_enable_cgi active httpd_enable_homedirs active httpd_ssi_exec active httpd_tty_comm inactive httpd_unified active mysqld_disable_trans inactive named_disable_trans inactive named_write_master_zonesinactive nscd_disable_trans inactive ntpd_disable_trans inactive pegasus_disable_trans inactive portmap_disable_trans inactive postgresql_disable_transinactive snmpd_disable_trans inactive squid_disable_trans inactive syslogd_disable_trans inactive use_nfs_home_dirs inactive use_samba_home_dirs inactive use_syslogng inactive winbind_disable_trans inactive ypbind_disable_trans inactive
[ RHEL-version tarkistus sekä SELinux-status ]
Syy miksi RHEL-versio tarkistetaan ensin on se että SELinux:iin liittyvät työkalujen nimet vaihtelevat valitettavasti eri jakeluiden välillä, joten ensin katsotaan että ollaan järjestelmässä jossa nämäkin ohjeet sitten toimivat :-).
sestatus-komennon tulostamat ensimmäiset rivit ovat tärkeimmät meidän kannalta. Ylläolevasta listauksesta nähdään että SELinux on aktiivinen nk. "noudattavassa" tilassa (enforcing). Tämä on RHEL4-asennusten oletus mikäli muuta ei asennuksen aikana valita.
Noudattava tila tarkoittaa sitä että AVC noudattaa SELinux:in sääntöjä eikä anna minkään operaation onnistua mikäli se on estetty. Astetta kevyempi tila on päällä silloin kun noudattaminen otetaan pois päältä (esim setenforce-komennolla, tai system-config-securitylevel-työkalulla). Tällöin AVC tekee samat tarkistukset kuin aiemminkin, mutta ei pysäytä kiellettyjä vaan ainoastaan tekee lokiviestejä. Heikennetty tila on tarkoitettu kolmansien osapuolten toimittamien ohjelmistojen testaukseen jotta nähdään mikäli SELinux-sääntöjä tulee muokata. Huomattavaa tässä vaiheessa on että Red Hat:in konfiguroima SELinux rajoittaa pelkästään verkko-ohjelmistoja. Muut prosessit jatkavat klassisen UNIX-mallin mukaisesti.
Koska SELinux:issa tiedostojärjestelmän objekteilla on normaalien ominaisuuksien lisäksi myös SELinux-ominaisuudet, olisi tässä vaiheessa hyvä esitellä komennot joilla näitä voitaisiin listata. Näiden lisäksi komennot id sekä ps osaavat -Z:n. Komennolla newrole voidaan käynnistää uusi komentotulkki tiettyyn kontekstiin.
[root@system named]# ls -Z drwxrwx--- named named system_u:object_r:named_cache_t data -rw-r--r-- named named system_u:object_r:named_zone_t localdomain.zone -rw-r--r-- named named system_u:object_r:named_zone_t localhost.zone -rw-r--r-- named named root:object_r:named_zone_t luokkaverkko.net.zone -rw-r--r-- named named root:object_r:named_zone_t luokkaverkko.net.zone.jnl -rw-r--r-- named named system_u:object_r:named_zone_t named.broadcast -rw-r--r-- named named system_u:object_r:named_conf_t named.ca -rw-r--r-- named named system_u:object_r:named_zone_t named.ip6.local -rw-r--r-- named named system_u:object_r:named_zone_t named.local -rw-r--r-- named named system_u:object_r:named_zone_t named.zero drwxrwx--- named named system_u:object_r:named_cache_t slaves [root@system named]# chcon --reference=data luokkaverkko.net.zone* [root@system named]# ls -Z luokkaverkko.zone* -rw-r--r-- named named system_u:object_r:named_cache_t luokkaverkko.net.zone -rw-r--r-- named named system_u:object_r:named_cache_t luokkaverkko.net.zone.jnl
[ SELinux-attribuuttien listaus ja muutos ]
ls-työkalun optiolla -Z saadaan näkyviin lista klassisista UNIX-oikeuksista, omistajuustiedot sekä SELinux-tietoturvakonteksti tiedostolle. Ylläolevasta listauksesta nähdään että zone- ja kirjanpitotiedostot molemmat ovat named_zone_t-tietoturvakontekstissa alunperin. Kyseinen konteksti kuitenkin on RH:n toimittamassa SELinux:issa tehty siten että BIND voi kyseisiä tiedostoja lukea, muttei niitä muuttaa. Tämän vuoksi ongelmia tulee kun BIND yrittää avata kirjanpitotiedoston kirjoitusta varten (AVC-viesti).
Yllä tilannetta korjataan siten että sekä zone- että kirjanpitotiedoston tietoturvakonteksti muutetaan sopivaksi. Sopiva tässä tarkoittaa named_cache_t-kontekstia joka on tarkoitettu välimuistitiedostoille. Sinänsä korjaus ei ole ihan tyylipuhdas, koska zone-tiedostot eivät ole sama asia kuin välimuisti, mutta kun dynaamiset päivitykset otetaan käyttöön, ne alkavat muistuttamaan osittain välimuistia koska BIND myös kirjoittaa niihin pelkän lukemisen sijaan.
chcon-työkalulle voidaan suoraan antaa haluttu konteksti parametrinä mutta tässä tapauksessa tiedetään että /var/named/data:lla on jo haluttu konteksti jolloin kopioidaan konteksti sieltä. Tämän jälkeen tarkistetaan että konteksti on muuttunut (named_cache_t).
Kokeillaan päivitystä taas tämän jälkeen (BIND:iä ei tarvitse edellisen jälkeen käynnistää uudelleen).
named[19731]: client 127.0.0.1#32807: updating zone 'luokkaverkko.net/IN': adding an RR named[19731]: client 127.0.0.1#32807: updating zone 'luokkaverkko.net/IN': error: journal open failed: no more
[ No ei se kyllä vieläkään toimi ]
Kuten ylläolevista lokiriveistä näkyy, AVC-ilmoitus katosi (eli SELinux-puoli on nyt kunnossa), mutta eipäs päivitys toimi vieläkään. BIND:in käyttämät kirjanpitotiedostot eivät koskaan voi olla tyhjiä, vaan niiden alussa lukee kuinka paljon dataa niiden sisällä on. Tässä tapauksessa BIND olettaa että kyseessä on sen oma kirjanpitotiedosto ja yrittää tulkita sitä, mutta tulkinta ei onnistu ja tämän vuoksi BIND antaa virheilmoituksen (joka ei ole hirveän kuvaava).
Ongelma siis on seuraava: BIND:in pitää päästä luomaan kirjanpitotiedosto itse, koska me ei sitä voida tehdä sen puolesta. BIND-prosessilla kuitenkaan ei ole kirjoitusoikeuksia /var/named-hakemistoon, joten ratkaisuna voidaan pitää sitä että sijoitetaan päivitettävä zone-tiedosto jonnekin mihin BIND:illä on kirjoitusoikeudet. Olisi myös hyvä että sitten kun BIND luo zoneen liittyvän kirjanpitotiedoston, luotu tiedosto saisi automaattisesti oikean SELinux-kontekstin.
RHEL4:ssä on valmis hakemisto johon BIND saa kirjoittaa. Normaalisti BIND kirjoittaa sinne välimuistinsa joten se on valmiiksi suojattu siten että oikeuksien suhteen ei pitäisi tulla ongelmia. SELinux taas toimii siten että kun uusia tiedostoja luodaan hakemistojen alle niin niihin kopioidaan hakemiston tietoturvakonteksti. Eli tässä tapauksessa zone-tiedostojen sijoitus /var/named/data-hakemistoon on ratkaisu. Huomionarvoista tässä on mv-komennon käyttäytyminen SELinux:in kanssa. Kun mv:llä siirretään tiedostoja paikasta toiseen, tiedostojen SELinux-konteksti siirtyy mukana. Eli mv:hen ei päde yllämainittu "uudet tiedostot perivät hakemiston kontekstin"-sääntö. Meidän tapauksessa tämä ei tule olemaan ongelma koska siirrettävillä tiedostoilla on jo valmiiksi oikea konteksti.
Korjaus vaatii siis sen että zone-tiedosto siirretään ensin, ja sitten /etc/named.conf:ia muutetaan siten että BIND osaa hakea zonen oikeasta paikkaa.
[root@system named]# rm luokkaverkko.net.zone.jnl [root@system named]# mv luokkaverkko.net.zone* data [root@system named]# ls -Z data -rw-r--r-- named named root:object_r:named_cache_t luokkaverkko.net.zone
[ Tiedoston siirto sekä tarkistus ]
Koska BIND ei osaa käsitellä tyhjiä kirjanpitotiedostoja, aiemmin luotu tyhjä kirjnapitotiedosto poistetaan ja luotetaan siihen että BIND saa luotua itse kirjanpitotiedoston kun se sitten saa ensimmäisen päivityspyynnön zoneen.
acl luokkaverkko_acl { 193.64.22.0/23; }; zone "luokkaverkko.net" { type master; file "data/luokkaverkko.net.zone"; allow-update { luokkaverkko_acl; localhost; }; };
[ BIND-asetusten korjaus ]
Eli ainoa korjaus BIND:in kannalta on zone-tiedoston sijainti.
Tämän jälkeen yritetään vielä kerran päivitystä:
named[19033]: client 127.0.0.1#32792: updating zone 'luokkaverkko.net/IN': adding an RR named[19033]: journal file data/luokkaverkko.net.zone.jnl does not exist, creating it named[19033]: zone luokkaverkko.net/IN: sending notifies (serial 2006030102)
[ Ihmeiden ihme, ei virheitä ]
BIND onnistuu luomaan kirjanpitotiedoston onnistuneesti (koska virheitä ei tule) ja myöskin kertoo vielä meille että se lähettää viestit zonen muutoksesta mahdollisille orja-palvelimille (slave). Tässä vaiheessa kannattaakin huomata että muutokset tehdään aina master-zoneen. Mikäli päivityspyyntö tulee zoneen joka on orja-tilassa, pyyntö hylätään. Tämän vuoksi asiakasohjelmat (kuten nsupdate) selvittävät ensin missä majailee kohdezonen master ja lähettävät päivityspyyntönsä sille.
Joka kerta kun zoneen tulee muutos dynaamisesti, BIND kasvattaa itsenäisesti zonen sarjanumeroa (joka näkyy samalla rivillä "notifies"-viestissä).
Tarkistetaan vielä tehty muutos dig:illä (pieni paranoia on tervettä siinä vaiheessa kun testataan uusia asioita):
[root@system ~]# dig @127.0.0.1 a foo3.luokkaverkko.net. ; <<>> DiG 9.2.4 <<>> @127.0.0.1 a foo3.luokkaverkko.net. ;; global options: printcmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18766 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0 ;; QUESTION SECTION: ;foo3.luokkaverkko.net. IN A ;; ANSWER SECTION: foo3.luokkaverkko.net. 8000 IN A 2.3.4.5 ;; AUTHORITY SECTION: luokkaverkko.net. 86400 IN NS 1.2.3.20. ;; Query time: 0 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Wed Mar 1 17:42:01 2006 ;; MSG SIZE rcvd: 77
[ Muutos näkyy kyselijöille ]
Muutos näkyy ulospäin, joten tärkein osuus toimii.
Koska BIND ei tallenna kaikkia muutoksia suoraan zone-tiedostoihin, tulee zonejen ylläpidossa käsin (ja ohjelmallisestikin) olla erityisen varovainen. Koska jokaisen zonen sisältö nyt muodostuu sekä varsinaisesti zone-tiedostosta että kirjanpitotiedostosta, pitää BIND:ille erikseen kertoa että se ajaa muutokset zonetiedostoon "nyt" ennen muutoksia. Jottei samanaikaisesti voi tulla ulkoapäin lisää muutoksia zoneen, pitää zone "sammuttaa".
Valitettavasti vanhemmissa BIND-versioissa tämä käytännössä tarkoittaa seuraavaa menetelmää:
- service named stop : Tämä pakottaa BIND:in ajamaan kirjanpitotiedostot zonetiedostoihin. Pysäyttää myös DNS:n.
- rm /var/named/data/*.jnl : Poistetaan kaikki kirjanpitotiedostot, koska niiden data on nyt myös zonetiedostoissa.
- Muokataan zone-tiedostoja halutuksi. Muista päivittää serial!
- service named start (kannattaa myös tarkistaa muokatut zone-tiedostot named-checkzone-työkalulla ennen starttia)
Mikäli käytössä on 9.3-sarjan BIND, on zonet mahdollista erikseen jäädyttää ja jatkaa (rndc-työkalulla), jolloin koko DNS-palvelua ei tarvitse ajaa operaatioiden ajaksi alas.
Esimerkki zone-tiedostosta johon on kirjanpitotiedosto ajettu useampien muutosten jälkeen:
$ORIGIN . $TTL 86400 ; 1 day luokkaverkko.net IN SOA localhost.luokkaverkko.net. root.luokkaverkko.net. ( 2006030105 ; serial 10800 ; refresh (3 hours) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) NS 1.2.3.20. A 1.2.3.4 A 1.2.3.58 $ORIGIN luokkaverkko.net. $TTL 700 ; 11 minutes 40 seconds foo A 1.2.3.7 $TTL 86400 ; 1 day foo2 A 1.2.3.9 $TTL 8000 ; 2 hours 13 minutes 20 seconds foo3 A 2.3.4.5 $TTL 86400 ; 1 day www A 1.2.3.4 A 1.2.3.5 A 1.2.3.6
[ Zone-tiedosto useamman muutoksen jälkeen ]
Nk. "oikea" ratkaisu jossa käsitellään myös ISC:n DHCPd:n integrointia tekemään dynaamisia päivityksiä: http://ops.ietf.org/dns/dynupd/secure-ddns-howto.html .
Red Hat:in oma SELinux-guide (ihan ok, kertoo myös jotain RH:n käyttämistä säännöistä): http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/selinux-guide/ .
Versiohistoria:
- 2006-03-03 Docsifioitu raapustus, eka julkistus
Copyright 2006 Aleksandr Koltsoff (czr(at)iki(piste)fi). Käyttö henkilökohtaiseen opiskeluun sallittu, muusta käytöstä erikseen sovittava.