On Thu, 2013-01-31 at 10:49 +0100, Sumit Bose wrote:
Hi,
I have created a design page for
https://fedorahosted.org/sssd/ticket/1032 "[RFE] sssd should support DNS
sites" at
https://fedorahosted.org/sssd/wiki/DesignDocs/ActiveDirectoryDNSSites .
It can be found below as well.
Corrections, comments and enhancements are welcome.
Good start, comments inline!
bye,
Sumit
== Use Active Directory's DNS sites ==
Related ticket(s):
* [
https://fedorahosted.org/sssd/ticket/1032 RFE sssd should support DNS sites]
=== Problem Statement ===
In larger Active Directory environments there is typically more than one domain
controller. Some of them are used for redundancy, others to build different administrative
domains. But in environments with multiple physical locations each location often has at
least one local domain controller to reduce latency and network load between the
locations.
Now clients have to find the local or nearest domain controller. For this the concept of
sites was introduce where each physical location can be seen as an individual site with a
unique name. The naming scheme for DNS service records was extended (see e.g.
http://technet.microsoft.com/en-us/library/cc759550(v=ws.10).aspx) so that clients can
first try to find the needed service in the local site and can fall back to look in the
whole domain if there is no local service available.
Additionally clients have to find out about which site they belong to. This must be done
dynamically because clients might move from one location to a different one on regular
basis (roaming users). For this a special LDAP request, the (C)LDAP ping
(
http://msdn.microsoft.com/en-us/library/cc223811.aspx), was introduced.
=== Overview view of the solution ===
A new request should be added to the sssd resolver code which can do site aware lookups
of DNS service records. This request will do the following steps:
1. do a DNS lookup to find any DC
1. send a CLDAP ping to the returned DC to get the client's site
1. lookup the requested service in the client's site
1. if this fails, lookup the requested service in the next nearest site if available
(only available in domains with functional level 2008 or higher)
1. if this fails, lookup the requested service in the 'Default-First-Site-Name'
site if it wasn't one of the previously used sites
1. if this fails, lookup the requested service without any site extension
1. if this fails the request will ultimately fail
The results of the different step should be available with one of the debug levels
reserved for tracing to make debugging easier and to allow acceptance tests to validate
the behavior with the help of the debug logs.
=== Implementation details ===
Currently the request to look up service records if defined by
{{{
struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *ctx,
const char *query);
int resolv_getsrv_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
int *status,
int *timeouts,
struct ares_srv_reply **reply_list);
}}}
from async_resolv.h.
The new request might have the following interface:
{{{
/**
* @brief Create an async request to lookup DNS service records and respect the AD DNS
sites
*
* @param[in] mem_ctx Talloc memory context for the returned tevent_req structure
* @param[in] ev Tevent context
* @param[in] ctx Resolver context
* @param[in] site_ctx Site related context, may be NULL. If site_ctx is NULL
resolv_getsrv_site_send() tries to resolve the site the client belongs to first and then
tries to find the nearest server for the requested service.
* @param[in] service the service which should be found, e.g. _ldap._tcp
* @param[in] domain the domain where the service should be found
*
* @return
* - tevent_req structure for the new request
* - NULL if a new request could not be allocated
*/
struct tevent_req *resolv_getsrv_site_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *ctx,
struct site_ctx *site_ctx,
const char *service,
const char *domain);
I do not think you should have a site_ctx and a resolv_ctx, one or the
other.
If you added site_ctx with the idea of calling this function recursively
then I would object to that, you should have a higher level internal
public function that does the whole processing and an internal private
one that is called multiple times and conceal internal requirements.
Also I do not know that we really need to specify the service.
The site is independent from the service you want to get. So we should
probably have a call that returns the site and a separate call that gets
the site name as input to get a specific service.
So I would have something like this:
struct tevent_req *resolv_get_site_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *ctx,
const char *domain);
And separately:
struct tevent_req *resolv_srv_by_site_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct resolv_ctx *ctx,
const char *site,
const char *domain);
/**
* @brief Return the results of the given resolv_getsrv_site request
*
* @param[in] mem_ctx Talloc memory context for the returned results
* @param[in] req resolv_getsrv_site request which should be checked
* @param[out] status the returned status of the underlying c-ares request, see
area_query(3) for details
* @param[out] timeouts the number of timeout occured during the underlying c-ares
request, see area_query(3) for details
* @param[out] site_ctx an opaque struct with site information which can be used in later
requests to skip the site lookup
* @param[out] reply_list list of replies
*
* @return
* - 0 (EOK) on success
* - !=0 in case of an error
*/
int resolv_getsrv_site_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
int *status,
int *timeouts,
struct site_ctx **site_ctx,
struct ares_srv_reply **reply_list);
}}}
As above
int resolv_get_site_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
int *status,
int *timeouts,
const char **site);
In site we can return something like 'Foo Site', or
'Default-First-site-name' (possibly localized) or NULL if no site was
found.
And another _recv() to return srv by site.
==== Finding a DC for the CLDAP ping ====
To find any DC in the domain a Windows client, and samba as well, look
for a _ldap._tcp service record. The only difference is that samba
uses a plain _ldap._tcp.domain.name while a Windows client
_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.domain.name. I
guess it will fall back to _ldap._tcp.domain.name if the other name
cannot be resolved. I would suggest to use _ldap._tcp.domain.name for
the SSSD implementation.
Why ? Using the Default-First-Site-Name one is technically more correct
when multiple sites are in use.
==== Sending the CLDAP ping ====
The CLDAP ping is a LDAP search request with a filter like
{{{
(&(&(DnsDomain=ad.domain)(DomainSid=S-1-5-21-1111-2222-3333))(NtVer=0x01000016))
}}}
and the attribute "!NetLogon".
The flags given with the !NtVer component of the search filter will be
different for a domain member (AD provider) and an IPA server in an
environment with trusts (IPA provider).
A domain member will belong to a site and the following flags
from /usr/include/samba-4.0/gen_ndr/nbt.h should be used
'NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX |
NETLOGON_NT_VERSION_IP'. A trusted server does not belong to one of
the sites of trusting domain so it can only ask for the closest site
with 'NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX |
NETLOGON_NT_VERSION_WITH_CLOSEST_SITE'. Maybe
NETLOGON_NT_VERSION_WITH_CLOSEST_SITE is useful for a domain member as
well if e.g. the services on the local site are not available.
==== Parsing the server response ====
The server response is a single attribute "!NetLogon" which is a
binary blob containing multiple NDR encoded values. This value can be
decoded with ndr_pull_netlogon_samlogon_response() from the Samba
library libndr-nbt.
Ack, we already use ndr libs for the PAC, we need to make sure though
that libndr-nbt doesn't drag in too many dependencies.
=== How to test ===
If this feature is tested the following scenarios can be considered:
==== AD domain does not have any sites other than the default
'Default-First-Site-Name' ====
* SSSD should be able to discover the default site 'Default-First-Site-Name'
* SSSD should connect to any DC.
==== AD domain has sites but the local site of the SSSD client has no domain controller
====
* SSSD should be able to discover the local site
* SSSD should connect to a DC from 'Default-First-Site-Name'
==== AD domain has sites and the local site of the SSSD client has a domain controller
====
* SSSD should be able to discover the local site
* SSSD should connect to a DC from the local site
Besides inspection the log files with a high debug level to connection
to the domain controller can also be verified with the netstat or ss
utilities.
we should probably have a sssdctl command in the future that can query
this kind of info.
Simo.
--
Simo Sorce * Red Hat, Inc * New York