1 # Unix SMB/CIFS implementation.
2 # backend code for provisioning DNS for a Samba4 server
4 # Copyright (C) Kai Blin <kai@samba.org> 2011
5 # Copyright (C) Amitay Isaacs <amitay@gmail.com> 2011
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 """DNS-related provisioning"""
29 from samba
.ndr
import ndr_pack
, ndr_unpack
30 from samba
import read_and_sub_file
, setup_file
31 from samba
.dcerpc
import dnsp
, misc
32 from samba
.dsdb
import (
33 DS_DOMAIN_FUNCTION_2000
,
34 DS_DOMAIN_FUNCTION_2003
,
35 DS_DOMAIN_FUNCTION_2008
,
36 DS_DOMAIN_FUNCTION_2008_R2
38 from base64
import b64encode
41 def add_ldif(ldb
, ldif_file
, subst_vars
, controls
=["relax:0"]):
42 ldif_file_path
= os
.path
.join(samba
.param
.setup_dir(), ldif_file
)
43 data
= read_and_sub_file(ldif_file_path
, subst_vars
)
44 ldb
.add_ldif(data
, controls
)
46 def modify_ldif(ldb
, ldif_file
, subst_vars
, controls
=["relax:0"]):
47 ldif_file_path
= os
.path
.join(samba
.param
.setup_dir(), ldif_file
)
48 data
= read_and_sub_file(ldif_file_path
, subst_vars
)
49 ldb
.modify_ldif(data
, controls
)
51 def setup_ldb(ldb
, ldif_path
, subst_vars
):
52 """Import a LDIF a file into a LDB handle, optionally substituting
55 :note: Either all LDIF data will be added or none (using transactions).
57 :param ldb: LDB file to import into.
58 :param ldif_path: Path to the LDIF file.
59 :param subst_vars: Dictionary with substitution variables.
61 assert ldb
is not None
62 ldb
.transaction_start()
64 add_ldif(ldb
, ldif_path
, subst_vars
)
66 ldb
.transaction_cancel()
69 ldb
.transaction_commit()
72 """Return an absolute path to the provision tempate file specified by file"""
73 return os
.path
.join(samba
.param
.setup_dir(), file)
75 def get_domainguid(samdb
, domaindn
):
76 res
= samdb
.search(base
=domaindn
, scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID"])
77 domainguid
= str(ndr_unpack(misc
.GUID
, res
[0]["objectGUID"][0]))
80 def get_ntdsguid(samdb
, domaindn
):
81 configdn
= samdb
.get_config_basedn()
83 res1
= samdb
.search(base
="OU=Domain Controllers,%s" % domaindn
, scope
=ldb
.SCOPE_ONELEVEL
,
84 attrs
=["dNSHostName"])
86 res2
= samdb
.search(expression
="serverReference=%s" % res1
[0].dn
, base
=configdn
)
88 res3
= samdb
.search(base
="CN=NTDS Settings,%s" % res2
[0].dn
, scope
=ldb
.SCOPE_BASE
,
90 ntdsguid
= str(ndr_unpack(misc
.GUID
, res3
[0]["objectGUID"][0]))
94 class ARecord(dnsp
.DnssrvRpcRecord
):
95 def __init__(self
, ip_addr
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
96 super(ARecord
, self
).__init
__()
97 self
.wType
= dnsp
.DNS_TYPE_A
99 self
.dwSerial
= serial
100 self
.dwTtlSeconds
= ttl
103 class AAAARecord(dnsp
.DnssrvRpcRecord
):
104 def __init__(self
, ip6_addr
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
105 super(AAAARecord
, self
).__init
__()
106 self
.wType
= dnsp
.DNS_TYPE_AAAA
108 self
.dwSerial
= serial
109 self
.dwTtlSeconds
= ttl
112 class CNameRecord(dnsp
.DnssrvRpcRecord
):
113 def __init__(self
, cname
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
114 super(CNameRecord
, self
).__init
__()
115 self
.wType
= dnsp
.DNS_TYPE_CNAME
117 self
.dwSerial
= serial
118 self
.dwTtlSeconds
= ttl
121 class NSRecord(dnsp
.DnssrvRpcRecord
):
122 def __init__(self
, dns_server
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
123 super(NSRecord
, self
).__init
__()
124 self
.wType
= dnsp
.DNS_TYPE_NS
126 self
.dwSerial
= serial
127 self
.dwTtlSeconds
= ttl
128 self
.data
= dns_server
130 class SOARecord(dnsp
.DnssrvRpcRecord
):
131 def __init__(self
, mname
, rname
, serial
=1, refresh
=900, retry
=600,
132 expire
=86400, minimum
=3600, ttl
=3600, rank
=dnsp
.DNS_RANK_ZONE
):
133 super(SOARecord
, self
).__init
__()
134 self
.wType
= dnsp
.DNS_TYPE_SOA
136 self
.dwSerial
= serial
137 self
.dwTtlSeconds
= ttl
140 soa
.refresh
= refresh
147 class SRVRecord(dnsp
.DnssrvRpcRecord
):
148 def __init__(self
, target
, port
, priority
=0, weight
=100, serial
=1, ttl
=900,
149 rank
=dnsp
.DNS_RANK_ZONE
):
150 super(SRVRecord
, self
).__init
__()
151 self
.wType
= dnsp
.DNS_TYPE_SRV
153 self
.dwSerial
= serial
154 self
.dwTtlSeconds
= ttl
156 srv
.nameTarget
= target
158 srv
.wPriority
= priority
163 def setup_dns_partitions(samdb
, domaindn
, forestdn
, configdn
, serverdn
):
165 # FIXME: Default security descriptor for Domain-DNS objectCategory is different in
166 # our documentation from windows
168 domainzone_dn
= "DC=DomainDnsZones,%s" % domaindn
169 forestzone_dn
= "DC=ForestDnsZones,%s" % forestdn
171 add_ldif(samdb
, "provision_dnszones_partitions.ldif", {
172 "DOMAINZONE_DN": domainzone_dn
,
173 "FORESTZONE_DN": forestzone_dn
,
176 domainzone_guid
= get_domainguid(samdb
, domainzone_dn
)
177 forestzone_guid
= get_domainguid(samdb
, forestzone_dn
)
179 domainzone_guid
= str(uuid
.uuid4())
180 forestzone_guid
= str(uuid
.uuid4())
182 domainzone_dns
= ldb
.Dn(samdb
, domainzone_dn
).canonical_ex_str().strip()
183 forestzone_dns
= ldb
.Dn(samdb
, forestzone_dn
).canonical_ex_str().strip()
185 add_ldif(samdb
, "provision_dnszones_add.ldif", {
186 "DOMAINZONE_DN": domainzone_dn
,
187 "FORESTZONE_DN": forestzone_dn
,
188 "DOMAINZONE_GUID": domainzone_guid
,
189 "FORESTZONE_GUID": forestzone_guid
,
190 "DOMAINZONE_DNS": domainzone_dns
,
191 "FORESTZONE_DNS": forestzone_dns
,
192 "CONFIGDN": configdn
,
193 "SERVERDN": serverdn
,
196 modify_ldif(samdb
, "provision_dnszones_modify.ldif", {
197 "CONFIGDN": configdn
,
198 "SERVERDN": serverdn
,
199 "DOMAINZONE_DN": domainzone_dn
,
200 "FORESTZONE_DN": forestzone_dn
,
204 def add_dns_accounts(samdb
, domaindn
):
205 add_ldif(samdb
, "provision_dns_accounts_add.ldif", {
206 "DOMAINDN": domaindn
,
209 def add_dns_container(samdb
, domaindn
, prefix
):
210 # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
211 msg
= ldb
.Message(ldb
.Dn(samdb
, "CN=MicrosoftDNS,%s,%s" % (prefix
, domaindn
)))
212 msg
["objectClass"] = ["top", "container"]
213 msg
["displayName"] = ldb
.MessageElement("DNS Servers", ldb
.FLAG_MOD_ADD
, "displayName")
217 def add_rootservers(samdb
, domaindn
, prefix
):
219 rootservers
["a.root-servers.net"] = "198.41.0.4"
220 rootservers
["b.root-servers.net"] = "192.228.79.201"
221 rootservers
["c.root-servers.net"] = "192.33.4.12"
222 rootservers
["d.root-servers.net"] = "128.8.10.90"
223 rootservers
["e.root-servers.net"] = "192.203.230.10"
224 rootservers
["f.root-servers.net"] = "192.5.5.241"
225 rootservers
["g.root-servers.net"] = "192.112.36.4"
226 rootservers
["h.root-servers.net"] = "128.63.2.53"
227 rootservers
["i.root-servers.net"] = "192.36.148.17"
228 rootservers
["j.root-servers.net"] = "192.58.128.30"
229 rootservers
["k.root-servers.net"] = "193.0.14.129"
230 rootservers
["l.root-servers.net"] = "199.7.83.42"
231 rootservers
["m.root-servers.net"] = "202.12.27.33"
234 rootservers_v6
["a.root-servers.net"] = "2001:503:ba3e::2:30"
235 rootservers_v6
["f.root-servers.net"] = "2001:500:2f::f"
236 rootservers_v6
["h.root-servers.net"] = "2001:500:1::803f:235"
237 rootservers_v6
["j.root-servers.net"] = "2001:503:c27::2:30"
238 rootservers_v6
["k.root-servers.net"] = "2001:7fd::1"
239 rootservers_v6
["m.root-servers.net"] = "2001:dc3::35"
241 container_dn
= "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix
, domaindn
)
243 # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
244 msg
= ldb
.Message(ldb
.Dn(samdb
, container_dn
))
245 msg
["objectClass"] = ["top", "dnsZone"]
248 # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
250 for rserver
in rootservers
:
251 record
.append(ndr_pack(NSRecord(rserver
, serial
=0, ttl
=0, rank
=dnsp
.DNS_RANK_ROOT_HINT
)))
253 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=@,%s" % container_dn
))
254 msg
["objectClass"] = ["top", "dnsNode"]
255 msg
["dnsRecord"] = ldb
.MessageElement(record
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
258 # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
259 for rserver
in rootservers
:
260 record
= [ndr_pack(ARecord(rootservers
[rserver
], serial
=0, ttl
=0, rank
=dnsp
.DNS_RANK_ROOT_HINT
))]
261 # Add AAAA record as well (How does W2K* add IPv6 records?)
262 #if rserver in rootservers_v6:
263 # record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0)))
264 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=%s,%s" % (rserver
, container_dn
)))
265 msg
["objectClass"] = ["top", "dnsNode"]
266 msg
["dnsRecord"] = ldb
.MessageElement(record
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
269 def add_at_record(samdb
, container_dn
, prefix
, hostname
, dnsdomain
, hostip
, hostip6
):
271 fqdn_hostname
= "%s.%s" % (hostname
, dnsdomain
)
276 at_soa_record
= SOARecord(fqdn_hostname
, "hostmaster.%s" % dnsdomain
)
277 at_records
.append(ndr_pack(at_soa_record
))
280 at_ns_record
= NSRecord(fqdn_hostname
)
281 at_records
.append(ndr_pack(at_ns_record
))
283 if hostip
is not None:
285 at_a_record
= ARecord(hostip
)
286 at_records
.append(ndr_pack(at_a_record
))
288 if hostip6
is not None:
290 at_aaaa_record
= AAAARecord(hostip6
)
291 at_records
.append(ndr_pack(at_aaaa_record
))
293 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=@,%s" % container_dn
))
294 msg
["objectClass"] = ["top", "dnsNode"]
295 msg
["dnsRecord"] = ldb
.MessageElement(at_records
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
298 def add_srv_record(samdb
, container_dn
, prefix
, host
, port
):
299 srv_record
= SRVRecord(host
, port
)
300 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
301 msg
["objectClass"] = ["top", "dnsNode"]
302 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(srv_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
305 def add_ns_record(samdb
, container_dn
, prefix
, host
):
306 ns_record
= NSRecord(host
)
307 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
308 msg
["objectClass"] = ["top", "dnsNode"]
309 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(ns_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
312 def add_ns_glue_record(samdb
, container_dn
, prefix
, host
):
313 ns_record
= NSRecord(host
, rank
=dnsp
.DNS_RANK_NS_GLUE
)
314 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
315 msg
["objectClass"] = ["top", "dnsNode"]
316 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(ns_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
319 def add_cname_record(samdb
, container_dn
, prefix
, host
):
320 cname_record
= CNameRecord(host
)
321 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
322 msg
["objectClass"] = ["top", "dnsNode"]
323 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(cname_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
326 def add_host_record(samdb
, container_dn
, prefix
, hostip
, hostip6
):
329 a_record
= ARecord(hostip
)
330 host_records
.append(ndr_pack(a_record
))
332 aaaa_record
= AAAARecord(hostip6
)
333 host_records
.append(ndr_pack(aaaa_record
))
335 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
336 msg
["objectClass"] = ["top", "dnsNode"]
337 msg
["dnsRecord"] = ldb
.MessageElement(host_records
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
340 def add_domain_record(samdb
, domaindn
, prefix
, dnsdomain
):
341 # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
342 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain
, prefix
, domaindn
)))
343 msg
["objectClass"] = ["top", "dnsZone"]
346 def add_msdcs_record(samdb
, forestdn
, prefix
, dnsforest
):
347 # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN>
348 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
349 (dnsforest
, prefix
, forestdn
)))
350 msg
["objectClass"] = ["top", "dnsZone"]
354 def add_dc_domain_records(samdb
, domaindn
, prefix
, site
, dnsdomain
, hostname
, hostip
, hostip6
):
356 fqdn_hostname
= "%s.%s" % (hostname
, dnsdomain
)
358 # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
359 domain_container_dn
= ldb
.Dn(samdb
, "DC=%s,CN=MicrosoftDNS,%s,%s" %
360 (dnsdomain
, prefix
, domaindn
))
363 add_at_record(samdb
, domain_container_dn
, "DC=@", hostname
, dnsdomain
, hostip
, hostip6
)
365 # DC=<HOSTNAME> record
366 add_host_record(samdb
, domain_container_dn
, "DC=%s" % hostname
, hostip
, hostip6
)
368 # DC=_kerberos._tcp record
369 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._tcp", fqdn_hostname
, 88)
371 # DC=_kerberos._tcp.<SITENAME>._sites record
372 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._tcp.%s._sites" % site
,
375 # DC=_kerberos._udp record
376 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._udp", fqdn_hostname
, 88)
378 # DC=_kpasswd._tcp record
379 add_srv_record(samdb
, domain_container_dn
, "DC=_kpasswd._tcp", fqdn_hostname
, 464)
381 # DC=_kpasswd._udp record
382 add_srv_record(samdb
, domain_container_dn
, "DC=_kpasswd._udp", fqdn_hostname
, 464)
384 # DC=_ldap._tcp record
385 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp", fqdn_hostname
, 389)
387 # DC=_ldap._tcp.<SITENAME>._sites record
388 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.%s._sites" % site
,
391 # FIXME: The number of SRV records depend on the various roles this DC has.
392 # _gc and _msdcs records are added if the we are the forest dc and not subdomain dc
394 # Assumption: current DC is GC and add all the entries
397 add_srv_record(samdb
, domain_container_dn
, "DC=_gc._tcp", fqdn_hostname
, 3268)
399 # DC=_gc._tcp.<SITENAME>,_sites record
400 add_srv_record(samdb
, domain_container_dn
, "DC=_gc._tcp.%s._sites" % site
, fqdn_hostname
, 3268)
403 add_ns_glue_record(samdb
, domain_container_dn
, "DC=_msdcs", fqdn_hostname
)
405 # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions
408 # Assumption: Additional entries won't hurt on os_level = 2000
410 # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones
411 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site
,
414 # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones
415 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site
,
418 # DC=_ldap._tcp.DomainDnsZones
419 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.DomainDnsZones",
422 # DC=_ldap._tcp.ForestDnsZones
423 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.ForestDnsZones",
427 add_host_record(samdb
, domain_container_dn
, "DC=DomainDnsZones", hostip
, hostip6
)
430 add_host_record(samdb
, domain_container_dn
, "DC=ForestDnsZones", hostip
, hostip6
)
433 def add_dc_msdcs_records(samdb
, forestdn
, prefix
, site
, dnsforest
, hostname
,
434 hostip
, hostip6
, domainguid
, ntdsguid
):
436 fqdn_hostname
= "%s.%s" % (hostname
, dnsforest
)
438 # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
439 forest_container_dn
= ldb
.Dn(samdb
, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
440 (dnsforest
, prefix
, forestdn
))
443 add_at_record(samdb
, forest_container_dn
, "DC=@", hostname
, dnsforest
, None, None)
445 # DC=_kerberos._tcp.dc record
446 add_srv_record(samdb
, forest_container_dn
, "DC=_kerberos._tcp.dc", fqdn_hostname
, 88)
448 # DC=_kerberos._tcp.<SITENAME>._sites.dc record
449 add_srv_record(samdb
, forest_container_dn
, "DC=_kerberos._tcp.%s._sites.dc" % site
,
452 # DC=_ldap._tcp.dc record
453 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.dc", fqdn_hostname
, 389)
455 # DC=_ldap._tcp.<SITENAME>._sites.dc record
456 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.%s._sites.dc" % site
,
459 # DC=_ldap._tcp.<SITENAME>._sites.gc record
460 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.%s._sites.gc" % site
,
463 # DC=_ldap._tcp.gc record
464 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.gc", fqdn_hostname
, 3268)
466 # DC=_ldap._tcp.pdc record
467 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.pdc", fqdn_hostname
, 389)
470 add_host_record(samdb
, forest_container_dn
, "DC=gc", hostip
, hostip6
)
472 # DC=_ldap._tcp.<DOMAINGUID>.domains record
473 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.%s.domains" % domainguid
,
477 add_cname_record(samdb
, forest_container_dn
, "DC=%s" % ntdsguid
, fqdn_hostname
)
480 def secretsdb_setup_dns(secretsdb
, names
, private_dir
, realm
,
481 dnsdomain
, dns_keytab_path
, dnspass
):
482 """Add DNS specific bits to a secrets database.
484 :param secretsdb: Ldb Handle to the secrets database
485 :param names: Names shortcut
486 :param machinepass: Machine password
489 os
.unlink(os
.path
.join(private_dir
, dns_keytab_path
))
493 setup_ldb(secretsdb
, setup_path("secrets_dns.ldif"), {
495 "DNSDOMAIN": dnsdomain
,
496 "DNS_KEYTAB": dns_keytab_path
,
497 "DNSPASS_B64": b64encode(dnspass
),
498 "HOSTNAME": names
.hostname
,
499 "DNSNAME" : '%s.%s' % (
500 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
504 def create_dns_dir(logger
, paths
):
505 """Write out a DNS zone file, from the info in the current database.
507 :param logger: Logger object
508 :param paths: paths object
510 dns_dir
= os
.path
.dirname(paths
.dns
)
513 shutil
.rmtree(dns_dir
, True)
517 os
.mkdir(dns_dir
, 0770)
519 if paths
.bind_gid
is not None:
521 os
.chown(dns_dir
, -1, paths
.bind_gid
)
522 # chmod needed to cope with umask
523 os
.chmod(dns_dir
, 0770)
525 if not os
.environ
.has_key('SAMBA_SELFTEST'):
526 logger
.error("Failed to chown %s to bind gid %u" % (
527 dns_dir
, paths
.bind_gid
))
530 def create_zone_file(lp
, logger
, paths
, targetdir
, dnsdomain
,
531 hostip
, hostip6
, hostname
, realm
, domainguid
,
533 """Write out a DNS zone file, from the info in the current database.
535 :param paths: paths object
536 :param dnsdomain: DNS Domain name
537 :param domaindn: DN of the Domain
538 :param hostip: Local IPv4 IP
539 :param hostip6: Local IPv6 IP
540 :param hostname: Local hostname
541 :param realm: Realm name
542 :param domainguid: GUID of the domain.
543 :param ntdsguid: GUID of the hosts nTDSDSA record.
545 assert isinstance(domainguid
, str)
547 if hostip6
is not None:
548 hostip6_base_line
= " IN AAAA " + hostip6
549 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
550 gc_msdcs_ip6_line
= "gc._msdcs IN AAAA " + hostip6
552 hostip6_base_line
= ""
553 hostip6_host_line
= ""
554 gc_msdcs_ip6_line
= ""
556 if hostip
is not None:
557 hostip_base_line
= " IN A " + hostip
558 hostip_host_line
= hostname
+ " IN A " + hostip
559 gc_msdcs_ip_line
= "gc._msdcs IN A " + hostip
561 hostip_base_line
= ""
562 hostip_host_line
= ""
563 gc_msdcs_ip_line
= ""
565 # we need to freeze the zone while we update the contents
566 if targetdir
is None:
567 rndc
= ' '.join(lp
.get("rndc command"))
568 os
.system(rndc
+ " freeze " + lp
.get("realm"))
570 setup_file(setup_path("provision.zone"), paths
.dns
, {
571 "HOSTNAME": hostname
,
572 "DNSDOMAIN": dnsdomain
,
574 "HOSTIP_BASE_LINE": hostip_base_line
,
575 "HOSTIP_HOST_LINE": hostip_host_line
,
576 "DOMAINGUID": domainguid
,
577 "DATESTRING": time
.strftime("%Y%m%d%H"),
579 "NTDSGUID": ntdsguid
,
580 "HOSTIP6_BASE_LINE": hostip6_base_line
,
581 "HOSTIP6_HOST_LINE": hostip6_host_line
,
582 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line
,
583 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line
,
586 if paths
.bind_gid
is not None:
588 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
589 # chmod needed to cope with umask
590 os
.chmod(paths
.dns
, 0664)
592 if not os
.environ
.has_key('SAMBA_SELFTEST'):
593 logger
.error("Failed to chown %s to bind gid %u" % (
594 paths
.dns
, paths
.bind_gid
))
596 if targetdir
is None:
597 os
.system(rndc
+ " unfreeze " + lp
.get("realm"))
600 def create_dns_update_list(lp
, logger
, paths
):
601 """Write out a dns_update_list file"""
602 # note that we use no variable substitution on this file
603 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
604 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
605 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
608 def create_named_conf(paths
, realm
, dnsdomain
, dns_backend
):
609 """Write out a file containing zone statements suitable for inclusion in a
610 named.conf file (including GSS-TSIG configuration).
612 :param paths: all paths
613 :param realm: Realm name
614 :param dnsdomain: DNS Domain name
615 :param dns_backend: DNS backend type
616 :param keytab_name: File name of DNS keytab file
619 if dns_backend
== "BIND9_FLATFILE":
620 setup_file(setup_path("named.conf"), paths
.namedconf
, {
621 "DNSDOMAIN": dnsdomain
,
623 "ZONE_FILE": paths
.dns
,
624 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
625 "NAMED_CONF": paths
.namedconf
,
626 "NAMED_CONF_UPDATE": paths
.namedconf_update
629 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
631 elif dns_backend
== "BIND9_DLZ":
632 dlz_module_path
= os
.path
.join(samba
.param
.modules_dir(),
633 "bind9/dlz_bind9.so")
634 setup_file(setup_path("named.conf.dlz"), paths
.namedconf
, {
635 "NAMED_CONF": paths
.namedconf
,
636 "BIND9_DLZ_MODULE": dlz_module_path
,
641 def create_named_txt(path
, realm
, dnsdomain
, dnsname
, private_dir
,
643 """Write out a file containing zone statements suitable for inclusion in a
644 named.conf file (including GSS-TSIG configuration).
646 :param path: Path of the new named.conf file.
647 :param realm: Realm name
648 :param dnsdomain: DNS Domain name
649 :param private_dir: Path to private directory
650 :param keytab_name: File name of DNS keytab file
652 setup_file(setup_path("named.txt"), path
, {
653 "DNSDOMAIN": dnsdomain
,
656 "DNS_KEYTAB": keytab_name
,
657 "DNS_KEYTAB_ABS": os
.path
.join(private_dir
, keytab_name
),
658 "PRIVATE_DIR": private_dir
662 def is_valid_dns_backend(dns_backend
):
663 return dns_backend
in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
666 def is_valid_os_level(os_level
):
667 return DS_DOMAIN_FUNCTION_2000
<= os_level
<= DS_DOMAIN_FUNCTION_2008_R2
670 def setup_ad_dns(samdb
, secretsdb
, names
, paths
, lp
, logger
, dns_backend
,
671 os_level
, site
, dnspass
=None, hostip
=None, hostip6
=None,
673 """Provision DNS information (assuming GC role)
675 :param samdb: LDB object connected to sam.ldb file
676 :param secretsdb: LDB object connected to secrets.ldb file
677 :param names: Names shortcut
678 :param paths: Paths shortcut
679 :param lp: Loadparm object
680 :param logger: Logger object
681 :param dns_backend: Type of DNS backend
682 :param os_level: Functional level (treated as os level)
683 :param site: Site to create hostnames in
684 :param dnspass: Password for bind's DNS account
685 :param hostip: IPv4 address
686 :param hostip6: IPv6 address
687 :param targetdir: Target directory for creating DNS-related files for BIND9
690 if not is_valid_dns_backend(dns_backend
):
691 raise Exception("Invalid dns backend: %r" % dns_backend
)
693 if not is_valid_os_level(os_level
):
694 raise Exception("Invalid os level: %r" % os_level
)
696 if dns_backend
is "NONE":
697 logger
.info("No DNS backend set, not configuring DNS")
700 # If dns_backend is BIND9_FLATFILE
701 # Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
703 # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
704 # Populate DNS partitions
706 # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
707 # All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
709 # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
710 # DS_DOMAIN_FUNCTION_2008_R2)
711 # Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
712 # Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
713 # Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
714 # Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<DOMAINDN>
716 domaindn
= names
.domaindn
717 forestdn
= samdb
.get_root_basedn().get_linearized()
719 dnsdomain
= names
.dnsdomain
.lower()
720 dnsforest
= dnsdomain
722 hostname
= names
.netbiosname
.lower()
724 domainguid
= get_domainguid(samdb
, domaindn
)
725 ntdsguid
= get_ntdsguid(samdb
, domaindn
)
727 # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
728 logger
.info("Adding DNS accounts")
729 add_dns_accounts(samdb
, domaindn
)
731 logger
.info("Populating CN=MicrosoftDNS,CN=System,%s" % domaindn
)
733 # Set up MicrosoftDNS container
734 add_dns_container(samdb
, domaindn
, "CN=System")
737 add_rootservers(samdb
, domaindn
, "CN=System")
739 if os_level
== DS_DOMAIN_FUNCTION_2000
:
742 add_domain_record(samdb
, domaindn
, "CN=System", dnsdomain
)
744 # Add DNS records for a DC in domain
745 add_dc_domain_records(samdb
, domaindn
, "CN=System", site
, dnsdomain
,
746 hostname
, hostip
, hostip6
)
748 elif dns_backend
in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
749 os_level
>= DS_DOMAIN_FUNCTION_2003
:
751 # Set up additional partitions (DomainDnsZones, ForstDnsZones)
752 logger
.info("Creating DomainDnsZones and ForestDnsZones partitions")
753 setup_dns_partitions(samdb
, domaindn
, forestdn
, names
.configdn
, names
.serverdn
)
755 ##### Set up DC=DomainDnsZones,<DOMAINDN>
756 logger
.info("Populating DomainDnsZones partition")
758 # Set up MicrosoftDNS container
759 add_dns_container(samdb
, domaindn
, "DC=DomainDnsZones")
761 # Add rootserver records
762 add_rootservers(samdb
, domaindn
, "DC=DomainDnsZones")
765 add_domain_record(samdb
, domaindn
, "DC=DomainDnsZones", dnsdomain
)
767 # Add DNS records for a DC in domain
768 add_dc_domain_records(samdb
, domaindn
, "DC=DomainDnsZones", site
, dnsdomain
,
769 hostname
, hostip
, hostip6
)
771 ##### Set up DC=ForestDnsZones,<DOMAINDN>
772 logger
.info("Populating ForestDnsZones partition")
774 # Set up MicrosoftDNS container
775 add_dns_container(samdb
, forestdn
, "DC=ForestDnsZones")
778 add_msdcs_record(samdb
, forestdn
, "DC=ForestDnsZones", dnsforest
)
780 # Add DNS records for a DC in forest
781 add_dc_msdcs_records(samdb
, forestdn
, "DC=ForestDnsZones", site
, dnsforest
,
782 hostname
, hostip
, hostip6
, domainguid
, ntdsguid
)
784 if dns_backend
.startswith("BIND9_"):
785 secretsdb_setup_dns(secretsdb
, names
,
786 paths
.private_dir
, realm
=names
.realm
,
787 dnsdomain
=names
.dnsdomain
,
788 dns_keytab_path
=paths
.dns_keytab
, dnspass
=dnspass
)
790 create_dns_dir(logger
, paths
)
792 # Only make a zone file on the first DC, it should be
793 # replicated with DNS replication
794 if dns_backend
== "BIND9_FLATFILE":
795 create_zone_file(lp
, logger
, paths
, targetdir
, site
=site
,
796 dnsdomain
=names
.dnsdomain
, hostip
=hostip
, hostip6
=hostip6
,
797 hostname
=names
.hostname
, realm
=names
.realm
,
798 domainguid
=domainguid
, ntdsguid
=names
.ntdsguid
)
800 create_named_conf(paths
, realm
=names
.realm
,
801 dnsdomain
=names
.dnsdomain
, dns_backend
=dns_backend
)
803 create_named_txt(paths
.namedtxt
,
804 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
805 dnsname
= "%s.%s" % (names
.hostname
, names
.dnsdomain
),
806 private_dir
=paths
.private_dir
,
807 keytab_name
=paths
.dns_keytab
)
808 logger
.info("See %s for an example configuration include file for BIND", paths
.namedconf
)
809 logger
.info("and %s for further documentation required for secure DNS "
810 "updates", paths
.namedtxt
)