URL: https://github.com/freeipa/freeipa/pull/746
Author: MartinBasti
 Title: #746: KDC proxy URI records
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/746/head:pr746
git checkout pr746
From 342158b9f427057c08b9a115b68825f918554ee1 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 26 Apr 2017 18:49:47 +0200
Subject: [PATCH] Automatic creation of KDC URI records

Enables creation of following records per each replica:

KDC URI records:
_kerberos.example.com. IN URI <prio> <weight> "krb5srv:M:tcp:ipaserver.example.com"
_kpasswd.example.com. IN URI <prio> <weight> "krb5srv:M:tcp:ipaserver.example.com"
_kerberos.example.com. IN URI <prio> <weight> "krb5srv:M:udp:ipaserver.example.com"
_kpasswd.example.com. IN URI <prio> <weight> "krb5srv:M:udp:ipaserver.example.com"

KDC proxy URI records:
_kerberos.example.com. IN URI <prio>+10 <weight> "krb5srv:M:kkdcp:https://ipaserver.example.com/KdcProxy";
_kpasswd.example.com. IN URI <prio>+10 <weight> "krb5srv:M:kkdcp:https://ipaserver.example.com/KdcProxy";

URI records for kadmin discovery are not created because FreeIPA doesn't
support kadmin.

KDC URI records (tcp, udp) must have higher priority than KDC proxy
(https) to prefer direct communication with KDC. Also there is a bug
that prevents ipa-client-install to enroll client with using only KDC
proxy in some cases (see https://pagure.io/freeipa/issue/6906).

All records are created for each replica in topology as KDC proxy is enabled
by default. (Please note if KDC proxy is manually disabled KDC Proxy records will be
created anyway)

See: https://k5wiki.kerberos.org/wiki/Projects/KDC_Discovery

https://pagure.io/freeipa/issue/6337
---
 ipaserver/dns_data_management.py                | 88 ++++++++++++++++++++++++-
 ipatests/test_integration/test_dns_locations.py | 47 +++++++++++++
 2 files changed, 133 insertions(+), 2 deletions(-)

diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
index d4dc42e..0dbedde 100644
--- a/ipaserver/dns_data_management.py
+++ b/ipaserver/dns_data_management.py
@@ -37,6 +37,23 @@
     (DNSName(u'_kpasswd._udp'), 464),
 )
 
+IPA_DEFAULT_KDC_URI_REC = (
+    # URI record name, target
+    (DNSName(u'_kpasswd'), u'krb5srv:M:tcp:{server}'),
+    (DNSName(u'_kpasswd'), u'krb5srv:M:udp:{server}'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:tcp:{server}'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:udp:{server}'),
+)
+
+# URI records for KDCProxy must have lower priority than for KDC, clients must
+# prefer to connect directly to KDC
+IPA_KDCPROXY_PRIORITY_PENALIZATION = 10
+IPA_DEFAULT_KDCPROXY_URI_REC = (
+    # URI record name, target
+    (DNSName(u'_kpasswd'), u'krb5srv:M:kkdcp:https://{server}/KdcProxy'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:kkdcp:https://{server}/KdcProxy'),
+)
+
 IPA_DEFAULT_ADTRUST_SRV_REC = (
     # srv record name, port
     (DNSName(u'_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs'), 389),
@@ -127,6 +144,34 @@ def __add_srv_records(
                 r_name, rdatatype.SRV, create=True)
             rdataset.add(rd, ttl=86400)  # FIXME: use TTL from config
 
+    def __add_kdc_uri_records(
+        self, zone_obj, hostname, rname_target_map,
+        weight=100, priority=0, location=None
+    ):
+        assert isinstance(hostname, DNSName)
+        assert isinstance(priority, int)
+        assert isinstance(weight, int)
+
+        if location:
+            suffix = self.__get_location_suffix(location)
+        else:
+            suffix = self.domain_abs
+
+        for name, target in rname_target_map:
+            rd = rdata.from_text(
+                rdataclass.IN, rdatatype.URI,
+                '{0} {1} {2}'.format(
+                    priority, weight,
+                    target.format(server=hostname.ToASCII())
+                )
+            )
+
+            r_name = name.derelativize(suffix)
+
+            rdataset = zone_obj.get_rdataset(
+                r_name, rdatatype.URI, create=True)
+            rdataset.add(rd, ttl=86400)  # FIXME: use TTL from config
+
     def __add_ca_records_from_hostname(self, zone_obj, hostname):
         assert isinstance(hostname, DNSName) and hostname.is_absolute()
         r_name = DNSName('ipa-ca') + self.domain_abs
@@ -173,6 +218,7 @@ def _add_base_dns_records_for_server(
         else:
             eff_roles = server['roles']
         hostname_abs = DNSName(hostname).make_absolute()
+        hostname_rel = DNSName(hostname)
 
         if include_kerberos_realm:
             self.__add_kerberos_txt_rec(zone_obj)
@@ -185,6 +231,21 @@ def _add_base_dns_records_for_server(
                 IPA_DEFAULT_MASTER_SRV_REC,
                 weight=server['weight']
             )
+            self.__add_kdc_uri_records(
+                zone_obj,
+                hostname_rel,
+                IPA_DEFAULT_KDC_URI_REC,
+                weight=server['weight']
+            )
+
+            # FIXME: create KDC Proxy records only when KDC proxy is enabled
+            self.__add_kdc_uri_records(
+                zone_obj,
+                hostname_rel,
+                IPA_DEFAULT_KDCPROXY_URI_REC,
+                weight=server['weight'],
+                priority=IPA_KDCPROXY_PRIORITY_PENALIZATION
+            )
 
         if 'CA server' in eff_roles:
             self.__add_ca_records_from_hostname(zone_obj, hostname_abs)
@@ -214,6 +275,7 @@ def _get_location_dns_records_for_server(
         else:
             eff_roles = server['roles']
         hostname_abs = DNSName(hostname).make_absolute()
+        hostname_rel = DNSName(hostname)
 
         # generate locations specific records
         for location in locations:
@@ -231,6 +293,24 @@ def _get_location_dns_records_for_server(
                     priority=priority,
                     location=location
                 )
+                self.__add_kdc_uri_records(
+                    zone_obj,
+                    hostname_rel,
+                    IPA_DEFAULT_KDC_URI_REC,
+                    weight=server['weight'],
+                    priority=priority,
+                    location=location
+                )
+
+                # FIXME: create KDC Proxy records only when KDC proxy is enabled
+                self.__add_kdc_uri_records(
+                    zone_obj,
+                    hostname_rel,
+                    IPA_DEFAULT_KDCPROXY_URI_REC,
+                    weight=server['weight'],
+                    priority=priority + IPA_KDCPROXY_PRIORITY_PENALIZATION,
+                    location=location
+                )
 
             if 'AD trust controller' in eff_roles:
                 self.__add_srv_records(
@@ -369,7 +449,9 @@ def update_base_records(self):
             rec[0].derelativize(self.domain_abs) for rec in (
                 IPA_DEFAULT_MASTER_SRV_REC +
                 IPA_DEFAULT_ADTRUST_SRV_REC +
-                IPA_DEFAULT_NTP_SRV_REC
+                IPA_DEFAULT_NTP_SRV_REC +
+                IPA_DEFAULT_KDC_URI_REC +
+                IPA_DEFAULT_KDCPROXY_URI_REC
             )
         )
 
@@ -442,7 +524,9 @@ def remove_location_records(self, location):
         for records in (
                 IPA_DEFAULT_MASTER_SRV_REC,
                 IPA_DEFAULT_ADTRUST_SRV_REC,
-                IPA_DEFAULT_NTP_SRV_REC
+                IPA_DEFAULT_NTP_SRV_REC,
+                IPA_DEFAULT_KDC_URI_REC,
+                IPA_DEFAULT_KDCPROXY_URI_REC
         ):
             for name, _port in records:
                 loc_records.append(
diff --git a/ipatests/test_integration/test_dns_locations.py b/ipatests/test_integration/test_dns_locations.py
index 47a0e85..6247ef8 100644
--- a/ipatests/test_integration/test_dns_locations.py
+++ b/ipatests/test_integration/test_dns_locations.py
@@ -22,6 +22,21 @@
     (DNSName(u'_kpasswd._udp'), 464),
 )
 
+IPA_DEFAULT_KDC_URI_REC = (
+    # URI record name, target
+    (DNSName(u'_kpasswd'), u'krb5srv:M:tcp:{server}'),
+    (DNSName(u'_kpasswd'), u'krb5srv:M:udp:{server}'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:tcp:{server}'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:udp:{server}'),
+)
+
+IPA_KDCPROXY_PRIORITY_PENALIZATION = 10
+IPA_DEFAULT_KDCPROXY_URI_REC = (
+    # URI record name, target
+    (DNSName(u'_kpasswd'), u'krb5srv:M:kkdcp:https://{server}/KdcProxy'),
+    (DNSName(u'_kerberos'), u'krb5srv:M:kkdcp:https://{server}/KdcProxy'),
+)
+
 
 def resolve_records_from_server(rname, rtype, nameserver, logger):
     res = dns.resolver.Resolver()
@@ -48,6 +63,21 @@ def _gen_expected_srv_rrset(rname, port, servers, ttl=86400):
     )
 
 
+def _gen_expected_kdc_uri_rrset(
+        rname, target, servers, ttl=86400, priority_penalization=0):
+    rdata_list = [
+        "{prio} {weight} {target}".format(
+            prio=prio,
+            weight=weight,
+            target=target.format(server=servername)
+        )
+        for prio + priority_penalization, weight, servername in servers
+    ]
+    return dns.rrset.from_text_list(
+        rname, ttl, dns.rdataclass.IN, dns.rdatatype.URI, rdata_list
+    )
+
+
 class TestDNSLocations(IntegrationTest):
     """Simple test if SRV DNS records for IPA locations are generated properly
 
@@ -92,6 +122,23 @@ def _test_against_server(self, server_ip, domain, expected_servers):
                 "with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{})".format(
                     server_ip, name_abs, expected, query))
 
+        for records, penalization in (
+            (IPA_DEFAULT_KDC_URI_REC, 0),
+            (IPA_DEFAULT_KDCPROXY_URI_REC, IPA_KDCPROXY_PRIORITY_PENALIZATION)
+        ):
+            for rname, target in records:
+                name_abs = rname.derelativize(domain)
+                expected = _gen_expected_kdc_uri_rrset(
+                    name_abs, target, expected_servers,
+                    priority_penalization=penalization
+                )
+                query = resolve_records_from_server(
+                    name_abs, 'URI', server_ip, self.log)
+                assert expected == query, (
+                    "Expected and received DNS data do not match on server "
+                    "with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{}"
+                    ")".format(server_ip, name_abs, expected, query))
+
     def test_without_locations(self):
         """Servers are not in locations, this tests if basic system records
         are generated properly"""
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to