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"""
28 from base64
import b64encode
31 from samba
.tdb_util
import tdb_copy
32 from samba
.mdb_util
import mdb_copy
33 from samba
.ndr
import ndr_pack
, ndr_unpack
34 from samba
import setup_file
35 from samba
.dcerpc
import dnsp
, misc
, security
36 from samba
.dsdb
import (
37 DS_DOMAIN_FUNCTION_2000
,
38 DS_DOMAIN_FUNCTION_2003
,
39 DS_DOMAIN_FUNCTION_2008_R2
,
40 DS_DOMAIN_FUNCTION_2012_R2
,
41 DS_DOMAIN_FUNCTION_2016
43 from samba
.descriptor
import (
44 get_domain_descriptor
,
45 get_domain_delete_protected1_descriptor
,
46 get_domain_delete_protected2_descriptor
,
47 get_dns_partition_descriptor
,
48 get_dns_forest_microsoft_dns_descriptor
,
49 get_dns_domain_microsoft_dns_descriptor
51 from samba
.provision
.common
import (
62 from samba
.samdb
import get_default_backend_store
63 from samba
.compat
import get_string
65 def get_domainguid(samdb
, domaindn
):
66 res
= samdb
.search(base
=domaindn
, scope
=ldb
.SCOPE_BASE
, attrs
=["objectGUID"])
67 domainguid
= str(ndr_unpack(misc
.GUID
, res
[0]["objectGUID"][0]))
71 def get_dnsadmins_sid(samdb
, domaindn
):
72 res
= samdb
.search(base
="CN=DnsAdmins,CN=Users,%s" % domaindn
, scope
=ldb
.SCOPE_BASE
,
74 dnsadmins_sid
= ndr_unpack(security
.dom_sid
, res
[0]["objectSid"][0])
78 class ARecord(dnsp
.DnssrvRpcRecord
):
80 def __init__(self
, ip_addr
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
81 super(ARecord
, self
).__init
__()
82 self
.wType
= dnsp
.DNS_TYPE_A
84 self
.dwSerial
= serial
85 self
.dwTtlSeconds
= ttl
89 class AAAARecord(dnsp
.DnssrvRpcRecord
):
91 def __init__(self
, ip6_addr
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
92 super(AAAARecord
, self
).__init
__()
93 self
.wType
= dnsp
.DNS_TYPE_AAAA
95 self
.dwSerial
= serial
96 self
.dwTtlSeconds
= ttl
100 class CNameRecord(dnsp
.DnssrvRpcRecord
):
102 def __init__(self
, cname
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
103 super(CNameRecord
, self
).__init
__()
104 self
.wType
= dnsp
.DNS_TYPE_CNAME
106 self
.dwSerial
= serial
107 self
.dwTtlSeconds
= ttl
111 class NSRecord(dnsp
.DnssrvRpcRecord
):
113 def __init__(self
, dns_server
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
114 super(NSRecord
, self
).__init
__()
115 self
.wType
= dnsp
.DNS_TYPE_NS
117 self
.dwSerial
= serial
118 self
.dwTtlSeconds
= ttl
119 self
.data
= dns_server
122 class SOARecord(dnsp
.DnssrvRpcRecord
):
124 def __init__(self
, mname
, rname
, serial
=1, refresh
=900, retry
=600,
125 expire
=86400, minimum
=3600, ttl
=3600, rank
=dnsp
.DNS_RANK_ZONE
):
126 super(SOARecord
, self
).__init
__()
127 self
.wType
= dnsp
.DNS_TYPE_SOA
129 self
.dwSerial
= serial
130 self
.dwTtlSeconds
= ttl
133 soa
.refresh
= refresh
138 soa
.minimum
= minimum
142 class SRVRecord(dnsp
.DnssrvRpcRecord
):
144 def __init__(self
, target
, port
, priority
=0, weight
=100, serial
=1, ttl
=900,
145 rank
=dnsp
.DNS_RANK_ZONE
):
146 super(SRVRecord
, self
).__init
__()
147 self
.wType
= dnsp
.DNS_TYPE_SRV
149 self
.dwSerial
= serial
150 self
.dwTtlSeconds
= ttl
152 srv
.nameTarget
= target
154 srv
.wPriority
= priority
159 class TXTRecord(dnsp
.DnssrvRpcRecord
):
161 def __init__(self
, slist
, serial
=1, ttl
=900, rank
=dnsp
.DNS_RANK_ZONE
):
162 super(TXTRecord
, self
).__init
__()
163 self
.wType
= dnsp
.DNS_TYPE_TXT
165 self
.dwSerial
= serial
166 self
.dwTtlSeconds
= ttl
167 stringlist
= dnsp
.string_list()
168 stringlist
.count
= len(slist
)
169 stringlist
.str = slist
170 self
.data
= stringlist
173 class TypeProperty(dnsp
.DnsProperty
):
175 def __init__(self
, zone_type
=dnsp
.DNS_ZONE_TYPE_PRIMARY
):
176 super(TypeProperty
, self
).__init
__()
179 self
.id = dnsp
.DSPROPERTY_ZONE_TYPE
180 self
.data
= zone_type
183 class AllowUpdateProperty(dnsp
.DnsProperty
):
185 def __init__(self
, allow_update
=dnsp
.DNS_ZONE_UPDATE_SECURE
):
186 super(AllowUpdateProperty
, self
).__init
__()
189 self
.id = dnsp
.DSPROPERTY_ZONE_ALLOW_UPDATE
190 self
.data
= allow_update
193 class SecureTimeProperty(dnsp
.DnsProperty
):
195 def __init__(self
, secure_time
=0):
196 super(SecureTimeProperty
, self
).__init
__()
199 self
.id = dnsp
.DSPROPERTY_ZONE_SECURE_TIME
200 self
.data
= secure_time
203 class NorefreshIntervalProperty(dnsp
.DnsProperty
):
205 def __init__(self
, norefresh_interval
=0):
206 super(NorefreshIntervalProperty
, self
).__init
__()
209 self
.id = dnsp
.DSPROPERTY_ZONE_NOREFRESH_INTERVAL
210 self
.data
= norefresh_interval
213 class RefreshIntervalProperty(dnsp
.DnsProperty
):
215 def __init__(self
, refresh_interval
=0):
216 super(RefreshIntervalProperty
, self
).__init
__()
219 self
.id = dnsp
.DSPROPERTY_ZONE_REFRESH_INTERVAL
220 self
.data
= refresh_interval
223 class AgingStateProperty(dnsp
.DnsProperty
):
225 def __init__(self
, aging_enabled
=0):
226 super(AgingStateProperty
, self
).__init
__()
229 self
.id = dnsp
.DSPROPERTY_ZONE_AGING_STATE
230 self
.data
= aging_enabled
233 class AgingEnabledTimeProperty(dnsp
.DnsProperty
):
235 def __init__(self
, next_cycle_hours
=0):
236 super(AgingEnabledTimeProperty
, self
).__init
__()
239 self
.id = dnsp
.DSPROPERTY_ZONE_AGING_ENABLED_TIME
240 self
.data
= next_cycle_hours
243 def setup_dns_partitions(samdb
, domainsid
, domaindn
, forestdn
, configdn
,
244 serverdn
, fill_level
):
245 domainzone_dn
= "DC=DomainDnsZones,%s" % domaindn
246 forestzone_dn
= "DC=ForestDnsZones,%s" % forestdn
247 descriptor
= get_dns_partition_descriptor(domainsid
)
249 setup_add_ldif(samdb
, setup_path("provision_dnszones_partitions.ldif"), {
250 "ZONE_DN": domainzone_dn
,
251 "SECDESC": b64encode(descriptor
).decode('utf8')
253 if fill_level
!= FILL_SUBDOMAIN
:
254 setup_add_ldif(samdb
, setup_path("provision_dnszones_partitions.ldif"), {
255 "ZONE_DN": forestzone_dn
,
256 "SECDESC": b64encode(descriptor
).decode('utf8')
259 domainzone_guid
= get_domainguid(samdb
, domainzone_dn
)
260 domainzone_guid
= str(uuid
.uuid4())
261 domainzone_dns
= ldb
.Dn(samdb
, domainzone_dn
).canonical_ex_str().strip()
263 protected1_desc
= get_domain_delete_protected1_descriptor(domainsid
)
264 protected2_desc
= get_domain_delete_protected2_descriptor(domainsid
)
265 setup_add_ldif(samdb
, setup_path("provision_dnszones_add.ldif"), {
266 "ZONE_DN": domainzone_dn
,
267 "ZONE_GUID": domainzone_guid
,
268 "ZONE_DNS": domainzone_dns
,
269 "CONFIGDN": configdn
,
270 "SERVERDN": serverdn
,
271 "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc
).decode('utf8'),
272 "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc
).decode('utf8'),
274 setup_modify_ldif(samdb
, setup_path("provision_dnszones_modify.ldif"), {
275 "CONFIGDN": configdn
,
276 "SERVERDN": serverdn
,
277 "ZONE_DN": domainzone_dn
,
280 if fill_level
!= FILL_SUBDOMAIN
:
281 forestzone_guid
= get_domainguid(samdb
, forestzone_dn
)
282 forestzone_guid
= str(uuid
.uuid4())
283 forestzone_dns
= ldb
.Dn(samdb
, forestzone_dn
).canonical_ex_str().strip()
285 setup_add_ldif(samdb
, setup_path("provision_dnszones_add.ldif"), {
286 "ZONE_DN": forestzone_dn
,
287 "ZONE_GUID": forestzone_guid
,
288 "ZONE_DNS": forestzone_dns
,
289 "CONFIGDN": configdn
,
290 "SERVERDN": serverdn
,
291 "LOSTANDFOUND_DESCRIPTOR": b64encode(protected2_desc
).decode('utf8'),
292 "INFRASTRUCTURE_DESCRIPTOR": b64encode(protected1_desc
).decode('utf8'),
294 setup_modify_ldif(samdb
, setup_path("provision_dnszones_modify.ldif"), {
295 "CONFIGDN": configdn
,
296 "SERVERDN": serverdn
,
297 "ZONE_DN": forestzone_dn
,
301 def add_dns_accounts(samdb
, domaindn
):
302 setup_add_ldif(samdb
, setup_path("provision_dns_accounts_add.ldif"), {
303 "DOMAINDN": domaindn
,
307 def add_dns_container(samdb
, domaindn
, prefix
, domain_sid
, dnsadmins_sid
, forest
=False):
308 name_map
= {'DnsAdmins': str(dnsadmins_sid
)}
310 sd_val
= get_dns_forest_microsoft_dns_descriptor(domain_sid
,
313 sd_val
= get_dns_domain_microsoft_dns_descriptor(domain_sid
,
315 # CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
316 msg
= ldb
.Message(ldb
.Dn(samdb
, "CN=MicrosoftDNS,%s,%s" % (prefix
, domaindn
)))
317 msg
["objectClass"] = ["top", "container"]
318 msg
["nTSecurityDescriptor"] = \
319 ldb
.MessageElement(sd_val
, ldb
.FLAG_MOD_ADD
,
320 "nTSecurityDescriptor")
324 def add_rootservers(samdb
, domaindn
, prefix
):
325 # https://www.internic.net/zones/named.root
327 rootservers
["a.root-servers.net"] = "198.41.0.4"
328 rootservers
["b.root-servers.net"] = "192.228.79.201"
329 rootservers
["c.root-servers.net"] = "192.33.4.12"
330 rootservers
["d.root-servers.net"] = "199.7.91.13"
331 rootservers
["e.root-servers.net"] = "192.203.230.10"
332 rootservers
["f.root-servers.net"] = "192.5.5.241"
333 rootservers
["g.root-servers.net"] = "192.112.36.4"
334 rootservers
["h.root-servers.net"] = "198.97.190.53"
335 rootservers
["i.root-servers.net"] = "192.36.148.17"
336 rootservers
["j.root-servers.net"] = "192.58.128.30"
337 rootservers
["k.root-servers.net"] = "193.0.14.129"
338 rootservers
["l.root-servers.net"] = "199.7.83.42"
339 rootservers
["m.root-servers.net"] = "202.12.27.33"
342 rootservers_v6
["a.root-servers.net"] = "2001:503:ba3e::2:30"
343 rootservers_v6
["b.root-servers.net"] = "2001:500:84::b"
344 rootservers_v6
["c.root-servers.net"] = "2001:500:2::c"
345 rootservers_v6
["d.root-servers.net"] = "2001:500:2d::d"
346 rootservers_v6
["e.root-servers.net"] = "2001:500:a8::e"
347 rootservers_v6
["f.root-servers.net"] = "2001:500:2f::f"
348 rootservers_v6
["g.root-servers.net"] = "2001:500:12::d0d"
349 rootservers_v6
["h.root-servers.net"] = "2001:500:1::53"
350 rootservers_v6
["i.root-servers.net"] = "2001:7fe::53"
351 rootservers_v6
["j.root-servers.net"] = "2001:503:c27::2:30"
352 rootservers_v6
["k.root-servers.net"] = "2001:7fd::1"
353 rootservers_v6
["l.root-servers.net"] = "2001:500:9f::42"
354 rootservers_v6
["m.root-servers.net"] = "2001:dc3::35"
356 container_dn
= "DC=RootDNSServers,CN=MicrosoftDNS,%s,%s" % (prefix
, domaindn
)
358 # Add DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
359 msg
= ldb
.Message(ldb
.Dn(samdb
, container_dn
))
361 props
.append(ndr_pack(TypeProperty(zone_type
=dnsp
.DNS_ZONE_TYPE_CACHE
)))
362 props
.append(ndr_pack(AllowUpdateProperty(allow_update
=dnsp
.DNS_ZONE_UPDATE_OFF
)))
363 props
.append(ndr_pack(SecureTimeProperty()))
364 props
.append(ndr_pack(NorefreshIntervalProperty()))
365 props
.append(ndr_pack(RefreshIntervalProperty()))
366 props
.append(ndr_pack(AgingStateProperty()))
367 props
.append(ndr_pack(AgingEnabledTimeProperty()))
368 msg
["objectClass"] = ["top", "dnsZone"]
369 msg
["cn"] = ldb
.MessageElement("Zone", ldb
.FLAG_MOD_ADD
, "cn")
370 msg
["dNSProperty"] = ldb
.MessageElement(props
, ldb
.FLAG_MOD_ADD
, "dNSProperty")
373 # Add DC=@,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
375 for rserver
in rootservers
:
376 record
.append(ndr_pack(NSRecord(rserver
, serial
=0, ttl
=0, rank
=dnsp
.DNS_RANK_ROOT_HINT
)))
378 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=@,%s" % container_dn
))
379 msg
["objectClass"] = ["top", "dnsNode"]
380 msg
["dnsRecord"] = ldb
.MessageElement(record
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
383 # Add DC=<rootserver>,DC=RootDNSServers,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
384 for rserver
in rootservers
:
385 record
= [ndr_pack(ARecord(rootservers
[rserver
], serial
=0, ttl
=0, rank
=dnsp
.DNS_RANK_ROOT_HINT
))]
386 # Add AAAA record as well (How does W2K* add IPv6 records?)
387 # if rserver in rootservers_v6:
388 # record.append(ndr_pack(AAAARecord(rootservers_v6[rserver], serial=0, ttl=0)))
389 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=%s,%s" % (rserver
, container_dn
)))
390 msg
["objectClass"] = ["top", "dnsNode"]
391 msg
["dnsRecord"] = ldb
.MessageElement(record
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
395 def add_at_record(samdb
, container_dn
, prefix
, hostname
, dnsdomain
, hostip
, hostip6
):
397 fqdn_hostname
= "%s.%s" % (hostname
, dnsdomain
)
402 at_soa_record
= SOARecord(fqdn_hostname
, "hostmaster.%s" % dnsdomain
)
403 at_records
.append(ndr_pack(at_soa_record
))
406 at_ns_record
= NSRecord(fqdn_hostname
)
407 at_records
.append(ndr_pack(at_ns_record
))
409 if hostip
is not None:
411 at_a_record
= ARecord(hostip
)
412 at_records
.append(ndr_pack(at_a_record
))
414 if hostip6
is not None:
416 at_aaaa_record
= AAAARecord(hostip6
)
417 at_records
.append(ndr_pack(at_aaaa_record
))
419 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=@,%s" % container_dn
))
420 msg
["objectClass"] = ["top", "dnsNode"]
421 msg
["dnsRecord"] = ldb
.MessageElement(at_records
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
425 def add_srv_record(samdb
, container_dn
, prefix
, host
, port
):
426 srv_record
= SRVRecord(host
, port
)
427 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
428 msg
["objectClass"] = ["top", "dnsNode"]
429 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(srv_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
433 def add_ns_record(samdb
, container_dn
, prefix
, host
):
434 ns_record
= NSRecord(host
)
435 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
436 msg
["objectClass"] = ["top", "dnsNode"]
437 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(ns_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
441 def add_ns_glue_record(samdb
, container_dn
, prefix
, host
):
442 ns_record
= NSRecord(host
, rank
=dnsp
.DNS_RANK_NS_GLUE
)
443 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
444 msg
["objectClass"] = ["top", "dnsNode"]
445 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(ns_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
449 def add_cname_record(samdb
, container_dn
, prefix
, host
):
450 cname_record
= CNameRecord(host
)
451 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
452 msg
["objectClass"] = ["top", "dnsNode"]
453 msg
["dnsRecord"] = ldb
.MessageElement(ndr_pack(cname_record
), ldb
.FLAG_MOD_ADD
, "dnsRecord")
457 def add_host_record(samdb
, container_dn
, prefix
, hostip
, hostip6
):
460 a_record
= ARecord(hostip
)
461 host_records
.append(ndr_pack(a_record
))
463 aaaa_record
= AAAARecord(hostip6
)
464 host_records
.append(ndr_pack(aaaa_record
))
466 msg
= ldb
.Message(ldb
.Dn(samdb
, "%s,%s" % (prefix
, container_dn
)))
467 msg
["objectClass"] = ["top", "dnsNode"]
468 msg
["dnsRecord"] = ldb
.MessageElement(host_records
, ldb
.FLAG_MOD_ADD
, "dnsRecord")
472 def add_domain_record(samdb
, domaindn
, prefix
, dnsdomain
, domainsid
, dnsadmins_sid
):
473 # DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
474 sddl
= "O:SYG:BAD:AI" \
475 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)" \
477 "(A;;RPLCLORC;;;WD)" \
478 "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
479 "(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
480 "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)" \
481 "(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)" \
482 "(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
483 "(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
485 "(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
486 "S:AI" % dnsadmins_sid
487 sec
= security
.descriptor
.from_sddl(sddl
, domainsid
)
489 props
.append(ndr_pack(TypeProperty()))
490 props
.append(ndr_pack(AllowUpdateProperty()))
491 props
.append(ndr_pack(SecureTimeProperty()))
492 props
.append(ndr_pack(NorefreshIntervalProperty(norefresh_interval
=168)))
493 props
.append(ndr_pack(RefreshIntervalProperty(refresh_interval
=168)))
494 props
.append(ndr_pack(AgingStateProperty()))
495 props
.append(ndr_pack(AgingEnabledTimeProperty()))
496 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=%s,CN=MicrosoftDNS,%s,%s" % (dnsdomain
, prefix
, domaindn
)))
497 msg
["objectClass"] = ["top", "dnsZone"]
498 msg
["ntSecurityDescriptor"] = \
499 ldb
.MessageElement(ndr_pack(sec
),
501 "nTSecurityDescriptor")
502 msg
["dNSProperty"] = ldb
.MessageElement(props
, ldb
.FLAG_MOD_ADD
, "dNSProperty")
506 def add_msdcs_record(samdb
, forestdn
, prefix
, dnsforest
):
507 # DC=_msdcs.<DNSFOREST>,CN=MicrosoftDNS,<PREFIX>,<FORESTDN>
508 msg
= ldb
.Message(ldb
.Dn(samdb
, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
509 (dnsforest
, prefix
, forestdn
)))
510 msg
["objectClass"] = ["top", "dnsZone"]
514 def add_dc_domain_records(samdb
, domaindn
, prefix
, site
, dnsdomain
, hostname
,
517 fqdn_hostname
= "%s.%s" % (hostname
, dnsdomain
)
519 # Set up domain container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
520 domain_container_dn
= ldb
.Dn(samdb
, "DC=%s,CN=MicrosoftDNS,%s,%s" %
521 (dnsdomain
, prefix
, domaindn
))
524 add_at_record(samdb
, domain_container_dn
, "DC=@", hostname
, dnsdomain
,
527 # DC=<HOSTNAME> record
528 add_host_record(samdb
, domain_container_dn
, "DC=%s" % hostname
, hostip
,
531 # DC=_kerberos._tcp record
532 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._tcp",
535 # DC=_kerberos._tcp.<SITENAME>._sites record
536 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._tcp.%s._sites" %
537 site
, fqdn_hostname
, 88)
539 # DC=_kerberos._udp record
540 add_srv_record(samdb
, domain_container_dn
, "DC=_kerberos._udp",
543 # DC=_kpasswd._tcp record
544 add_srv_record(samdb
, domain_container_dn
, "DC=_kpasswd._tcp",
547 # DC=_kpasswd._udp record
548 add_srv_record(samdb
, domain_container_dn
, "DC=_kpasswd._udp",
551 # DC=_ldap._tcp record
552 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp", fqdn_hostname
,
555 # DC=_ldap._tcp.<SITENAME>._sites record
556 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.%s._sites" %
557 site
, fqdn_hostname
, 389)
559 # FIXME: The number of SRV records depend on the various roles this DC has.
560 # _gc and _msdcs records are added if the we are the forest dc and not subdomain dc
562 # Assumption: current DC is GC and add all the entries
565 add_srv_record(samdb
, domain_container_dn
, "DC=_gc._tcp", fqdn_hostname
,
568 # DC=_gc._tcp.<SITENAME>,_sites record
569 add_srv_record(samdb
, domain_container_dn
, "DC=_gc._tcp.%s._sites" % site
,
573 add_ns_glue_record(samdb
, domain_container_dn
, "DC=_msdcs", fqdn_hostname
)
575 # FIXME: Following entries are added only if DomainDnsZones and ForestDnsZones partitions
578 # Assumption: Additional entries won't hurt on os_level = 2000
580 # DC=_ldap._tcp.<SITENAME>._sites.DomainDnsZones
581 add_srv_record(samdb
, domain_container_dn
,
582 "DC=_ldap._tcp.%s._sites.DomainDnsZones" % site
, fqdn_hostname
,
585 # DC=_ldap._tcp.<SITENAME>._sites.ForestDnsZones
586 add_srv_record(samdb
, domain_container_dn
,
587 "DC=_ldap._tcp.%s._sites.ForestDnsZones" % site
, fqdn_hostname
,
590 # DC=_ldap._tcp.DomainDnsZones
591 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.DomainDnsZones",
594 # DC=_ldap._tcp.ForestDnsZones
595 add_srv_record(samdb
, domain_container_dn
, "DC=_ldap._tcp.ForestDnsZones",
599 add_host_record(samdb
, domain_container_dn
, "DC=DomainDnsZones", hostip
,
603 add_host_record(samdb
, domain_container_dn
, "DC=ForestDnsZones", hostip
,
607 def add_dc_msdcs_records(samdb
, forestdn
, prefix
, site
, dnsforest
, hostname
,
608 hostip
, hostip6
, domainguid
, ntdsguid
):
610 fqdn_hostname
= "%s.%s" % (hostname
, dnsforest
)
612 # Set up forest container - DC=<DNSDOMAIN>,CN=MicrosoftDNS,<PREFIX>,<DOMAINDN>
613 forest_container_dn
= ldb
.Dn(samdb
, "DC=_msdcs.%s,CN=MicrosoftDNS,%s,%s" %
614 (dnsforest
, prefix
, forestdn
))
617 add_at_record(samdb
, forest_container_dn
, "DC=@", hostname
, dnsforest
,
620 # DC=_kerberos._tcp.dc record
621 add_srv_record(samdb
, forest_container_dn
, "DC=_kerberos._tcp.dc",
624 # DC=_kerberos._tcp.<SITENAME>._sites.dc record
625 add_srv_record(samdb
, forest_container_dn
,
626 "DC=_kerberos._tcp.%s._sites.dc" % site
, fqdn_hostname
, 88)
628 # DC=_ldap._tcp.dc record
629 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.dc",
632 # DC=_ldap._tcp.<SITENAME>._sites.dc record
633 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.%s._sites.dc" %
634 site
, fqdn_hostname
, 389)
636 # DC=_ldap._tcp.<SITENAME>._sites.gc record
637 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.%s._sites.gc" %
638 site
, fqdn_hostname
, 3268)
640 # DC=_ldap._tcp.gc record
641 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.gc",
644 # DC=_ldap._tcp.pdc record
645 add_srv_record(samdb
, forest_container_dn
, "DC=_ldap._tcp.pdc",
649 add_host_record(samdb
, forest_container_dn
, "DC=gc", hostip
, hostip6
)
651 # DC=_ldap._tcp.<DOMAINGUID>.domains record
652 add_srv_record(samdb
, forest_container_dn
,
653 "DC=_ldap._tcp.%s.domains" % domainguid
, fqdn_hostname
, 389)
656 add_cname_record(samdb
, forest_container_dn
, "DC=%s" % ntdsguid
,
660 def secretsdb_setup_dns(secretsdb
, names
, private_dir
, binddns_dir
, realm
,
661 dnsdomain
, dns_keytab_path
, dnspass
, key_version_number
):
662 """Add DNS specific bits to a secrets database.
664 :param secretsdb: Ldb Handle to the secrets database
665 :param names: Names shortcut
666 :param machinepass: Machine password
669 os
.unlink(os
.path
.join(private_dir
, dns_keytab_path
))
670 os
.unlink(os
.path
.join(binddns_dir
, dns_keytab_path
))
674 if key_version_number
is None:
675 key_version_number
= 1
677 # This will create the dns.keytab file in the private_dir when it is
679 setup_ldb(secretsdb
, setup_path("secrets_dns.ldif"), {
681 "DNSDOMAIN": dnsdomain
,
682 "DNS_KEYTAB": dns_keytab_path
,
683 "DNSPASS_B64": b64encode(dnspass
.encode('utf-8')).decode('utf8'),
684 "KEY_VERSION_NUMBER": str(key_version_number
),
685 "HOSTNAME": names
.hostname
,
686 "DNSNAME": '%s.%s' % (
687 names
.netbiosname
.lower(), names
.dnsdomain
.lower())
691 def create_dns_dir(logger
, paths
):
692 """Write out a DNS zone file, from the info in the current database.
694 :param logger: Logger object
695 :param paths: paths object
697 dns_dir
= os
.path
.dirname(paths
.dns
)
700 shutil
.rmtree(dns_dir
, True)
704 os
.mkdir(dns_dir
, 0o770)
706 if paths
.bind_gid
is not None:
708 os
.chown(dns_dir
, -1, paths
.bind_gid
)
709 # chmod needed to cope with umask
710 os
.chmod(dns_dir
, 0o770)
712 if 'SAMBA_SELFTEST' not in os
.environ
:
713 logger
.error("Failed to chown %s to bind gid %u" % (
714 dns_dir
, paths
.bind_gid
))
717 def create_zone_file(lp
, logger
, paths
, targetdir
, dnsdomain
,
718 hostip
, hostip6
, hostname
, realm
, domainguid
,
720 """Write out a DNS zone file, from the info in the current database.
722 :param paths: paths object
723 :param dnsdomain: DNS Domain name
724 :param domaindn: DN of the Domain
725 :param hostip: Local IPv4 IP
726 :param hostip6: Local IPv6 IP
727 :param hostname: Local hostname
728 :param realm: Realm name
729 :param domainguid: GUID of the domain.
730 :param ntdsguid: GUID of the hosts nTDSDSA record.
732 assert isinstance(domainguid
, str)
734 if hostip6
is not None:
735 hostip6_base_line
= " IN AAAA " + hostip6
736 hostip6_host_line
= hostname
+ " IN AAAA " + hostip6
737 gc_msdcs_ip6_line
= "gc._msdcs IN AAAA " + hostip6
739 hostip6_base_line
= ""
740 hostip6_host_line
= ""
741 gc_msdcs_ip6_line
= ""
743 if hostip
is not None:
744 hostip_base_line
= " IN A " + hostip
745 hostip_host_line
= hostname
+ " IN A " + hostip
746 gc_msdcs_ip_line
= "gc._msdcs IN A " + hostip
748 hostip_base_line
= ""
749 hostip_host_line
= ""
750 gc_msdcs_ip_line
= ""
752 setup_file(setup_path("provision.zone"), paths
.dns
, {
753 "HOSTNAME": hostname
,
754 "DNSDOMAIN": dnsdomain
,
756 "HOSTIP_BASE_LINE": hostip_base_line
,
757 "HOSTIP_HOST_LINE": hostip_host_line
,
758 "DOMAINGUID": domainguid
,
759 "DATESTRING": time
.strftime("%Y%m%d%H"),
761 "NTDSGUID": ntdsguid
,
762 "HOSTIP6_BASE_LINE": hostip6_base_line
,
763 "HOSTIP6_HOST_LINE": hostip6_host_line
,
764 "GC_MSDCS_IP_LINE": gc_msdcs_ip_line
,
765 "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line
,
768 if paths
.bind_gid
is not None:
770 os
.chown(paths
.dns
, -1, paths
.bind_gid
)
771 # chmod needed to cope with umask
772 os
.chmod(paths
.dns
, 0o664)
774 if 'SAMBA_SELFTEST' not in os
.environ
:
775 logger
.error("Failed to chown %s to bind gid %u" % (
776 paths
.dns
, paths
.bind_gid
))
779 def create_samdb_copy(samdb
, logger
, paths
, names
, domainsid
, domainguid
):
780 """Create a copy of samdb and give write permissions to named for dns partitions
782 private_dir
= paths
.private_dir
783 samldb_dir
= os
.path
.join(private_dir
, "sam.ldb.d")
784 dns_dir
= os
.path
.dirname(paths
.dns
)
785 dns_samldb_dir
= os
.path
.join(dns_dir
, "sam.ldb.d")
787 # Find the partitions and corresponding filenames
789 res
= samdb
.search(base
="@PARTITION",
790 scope
=ldb
.SCOPE_BASE
,
791 attrs
=["partition", "backendStore"])
792 for tmp
in res
[0]["partition"]:
793 (nc
, fname
) = str(tmp
).split(':')
794 partfile
[nc
.upper()] = fname
796 backend_store
= get_default_backend_store()
797 if "backendStore" in res
[0]:
798 backend_store
= str(res
[0]["backendStore"][0])
800 # Create empty domain partition
802 domaindn
= names
.domaindn
.upper()
803 domainpart_file
= os
.path
.join(dns_dir
, partfile
[domaindn
])
805 os
.mkdir(dns_samldb_dir
)
806 open(domainpart_file
, 'w').close()
808 # Fill the basedn and @OPTION records in domain partition
809 dom_url
= "%s://%s" % (backend_store
, domainpart_file
)
810 dom_ldb
= samba
.Ldb(dom_url
)
812 # We need the dummy main-domain DB to have the correct @INDEXLIST
813 index_res
= samdb
.search(base
="@INDEXLIST", scope
=ldb
.SCOPE_BASE
)
814 dom_ldb
.add(index_res
[0])
816 domainguid_line
= "objectGUID: %s\n-" % domainguid
817 descr
= b64encode(get_domain_descriptor(domainsid
)).decode('utf8')
818 setup_add_ldif(dom_ldb
, setup_path("provision_basedn.ldif"), {
819 "DOMAINDN": names
.domaindn
,
820 "DOMAINGUID": domainguid_line
,
821 "DOMAINSID": str(domainsid
),
822 "DESCRIPTOR": descr
})
823 setup_add_ldif(dom_ldb
,
824 setup_path("provision_basedn_options.ldif"), None)
828 "Failed to setup database for BIND, AD based DNS cannot be used")
831 # This line is critical to the security of the whole scheme.
832 # We assume there is no secret data in the (to be left out of
833 # date and essentially read-only) config, schema and metadata partitions.
835 # Only the stub of the domain partition is created above.
837 # That way, things like the krbtgt key do not leak.
838 del partfile
[domaindn
]
840 # Link dns partitions and metadata
841 domainzonedn
= "DC=DOMAINDNSZONES,%s" % names
.domaindn
.upper()
842 forestzonedn
= "DC=FORESTDNSZONES,%s" % names
.rootdn
.upper()
844 domainzone_file
= partfile
[domainzonedn
]
845 forestzone_file
= partfile
.get(forestzonedn
)
847 metadata_file
= "metadata.tdb"
849 os
.link(os
.path
.join(samldb_dir
, metadata_file
),
850 os
.path
.join(dns_samldb_dir
, metadata_file
))
851 os
.link(os
.path
.join(private_dir
, domainzone_file
),
852 os
.path
.join(dns_dir
, domainzone_file
))
853 if backend_store
== "mdb":
854 # If the file is an lmdb data file need to link the
856 os
.link(os
.path
.join(private_dir
, domainzone_file
+ "-lock"),
857 os
.path
.join(dns_dir
, domainzone_file
+ "-lock"))
859 os
.link(os
.path
.join(private_dir
, forestzone_file
),
860 os
.path
.join(dns_dir
, forestzone_file
))
861 if backend_store
== "mdb":
862 # If the database file is an lmdb data file need to link the
864 os
.link(os
.path
.join(private_dir
, forestzone_file
+ "-lock"),
865 os
.path
.join(dns_dir
, forestzone_file
+ "-lock"))
868 "Failed to setup database for BIND, AD based DNS cannot be used")
870 del partfile
[domainzonedn
]
872 del partfile
[forestzonedn
]
874 # Copy root, config, schema partitions (and any other if any)
875 # Since samdb is open in the current process, copy them in a child process
877 tdb_copy(os
.path
.join(private_dir
, "sam.ldb"),
878 os
.path
.join(dns_dir
, "sam.ldb"))
881 if backend_store
== "mdb":
882 mdb_copy(os
.path
.join(private_dir
, pfile
),
883 os
.path
.join(dns_dir
, pfile
))
885 tdb_copy(os
.path
.join(private_dir
, pfile
),
886 os
.path
.join(dns_dir
, pfile
))
889 "Failed to setup database for BIND, AD based DNS cannot be used")
892 # Give bind read/write permissions dns partitions
893 if paths
.bind_gid
is not None:
895 for dirname
, dirs
, files
in os
.walk(dns_dir
):
897 dpath
= os
.path
.join(dirname
, d
)
898 os
.chown(dpath
, -1, paths
.bind_gid
)
899 os
.chmod(dpath
, 0o770)
901 if f
.endswith(('.ldb', '.tdb', 'ldb-lock')):
902 fpath
= os
.path
.join(dirname
, f
)
903 os
.chown(fpath
, -1, paths
.bind_gid
)
904 os
.chmod(fpath
, 0o660)
906 if 'SAMBA_SELFTEST' not in os
.environ
:
908 "Failed to set permissions to sam.ldb* files, fix manually")
910 if 'SAMBA_SELFTEST' not in os
.environ
:
911 logger
.warning("""Unable to find group id for BIND,
912 set permissions to sam.ldb* files manually""")
915 def create_dns_update_list(lp
, logger
, paths
):
916 """Write out a dns_update_list file"""
917 # note that we use no variable substitution on this file
918 # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
919 setup_file(setup_path("dns_update_list"), paths
.dns_update_list
, None)
920 setup_file(setup_path("spn_update_list"), paths
.spn_update_list
, None)
923 def create_named_conf(paths
, realm
, dnsdomain
, dns_backend
, logger
):
924 """Write out a file containing zone statements suitable for inclusion in a
925 named.conf file (including GSS-TSIG configuration).
927 :param paths: all paths
928 :param realm: Realm name
929 :param dnsdomain: DNS Domain name
930 :param dns_backend: DNS backend type
931 :param keytab_name: File name of DNS keytab file
932 :param logger: Logger object
935 # TODO: This really should have been done as a top level import.
936 # It is done here to avoid a depencency loop. That is, we move
937 # ProvisioningError to another file, and have all the provision
938 # scripts import it from there.
940 from samba
.provision
import ProvisioningError
942 if dns_backend
== "BIND9_FLATFILE":
943 setup_file(setup_path("named.conf"), paths
.namedconf
, {
944 "DNSDOMAIN": dnsdomain
,
946 "ZONE_FILE": paths
.dns
,
947 "REALM_WC": "*." + ".".join(realm
.split(".")[1:]),
948 "NAMED_CONF": paths
.namedconf
,
949 "NAMED_CONF_UPDATE": paths
.namedconf_update
952 setup_file(setup_path("named.conf.update"), paths
.namedconf_update
)
954 elif dns_backend
== "BIND9_DLZ":
955 bind_info
= subprocess
.Popen(['named -V'], shell
=True,
956 stdout
=subprocess
.PIPE
,
957 stderr
=subprocess
.STDOUT
,
958 cwd
='.').communicate()[0]
959 bind_info
= get_string(bind_info
)
967 if bind_info
.upper().find('BIND 9.8') != -1:
969 elif bind_info
.upper().find('BIND 9.9') != -1:
971 elif bind_info
.upper().find('BIND 9.10') != -1:
973 elif bind_info
.upper().find('BIND 9.11') != -1:
975 elif bind_info
.upper().find('BIND 9.12') != -1:
977 elif bind_info
.upper().find('BIND 9.14') != -1:
979 elif bind_info
.upper().find('BIND 9.16') != -1:
981 elif bind_info
.upper().find('BIND 9.7') != -1:
982 raise ProvisioningError("DLZ option incompatible with BIND 9.7.")
983 elif bind_info
.upper().find('BIND_9.13') != -1:
984 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
985 elif bind_info
.upper().find('BIND_9.15') != -1:
986 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
987 elif bind_info
.upper().find('BIND_9.17') != -1:
988 raise ProvisioningError("Only stable/esv releases of BIND are supported.")
990 logger
.warning("BIND version unknown, please modify %s manually." % paths
.namedconf
)
991 setup_file(setup_path("named.conf.dlz"), paths
.namedconf
, {
992 "NAMED_CONF": paths
.namedconf
,
993 "MODULESDIR": samba
.param
.modules_dir(),
996 "BIND9_10": bind9_10
,
997 "BIND9_11": bind9_11
,
998 "BIND9_12": bind9_12
,
999 "BIND9_14": bind9_14
,
1000 "BIND9_16": bind9_16
1004 def create_named_txt(path
, realm
, dnsdomain
, dnsname
, binddns_dir
,
1006 """Write out a file containing zone statements suitable for inclusion in a
1007 named.conf file (including GSS-TSIG configuration).
1009 :param path: Path of the new named.conf file.
1010 :param realm: Realm name
1011 :param dnsdomain: DNS Domain name
1012 :param binddns_dir: Path to bind dns directory
1013 :param keytab_name: File name of DNS keytab file
1015 setup_file(setup_path("named.txt"), path
, {
1016 "DNSDOMAIN": dnsdomain
,
1019 "DNS_KEYTAB": keytab_name
,
1020 "DNS_KEYTAB_ABS": os
.path
.join(binddns_dir
, keytab_name
),
1021 "PRIVATE_DIR": binddns_dir
1025 def is_valid_dns_backend(dns_backend
):
1026 return dns_backend
in ("BIND9_FLATFILE", "BIND9_DLZ", "SAMBA_INTERNAL", "NONE")
1029 def is_valid_os_level(os_level
):
1030 return DS_DOMAIN_FUNCTION_2000
<= os_level
<= DS_DOMAIN_FUNCTION_2016
1033 def create_dns_legacy(samdb
, domainsid
, forestdn
, dnsadmins_sid
):
1034 # Set up MicrosoftDNS container
1035 add_dns_container(samdb
, forestdn
, "CN=System", domainsid
, dnsadmins_sid
)
1037 add_rootservers(samdb
, forestdn
, "CN=System")
1040 def fill_dns_data_legacy(samdb
, domainsid
, forestdn
, dnsdomain
, site
, hostname
,
1041 hostip
, hostip6
, dnsadmins_sid
):
1043 add_domain_record(samdb
, forestdn
, "CN=System", dnsdomain
, domainsid
,
1046 # Add DNS records for a DC in domain
1047 add_dc_domain_records(samdb
, forestdn
, "CN=System", site
, dnsdomain
,
1048 hostname
, hostip
, hostip6
)
1051 def create_dns_partitions(samdb
, domainsid
, names
, domaindn
, forestdn
,
1052 dnsadmins_sid
, fill_level
):
1053 # Set up additional partitions (DomainDnsZones, ForstDnsZones)
1054 setup_dns_partitions(samdb
, domainsid
, domaindn
, forestdn
,
1055 names
.configdn
, names
.serverdn
, fill_level
)
1057 # Set up MicrosoftDNS containers
1058 add_dns_container(samdb
, domaindn
, "DC=DomainDnsZones", domainsid
,
1060 if fill_level
!= FILL_SUBDOMAIN
:
1061 add_dns_container(samdb
, forestdn
, "DC=ForestDnsZones", domainsid
,
1062 dnsadmins_sid
, forest
=True)
1065 def fill_dns_data_partitions(samdb
, domainsid
, site
, domaindn
, forestdn
,
1066 dnsdomain
, dnsforest
, hostname
, hostip
, hostip6
,
1067 domainguid
, ntdsguid
, dnsadmins_sid
, autofill
=True,
1068 fill_level
=FILL_FULL
, add_root
=True):
1069 """Fill data in various AD partitions
1071 :param samdb: LDB object connected to sam.ldb file
1072 :param domainsid: Domain SID (as dom_sid object)
1073 :param site: Site name to create hostnames in
1074 :param domaindn: DN of the domain
1075 :param forestdn: DN of the forest
1076 :param dnsdomain: DNS name of the domain
1077 :param dnsforest: DNS name of the forest
1078 :param hostname: Host name of this DC
1079 :param hostip: IPv4 addresses
1080 :param hostip6: IPv6 addresses
1081 :param domainguid: Domain GUID
1082 :param ntdsguid: NTDS GUID
1083 :param dnsadmins_sid: SID for DnsAdmins group
1084 :param autofill: Create DNS records (using fixed template)
1087 # Set up DC=DomainDnsZones,<DOMAINDN>
1088 # Add rootserver records
1090 add_rootservers(samdb
, domaindn
, "DC=DomainDnsZones")
1093 add_domain_record(samdb
, domaindn
, "DC=DomainDnsZones", dnsdomain
,
1094 domainsid
, dnsadmins_sid
)
1096 # Add DNS records for a DC in domain
1098 add_dc_domain_records(samdb
, domaindn
, "DC=DomainDnsZones", site
,
1099 dnsdomain
, hostname
, hostip
, hostip6
)
1101 if fill_level
!= FILL_SUBDOMAIN
:
1102 # Set up DC=ForestDnsZones,<FORESTDN>
1104 add_msdcs_record(samdb
, forestdn
, "DC=ForestDnsZones", dnsforest
)
1106 # Add DNS records for a DC in forest
1108 add_dc_msdcs_records(samdb
, forestdn
, "DC=ForestDnsZones", site
,
1109 dnsforest
, hostname
, hostip
, hostip6
,
1110 domainguid
, ntdsguid
)
1113 def setup_ad_dns(samdb
, secretsdb
, names
, paths
, lp
, logger
,
1114 dns_backend
, os_level
, dnspass
=None, hostip
=None, hostip6
=None,
1115 targetdir
=None, fill_level
=FILL_FULL
, backend_store
=None):
1116 """Provision DNS information (assuming GC role)
1118 :param samdb: LDB object connected to sam.ldb file
1119 :param secretsdb: LDB object connected to secrets.ldb file
1120 :param names: Names shortcut
1121 :param paths: Paths shortcut
1122 :param lp: Loadparm object
1123 :param logger: Logger object
1124 :param dns_backend: Type of DNS backend
1125 :param os_level: Functional level (treated as os level)
1126 :param dnspass: Password for bind's DNS account
1127 :param hostip: IPv4 address
1128 :param hostip6: IPv6 address
1129 :param targetdir: Target directory for creating DNS-related files for BIND9
1132 if not is_valid_dns_backend(dns_backend
):
1133 raise Exception("Invalid dns backend: %r" % dns_backend
)
1135 if not is_valid_os_level(os_level
):
1136 raise Exception("Invalid os level: %r" % os_level
)
1138 if dns_backend
== "NONE":
1139 logger
.info("No DNS backend set, not configuring DNS")
1142 # Add dns accounts (DnsAdmins, DnsUpdateProxy) in domain
1143 logger
.info("Adding DNS accounts")
1144 add_dns_accounts(samdb
, names
.domaindn
)
1146 # If dns_backend is BIND9_FLATFILE
1147 # Populate only CN=MicrosoftDNS,CN=System,<DOMAINDN>
1149 # If dns_backend is SAMBA_INTERNAL or BIND9_DLZ
1150 # Populate DNS partitions
1152 # If os_level < 2003 (DS_DOMAIN_FUNCTION_2000)
1153 # All dns records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1155 # If os_level >= 2003 (DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008,
1156 # DS_DOMAIN_FUNCTION_2008_R2)
1157 # Root server records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1158 # Domain records are in CN=MicrosoftDNS,CN=System,<DOMAINDN>
1159 # Domain records are in CN=MicrosoftDNS,DC=DomainDnsZones,<DOMAINDN>
1160 # Forest records are in CN=MicrosoftDNS,DC=ForestDnsZones,<FORESTDN>
1161 domaindn
= names
.domaindn
1162 forestdn
= samdb
.get_root_basedn().get_linearized()
1164 dnsdomain
= names
.dnsdomain
.lower()
1165 dnsforest
= dnsdomain
1167 site
= names
.sitename
1169 hostname
= names
.netbiosname
.lower()
1171 dnsadmins_sid
= get_dnsadmins_sid(samdb
, domaindn
)
1172 domainguid
= get_domainguid(samdb
, domaindn
)
1174 samdb
.transaction_start()
1177 logger
.info("Creating CN=MicrosoftDNS,CN=System,%s" % domaindn
)
1178 create_dns_legacy(samdb
, names
.domainsid
, domaindn
, dnsadmins_sid
)
1180 if os_level
== DS_DOMAIN_FUNCTION_2000
:
1181 # Populating legacy dns
1182 logger
.info("Populating CN=MicrosoftDNS,CN=System,%s" % domaindn
)
1183 fill_dns_data_legacy(samdb
, names
.domainsid
, domaindn
, dnsdomain
, site
,
1184 hostname
, hostip
, hostip6
, dnsadmins_sid
)
1186 elif dns_backend
in ("SAMBA_INTERNAL", "BIND9_DLZ") and \
1187 os_level
>= DS_DOMAIN_FUNCTION_2003
:
1189 # Create DNS partitions
1190 logger
.info("Creating DomainDnsZones and ForestDnsZones partitions")
1191 create_dns_partitions(samdb
, names
.domainsid
, names
, domaindn
, forestdn
,
1192 dnsadmins_sid
, fill_level
)
1194 # Populating dns partitions
1195 logger
.info("Populating DomainDnsZones and ForestDnsZones partitions")
1196 fill_dns_data_partitions(samdb
, names
.domainsid
, site
, domaindn
, forestdn
,
1197 dnsdomain
, dnsforest
, hostname
, hostip
, hostip6
,
1198 domainguid
, names
.ntdsguid
, dnsadmins_sid
,
1199 fill_level
=fill_level
)
1202 samdb
.transaction_cancel()
1205 samdb
.transaction_commit()
1207 if dns_backend
.startswith("BIND9_"):
1208 setup_bind9_dns(samdb
, secretsdb
, names
, paths
, lp
, logger
,
1209 dns_backend
, os_level
, site
=site
, dnspass
=dnspass
, hostip
=hostip
,
1210 hostip6
=hostip6
, targetdir
=targetdir
,
1211 backend_store
=backend_store
)
1214 def setup_bind9_dns(samdb
, secretsdb
, names
, paths
, lp
, logger
,
1215 dns_backend
, os_level
, site
=None, dnspass
=None, hostip
=None,
1216 hostip6
=None, targetdir
=None, key_version_number
=None,
1217 backend_store
=None):
1218 """Provision DNS information (assuming BIND9 backend in DC role)
1220 :param samdb: LDB object connected to sam.ldb file
1221 :param secretsdb: LDB object connected to secrets.ldb file
1222 :param names: Names shortcut
1223 :param paths: Paths shortcut
1224 :param lp: Loadparm object
1225 :param logger: Logger object
1226 :param dns_backend: Type of DNS backend
1227 :param os_level: Functional level (treated as os level)
1228 :param site: Site to create hostnames in
1229 :param dnspass: Password for bind's DNS account
1230 :param hostip: IPv4 address
1231 :param hostip6: IPv6 address
1232 :param targetdir: Target directory for creating DNS-related files for BIND9
1235 if (not is_valid_dns_backend(dns_backend
) or
1236 not dns_backend
.startswith("BIND9_")):
1237 raise Exception("Invalid dns backend: %r" % dns_backend
)
1239 if not is_valid_os_level(os_level
):
1240 raise Exception("Invalid os level: %r" % os_level
)
1242 domaindn
= names
.domaindn
1244 domainguid
= get_domainguid(samdb
, domaindn
)
1246 secretsdb_setup_dns(secretsdb
, names
,
1250 dnsdomain
=names
.dnsdomain
,
1251 dns_keytab_path
=paths
.dns_keytab
, dnspass
=dnspass
,
1252 key_version_number
=key_version_number
)
1254 create_dns_dir(logger
, paths
)
1256 if dns_backend
== "BIND9_FLATFILE":
1257 create_zone_file(lp
, logger
, paths
, targetdir
, site
=site
,
1258 dnsdomain
=names
.dnsdomain
, hostip
=hostip
,
1259 hostip6
=hostip6
, hostname
=names
.hostname
,
1260 realm
=names
.realm
, domainguid
=domainguid
,
1261 ntdsguid
=names
.ntdsguid
)
1263 if dns_backend
== "BIND9_DLZ" and os_level
>= DS_DOMAIN_FUNCTION_2003
:
1264 create_samdb_copy(samdb
, logger
, paths
,
1265 names
, names
.domainsid
, domainguid
)
1267 create_named_conf(paths
, realm
=names
.realm
,
1268 dnsdomain
=names
.dnsdomain
, dns_backend
=dns_backend
,
1271 create_named_txt(paths
.namedtxt
,
1272 realm
=names
.realm
, dnsdomain
=names
.dnsdomain
,
1273 dnsname
="%s.%s" % (names
.hostname
, names
.dnsdomain
),
1274 binddns_dir
=paths
.binddns_dir
,
1275 keytab_name
=paths
.dns_keytab
)
1276 logger
.info("See %s for an example configuration include file for BIND",
1278 logger
.info("and %s for further documentation required for secure DNS "
1279 "updates", paths
.namedtxt
)