The next IPA release will support one-way trusts. SSSD needs to add support for this feature in its server mode.
One-way trust to Active Directory where FreeIPA realm trusts Active Directory forest using cross-forest trust feature of AD but the AD forest does not trust FreeIPA realm. Users from AD forest can access resources in FreeIPA realm.
At a high level, SSSD needs to examine the trust objects whether they are one-way or two way trusts. For each one-way trust, SSSD needs to fetch and store the keytab and use the keytab to secure the connection. For two-way trusts, we can keep using the existing code that reuses the IPA realm and the system keytab for both IPA and AD connections. Care must be taken to remove keytabs of trusts that were removed as well.
Fetching the keytab would be done by calling the
ipa-getkeytab utility for every one-way trust. The keytab would only be (re)fetched if it’s missing or if attempting to use the keytab failed. On the IPA server, we must make sure that the IPA server identity is allowed to read the keytab.
Because handling multiple keytabs increases the risk of failing connections in case the trust wasn’t setup correctly, we need to modify the failover code to not set the whole back end offline in case connecting to a subdomain AD server fails. Instead, the subdomain will be marked as inactive for a short period of time, during which it would act as offline. The proper way of solving this problem would be to rework the failover module so that it can act per domain, not only per back end, however, that change is out of scope for this release.
This section describes all the required changes in detail.
The IPA subdomain handler will include the attribute
ipaNTTrustDirection when reading the trust objects. Currently this attribute is not readable by the host principal, so the IPA ACIs must be relaxed (ipa ticket?). If the trust direction is set to an OR of
lsa.LSA_TRUST_DIRECTION_INBOUND, then it’s a two-way trust and we’ll just use the existing code that re-uses the IPA keytab for the AD trusted domain as well. If the attribute is only
lsa.LSA_TRUST_DIRECTION_OUTBOUND, we handle the trust as a one-way trust. The trust type can be stored in
ipa_ad_server_ctx. If the trust direction is set to
lsa.LSA_TRUST_DIRECTION_INBOUND only, then we would log this trust object as unsupported and continue.
sss_domain_info structure will be created as
inactive in the subdomain code. After enumerating the trusted domains, the subdomain handler will check if a keytab already exists for every one-way trusted domain. If yes, the domain is ready to use and can be enabled. If there is no keytab, the subdomain handler will fork out a call to
ipa-getkeytab, fetch the keytab and store it under
ipa-getkeytab call will be done using Kerberos credentials the host has. IPA ACIs must be modified accordingly to allow the IPA server principals to fetch the trust keytabs, but nobody else. The SSSD invocation of
ipa-getkeytab will not limit the enctypes in any way, we just rely on IPA creating the objects in LDAP in the correct manner.
/var/lib/sss/keytabs must only be accessible to the sssd user. As an additional security measure, the directory will also receive a SELinux context stricter than the default
sssd_var_lib_t. That way, processes that are able to access the sssd state directory, which is public, will not be able to access the keytabs. If fetching the keytab succeeds, the domain would be enabled. The SELinux policy must also be adjusted to allow calling
ipa-getkeytab by the
If any trust relationships were removed, the corresponding keytabs must be removed from the disk as well.
With two-way trust, we can keep using the default IPA principal and keytab.
With one-way trust, the keytab retrieved from the IPA server must be used. Also, the principal must be passed into the
ad_create_default_options function. The custom values must be set before we proceed to instantiate LDAP provider options. The only AD provider option we need to set is AD_KRB5_REALM.
In the LDAP provider, we must take care that the following sdap options are set correctly:
- SDAP_SASL_AUTHID - must be set to the NetBIOS name of the IPA domain. (A domain
TRUST.COMwould set this value to
TRUST$. We would use the
IPA_FLATNAMEattribute, not truncate the DNS domain).
- SDAP_SASL_REALM - must be set to the AD realm
- SDAP_KRB5_KEYTAB - must be set to the per-domain keytab retrieved from IPA
The AD provider eventually calls
sdap_set_sasl_options() from the LDAP provider, we need to make sure this function receives the correct values. During experimentation we were able to show that using multiple different SASL users and different realms doesn’t cause any problems in SASL or LDAP libraries.
The only place that will keep using the IPA realm is the failover instantiation. We need to keep using this hack until failover is per-backend.
At the moment, the whole back end can be either online or offline and the status applies to both the main domain and the subdomains alike. As an effect, a failure to connect to a subdomain server would also make the main domain operate offline. In many subdomain setups, it’s actually more convenient not to, because the subdomain server might be on a different network segment, behind a different firewall etc. Instead, the domain should only be made inactive.
sss_domain_info structure would convert the ‘bool disabled’ parameter into an
enum sss_domain_state. The supported values would be:
- disabled - the domain should not be used by either responder or provider. It was removed or disabled on the server.
- active - the domain can be used by a responder and the data provider would forward its request to the backend
- inactive - the domain can be used by a responder, but the data provider would just shortcut as if the domain was offline. For now, this option will be used by subdomains only.
The implementation would include renaming the existing
be_mark_offline() function to be called
be_mark_dom_offline() and modifying its behaviour. The existing code that sets the offline status and runs the offline callbacks would be called for parent domains only. For subdomains, we would mark the subdomain as inactive and schedule a tevent request that would unconditionally reset inactive domain to active. The request would be scheduled after
offline_timeout seconds to be consistent with main domains from user’s perspective. Likewise, the
be_reset_offline() function will be extended to reset inactive domains to active as well as the SIGUSR1 and SIGUSR2 signal handlers. Finally, all calls to the
be_is_offline() function should be inspected and the invocations that are per-domain should be converted to a new function
be_dom_is_offline() that would check the offline status for parent domains and the offline state for subdomains. We should also make sure the backend offline status structure is opaque as currently its internals are readable by external users as well. Making the offline status opaque would make it safer to perform modifications to the offline code.
In both offline and inactive cases, the ID handlers would reply with
DP_ERR_OFFLINE. The crucial difference between offline and inactive at this point would be that inactive domains are re-activated unconditionally. When we modify the failover code to handle domains separately, we’ll be able to leverage per-domain online checks or online/offline callbacks as well.
The trust keytabs would be fetched on each SSSD restart. This may seem like a bit of a churn, but retrieving the keytab should be relatively cheap since the SSSD instance runs on the local server. The advantage of retrieving the keytabs again is that a simple sssd service restart would provide an option for the admin to start from a clean slate. Either way, SSSD service restarts on the server should be quite rare.
In cases the
sdap_kinit_send() request fails, the sdap code would return a special error code instead of blindly returning
EIO as it does at the moment. When the
ipa_get_ad_acct request receives this error code, it would re-run the subdomain request in order to check if the trust relationship still exists and in order to re-fetch the keytab again. In order to be able to run the subdomain request separately from the subdomain back end handler, the subdomain code must be wrapped into a separate tevent request as the code currently assumes it’s being called from the subdomain backend handler only.
After the keytabs are fetched again, we would attempt to detect if the trust has been re-established by comparing the keys in the keytab. Using krb5 calls to read the keytab is fine in the back end code, because the keytabs will be readable by the SSSD user and could be accessed from the provider code without elevating privileges. We can’t rely on
kvno here, because it is generally always 1. In case the keys differ, then trust was re-established. In that case would re-set the inactive domain status and re-run the account request. If the keys are the same, we just leave the domain as inactive. The
ipa_ad_trust_ctx structure for each trust would contain a flag that would track that we already tried refreshing the keytab so that we don’t download them on each failed attempt. This flag would be cleared by the online callbacks (either periodical or with SIGUSR2).
In case the trust went away, the subdomain code should remove the trusted domain already with the existing code (however, this must be tested). In this case, also the keytab must be removed.
- Handling failover and offline status on per-domain basis instead of per-backend basis should be done in the next release.
- If we ever need to store the keytabs in the database instead of on the filesystem, we might want to switch from calling ipa-getkeytab to calling the LDAP extended operation ourselves. However, this is not planned at the moment.
Establish a one-way trust relationship with an AD domain. Make sure both IPA and AD users are resolvable. It’s prudent to test combinations of one-way and two-way trusts with different forests. Make sure removing a trust relationship removes the keytab from the filesystem. Make sure that SSSD handles re-establishing a trust relationship.
- Jakub Hrozek <firstname.lastname@example.org>