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

PR body:
"""
Automatic creation of KDC proxy URI records
    
Enables creation of following KDC proxy URL records per each replica:
    
 _kerberos.example.com. IN URI <prio> <weight> 
 krb5srv:M:kkdcp:https://ipaserver.example.com/KdcProxy";
 _kpasswd.example.com. IN URI <prio> <weight> 
"krb5srv:M:kkdcp:https://ipaserver.example.com/KdcProxy";
    
Records are created for each replica in topology as KDC proxy is enabled
by default. (If KDC proxy is manually disabled this record will be
created anyway)
    
URI records for kadmin discovery are not created because FreeIPA doesn't
support kadmin.
    
See: https://k5wiki.kerberos.org/wiki/Projects/KDC_Discovery
    
https://pagure.io/freeipa/issue/6337


### TODO
[ ] Add URI records for 88/UDP, 88/TCP with higher priority to keep client ask 
directly KDC first
[ ] Add URI records for HTTPS only when kdc proxy is enabled on server 
(requires to update server roles with role attribute KDC proxy)
[ ] Fix https://pagure.io/freeipa/issue/6906 to allow enroll FreeIPA clients 
using HTTPS KDC proxy
"""

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 af1f05ce36437a79a0818428319f977b1663c2a0 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 26 Apr 2017 18:49:47 +0200
Subject: [PATCH 1/2] Automatic creation of KDC proxy URI records

Enables creation of following KDC proxy URL records per each replica:

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

Records are created for each replica in topology as KDC proxy is enabled
by default. (If KDC proxy is manually disabled this record will be
created anyway)

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

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

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

diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
index d4dc42e..8af8011 100644
--- a/ipaserver/dns_data_management.py
+++ b/ipaserver/dns_data_management.py
@@ -37,6 +37,12 @@
     (DNSName(u'_kpasswd._udp'), 464),
 )
 
+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 +133,34 @@ def __add_srv_records(
                 r_name, rdatatype.SRV, create=True)
             rdataset.add(rd, ttl=86400)  # FIXME: use TTL from config
 
+    def __add_kdcproxy_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 +207,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 +220,12 @@ def _add_base_dns_records_for_server(
                 IPA_DEFAULT_MASTER_SRV_REC,
                 weight=server['weight']
             )
+            self.__add_kdcproxy_uri_records(
+                zone_obj,
+                hostname_rel,
+                IPA_DEFAULT_KDCPROXY_URI_REC,
+                weight=server['weight']
+            )
 
         if 'CA server' in eff_roles:
             self.__add_ca_records_from_hostname(zone_obj, hostname_abs)
@@ -214,6 +255,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 +273,14 @@ def _get_location_dns_records_for_server(
                     priority=priority,
                     location=location
                 )
+                self.__add_kdcproxy_uri_records(
+                    zone_obj,
+                    hostname_rel,
+                    IPA_DEFAULT_KDCPROXY_URI_REC,
+                    weight=server['weight'],
+                    priority=priority,
+                    location=location
+                )
 
             if 'AD trust controller' in eff_roles:
                 self.__add_srv_records(
@@ -369,7 +419,8 @@ 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_KDCPROXY_URI_REC
             )
         )
 
@@ -442,7 +493,8 @@ 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_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..d7145cb 100644
--- a/ipatests/test_integration/test_dns_locations.py
+++ b/ipatests/test_integration/test_dns_locations.py
@@ -22,6 +22,12 @@
     (DNSName(u'_kpasswd._udp'), 464),
 )
 
+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 +54,20 @@ def _gen_expected_srv_rrset(rname, port, servers, ttl=86400):
     )
 
 
+def _gen_expected_kdcproxy_uri_rrset(rname, target, servers, ttl=86400):
+    rdata_list = [
+        "{prio} {weight} {target}".format(
+            prio=prio,
+            weight=weight,
+            target=target.format(server=servername)
+        )
+        for prio, 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 +112,17 @@ 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 rname, target in IPA_DEFAULT_KDCPROXY_URI_REC:
+            name_abs = rname.derelativize(domain)
+            expected = _gen_expected_kdcproxy_uri_rrset(
+                name_abs, target, expected_servers)
+            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"""

From 04d0209cdd6b49a9a796e3adbc69ce9b9af1d186 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Thu, 27 Apr 2017 12:12:45 +0200
Subject: [PATCH 2/2] [WIP] test: HTTPS KDC proxy with KDC detaction from URI
 RR

TODO: make sure that krb5.conf is configured to use DNS KDC detection
(i.e ipa-client-install --server ... --domain ... has static thus with
current tests setups this will not work and file must be ammended in
test)
---
 ipatests/test_integration/test_http_kdc_proxy.py | 34 ++++++++++++++++--------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/ipatests/test_integration/test_http_kdc_proxy.py b/ipatests/test_integration/test_http_kdc_proxy.py
index c81a86a..c88a08d 100644
--- a/ipatests/test_integration/test_http_kdc_proxy.py
+++ b/ipatests/test_integration/test_http_kdc_proxy.py
@@ -32,26 +32,38 @@ def install(cls, mh):
         cls.clients[0].run_command([
             'ip6tables', '-A', 'OUTPUT', '-p', 'udp',
             '--dport', '88', '-j', 'DROP'])
+
+    @classmethod
+    def uninstall(cls, mh):
+        super(TestHttpKdcProxy, cls).uninstall(mh)
+        cls.clients[0].run_command(['iptables', '-F'])
+
+    def test_http_kdc_proxy_uri_discovery(self):
+        """FreeIPA provides URI records for KDC autodiscovery, test if client
+        is able to kinit via KDC proxy without paths in config
+        """
+        result = tasks.kinit_admin(self.clients[0], raiseonerr=False)
+        assert(result.returncode == 0), (
+            "Unable to kinit using KdcProxy: %s" % result.stderr_text
+            )
+
+    def test_http_kdc_proxy_works(self):
+        """Test HTTP KDC proxy with URL in config
+        """
         # configure client
-        cls.clients[0].run_command(
+        self.clients[0].run_command(
             "sed -i 's/ kdc = .*$/ kdc = https:\/\/%s\/KdcProxy/' %s" % (
-                cls.master.hostname, paths.KRB5_CONF)
+                self.master.hostname, paths.KRB5_CONF)
             )
-        cls.clients[0].run_command(
+        self.clients[0].run_command(
             "sed -i 's/master_kdc = .*$/master_kdc"
             " = https:\/\/%s\/KdcProxy/' %s" % (
-                cls.master.hostname, paths.KRB5_CONF)
+                self.master.hostname, paths.KRB5_CONF)
             )
         # Workaround for https://fedorahosted.org/freeipa/ticket/6443
-        cls.clients[0].run_command(['systemctl', 'restart', 'sssd.service'])
+        self.clients[0].run_command(['systemctl', 'restart', 'sssd.service'])
         # End of workaround
 
-    @classmethod
-    def uninstall(cls, mh):
-        super(TestHttpKdcProxy, cls).uninstall(mh)
-        cls.clients[0].run_command(['iptables', '-F'])
-
-    def test_http_kdc_proxy_works(self):
         result = tasks.kinit_admin(self.clients[0], raiseonerr=False)
         assert(result.returncode == 0), (
             "Unable to kinit using KdcProxy: %s" % result.stderr_text
-- 
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