diff options
author | Andrew Bartlett <abartlet@samba.org> | 2015-02-26 12:29:23 +1300 |
---|---|---|
committer | Garming Sam <garming@samba.org> | 2016-06-16 04:40:13 +0200 |
commit | c9aefa93c1f79e692791e4d384aca2e20e8f6f3f (patch) | |
tree | 0b3748eda644e67850fab5b66ad63fc156f72349 /source4/scripting/bin/samba_dnsupdate | |
parent | 789ec3400773e795977c4f466872cbb81727d99a (diff) | |
download | samba-c9aefa93c1f79e692791e4d384aca2e20e8f6f3f.tar.gz |
samba_dnsupdate: Add a mode that calls samba-tool dns, rather than nsupdate
This mode is more likely to work when we change hostname or IP
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Diffstat (limited to 'source4/scripting/bin/samba_dnsupdate')
-rwxr-xr-x | source4/scripting/bin/samba_dnsupdate | 159 |
1 files changed, 145 insertions, 14 deletions
diff --git a/source4/scripting/bin/samba_dnsupdate b/source4/scripting/bin/samba_dnsupdate index 025664a18d9..d9bf726d37f 100755 --- a/source4/scripting/bin/samba_dnsupdate +++ b/source4/scripting/bin/samba_dnsupdate @@ -46,6 +46,8 @@ from samba import dsdb from samba.auth import system_session from samba.samdb import SamDB from samba.dcerpc import netlogon, winbind +from samba.netcmd.dns import cmd_dns +from samba import gensec samba.ensure_third_party_module("dns", "dnspython") import dns.resolver @@ -60,6 +62,8 @@ sambaopts = options.SambaOptions(parser) parser.add_option_group(sambaopts) parser.add_option_group(options.VersionOptions(parser)) parser.add_option("--verbose", action="store_true") +parser.add_option("--use-samba-tool", action="store_true", help="Use samba-tool to make updates over RPC, rather than over DNS") +parser.add_option("--use-nsupdate", action="store_true", help="Use nsupdate command to make updates over DNS (default, if kinit successful)") parser.add_option("--all-names", action="store_true") parser.add_option("--all-interfaces", action="store_true") parser.add_option("--use-file", type="string", help="Use a file, rather than real DNS calls") @@ -118,6 +122,32 @@ def get_credentials(lp): (tmp_fd, ccachename) = tempfile.mkstemp() try: creds.get_named_ccache(lp, ccachename) + + if opts.use_file is None: + # Now confirm we can get a ticket to a DNS server + ans = check_one_dns_name(sub_vars['DNSDOMAIN'] + '.', 'NS') + for i in range(len(ans)): + target_hostname = str(ans[i].target).rstrip('.') + settings = {} + settings["lp_ctx"] = lp + settings["target_hostname"] = target_hostname + + gensec_client = gensec.Security.start_client(settings) + gensec_client.set_credentials(creds) + gensec_client.set_target_service("DNS") + gensec_client.set_target_hostname(target_hostname) + gensec_client.want_feature(gensec.FEATURE_SEAL) + gensec_client.start_mech_by_sasl_name("GSSAPI") + server_to_client = "" + try: + (client_finished, client_to_server) = gensec_client.update(server_to_client) + return + except RuntimeError: + # Only raise an exception if they all failed + if i != len(ans) - 1: + pass + raise + except RuntimeError as e: os.unlink(ccachename) raise e @@ -135,6 +165,7 @@ class dnsobj(object): self.ip = None self.existing_port = None self.existing_weight = None + self.existing_cname_target = None self.type = list[0] self.name = list[1] self.nameservers = [] @@ -184,6 +215,19 @@ def hostname_match(h1, h2): h2 = str(h2) return h1.lower().rstrip('.') == h2.lower().rstrip('.') +def check_one_dns_name(name, name_type, d=None): + resolv_conf = os.getenv('RESOLV_WRAPPER_CONF') + if not resolv_conf: + resolv_conf = '/etc/resolv.conf' + resolver = dns.resolver.Resolver(filename=resolv_conf, configure=True) + + if d is not None and d.nameservers != []: + resolver.nameservers = d.nameservers + elif d is not None: + d.nameservers = resolver.nameservers + + ans = resolver.query(name, name_type) + return ans def check_dns_name(d): """check that a DNS entry exists.""" @@ -205,18 +249,8 @@ def check_dns_name(d): return True return False - resolv_conf = os.getenv('RESOLV_WRAPPER_CONF') - if not resolv_conf: - resolv_conf = '/etc/resolv.conf' - resolver = dns.resolver.Resolver(filename=resolv_conf, configure=True) - - if d.nameservers != []: - resolver.nameservers = d.nameservers - else: - d.nameservers = resolver.nameservers - try: - ans = resolver.query(normalised_name, d.type) + ans = check_one_dns_name(normalised_name, d.type, d) except dns.exception.DNSException: if opts.verbose: print "Failed to find DNS entry %s" % d @@ -230,6 +264,8 @@ def check_dns_name(d): for i in range(len(ans)): if hostname_match(ans[i].target, d.dest): return True + else: + d.existing_cname_target = str(ans[i].target) elif d.type == 'NS': for i in range(len(ans)): if hostname_match(ans[i].target, d.dest): @@ -417,6 +453,80 @@ def call_nsupdate(d, op="add"): os.environ["SOCKET_WRAPPER_MTU"] = "0" +def call_samba_tool(d, op="add"): + """call samba-tool dns to update an entry.""" + + assert(op in ["add", "delete"]) + + if (sub_vars['DNSFOREST'] != sub_vars['DNSDOMAIN']) and \ + sub_vars['DNSFOREST'].endswith('.' + sub_vars['DNSDOMAIN']): + print "Refusing to use samba-tool when forest %s is under domain %s" \ + % (sub_vars['DNSFOREST'], sub_vars['DNSDOMAIN']) + + if opts.verbose: + print "Calling samba-tool dns for %s (%s)" % (d, op) + + normalised_name = d.name.rstrip('.') + '.' + if normalised_name == (sub_vars['DNSDOMAIN'] + '.'): + short_name = '@' + zone = sub_vars['DNSDOMAIN'] + elif normalised_name == (sub_vars['DNSFOREST'] + '.'): + short_name = '@' + zone = sub_vars['DNSFOREST'] + elif normalised_name == ('_msdcs.' + sub_vars['DNSFOREST'] + '.'): + short_name = '@' + zone = '_msdcs.' + sub_vars['DNSFOREST'] + else: + if not normalised_name.endswith('.' + sub_vars['DNSDOMAIN'] + '.'): + print "Not Calling samba-tool dns for %s (%s), %s not in %s" % (d, op, normalised_name, sub_vars['DNSDOMAIN'] + '.') + return False + elif normalised_name.endswith('._msdcs.' + sub_vars['DNSFOREST'] + '.'): + zone = '_msdcs.' + sub_vars['DNSFOREST'] + else: + zone = sub_vars['DNSDOMAIN'] + len_zone = len(zone)+2 + short_name = normalised_name[:-len_zone] + + if d.type == "A": + args = [IPs[0], zone, short_name, "A", d.ip] + if d.type == "AAAA": + args = [IPs[0], zone, short_name, "AAAA", d.ip] + if d.type == "SRV": + if op == "add" and d.existing_port is not None: + print "Not handling modify of exising SRV %s using samba-tool" % d + return False + op = "update" + args = [IPs[0], zone, short_name, "SRV", + "%s %s %s %s" % (d.existing_weight, + d.existing_port, "0", "100"), + "%s %s %s %s" % (d.dest, d.port, "0", "100")] + else: + args = [IPs[0], zone, short_name, "SRV", "%s %s %s %s" % (d.dest, d.port, "0", "100")] + if d.type == "CNAME": + if d.existing_cname_target is None: + args = [IPs[0], zone, short_name, "CNAME", d.dest] + else: + op = "update" + args = [IPs[0], zone, short_name, "CNAME", + d.existing_cname_target.rstrip('.'), d.dest] + + if d.type == "NS": + args = [IPs[0], zone, short_name, "NS", d.dest] + + global error_count + try: + cmd = cmd_dns() + if opts.verbose: + print "Calling samba-tool dns %s -k no -P %s" % (op, args) + cmd._run("dns", op, "-k", "no", "-P", *args) + except Exception, estr: + raise + if opts.fail_immediately: + sys.exit(1) + error_count = error_count + 1 + if opts.verbose: + print("Failed 'samba-tool dns' based update: %s : %s" % (str(d), estr)) + def rodc_dns_update(d, t, op): '''a single DNS update via the RODC netlogon call''' global sub_vars @@ -630,14 +740,32 @@ else: if opts.verbose: print "%d DNS updates and %d DNS deletes needed" % (len(update_list), len(delete_list)) +use_samba_tool = opts.use_samba_tool +use_nsupdate = opts.use_nsupdate # get our krb5 creds if len(delete_list) != 0 or len(update_list) != 0: if not opts.nocreds: - get_credentials(lp) + try: + get_credentials(lp) + except RuntimeError as e: + ccachename = None + + if sub_vars['IF_RWDNS_DOMAIN'] == "# ": + raise + + if use_nsupdate: + raise + + print "Failed to get Kerberos credentials, falling back to samba-tool: %s" % e + use_samba_tool = True + # ask nsupdate to delete entries as needed for d in delete_list: - if am_rodc: + if not use_nsupdate and use_samba_tool: + call_samba_tool(d, op="delete") + + elif am_rodc: if d.name.lower() == domain.lower(): if opts.verbose: print "skip delete (rodc): %s" % d @@ -657,7 +785,10 @@ for d in delete_list: # ask nsupdate to add entries as needed for d in update_list: - if am_rodc: + if not use_nsupdate and use_samba_tool: + call_samba_tool(d) + + elif am_rodc: if d.name.lower() == domain.lower(): if opts.verbose: print "skip (rodc): %s" % d |