On Fri, 27 Jul 2012, Rob Crittenden wrote:
Alexander Bokovoy wrote:
On Thu, 26 Jul 2012, Alexander Bokovoy wrote:
Hi,
When setting up AD trusts support, ipa-adtrust-install utility
needs to be run as:
- root, for performing Samba configuration and using LDAPI/autobind
- kinit-ed IPA admin user, to ensure proper ACIs are granted to
fetch keytab
As result, we can get rid of Directory Manager credentials in
ipa-adtrust-install
https://fedorahosted.org/freeipa/ticket/2815
This ticket also simplifies a bit the way we handle admin connection in
Service class and particulary in Service._ldap_mod() by defaulting to
LDAPI/autobind in case of running as root and to GSSAPI otherwise.
Except few cases in remote replica management (not applicable in
_ldap_mod() case) we always run installation tools as root and can
benefit from using autobind feature. Unfortunately, it is not yet
possible to get away from using DM credentials for all cases as the same
class is used to perform initial directory server instance
configuration.
One side effect is explicit disconnect and reconnect in
Service.add_cert_to_service() due to way how SimpleLDAPObject class
handles stale connections (no handling at all). I've put some comments
in place so that others would not try to err out optimizing it in
future.
Finally, with next patch series which will introduce syncing ipaNTHash
attribute with RC4 key in existing kerberos credentials, we can remove
requirements to change passwords or re-kinit for majority of trust
cases. This should then conclude our trusts content for beta2 release.
Patch updated, fixed small typo (auth_parms was initialized as
auth_params which led to non-existing auth_parms in ipa-adtrust-install
case).
Nack, a couple of minor issues:
The exception handling is rather unusual in
ensure_kerberos_admin_rights(api). I'm not sure if this is any more
efficient than a series of excepts...
I've rewrote this code and put it directly in the main.
You don't need to pass in api, it's a global.
Fixed.
It may be safe to see if the user is in the group the way you are
doing it, I wonder if it would be clearer to cast those into DN
objects.
Not sure if checking DNs would be sustaining in long run. Ideally we
should check ACI here, not just hardcoded group name. I'd like to keep
it explicit with memberof for now because it shows what exactly we want
to check.
In the Service class what is the point of ldapi if it is going to be
ignored in the case we know the realm? What if I really, really just
want to use a password?
LDAPI bind in IPAAdmin.__local_init() requires that there is realm known.
No realm -- no LDAPI use because we otherwise cannot construct the
socket name. For 'just want to use a password' case you can simply set
self.dm_password.
However, I've changed the code in Service.ldap_connect() to do
following:
1. if DM password is provided, we'll try to use it
2. Otherwise, if LDAPI is asked for and realm is set, we'll use LDAPI and realm
3. Otherwise (ldapi was False or realm not provided), we'll try to
connect to fqdn:389 with GSSAPI
I think this covers all cases.
And later where it forces ldapi, it seems better to either commit all
the way and drop the ldapi argument or convert it to a better name
(like autobind).
ldapi requires realm but can be used with either GSSAPI or autobind.
Calling it autobind isn't really correct as autobind only available on
ldapi under root.
--
/ Alexander Bokovoy
>From d7fea3f6f50218821560ea739a26030f3a76640f Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Fri, 13 Jul 2012 18:12:48 +0300
Subject: [PATCH 2/6] Ensure ipa-adtrust-install is run with Kerberos ticket
for admin user
When setting up AD trusts support, ipa-adtrust-install utility
needs to be run as:
- root, for performing Samba configuration and using LDAPI/autobind
- kinit-ed IPA admin user, to ensure proper ACIs are granted to
fetch keytab
As result, we can get rid of Directory Manager credentials in
ipa-adtrust-install
https://fedorahosted.org/freeipa/ticket/2815
---
install/tools/ipa-adtrust-install | 37 +++++++++++++----------
install/tools/man/ipa-adtrust-install.1 | 3 --
ipaserver/install/adtrustinstance.py | 21 ++++++-------
ipaserver/install/service.py | 53 ++++++++++++++++++++++++---------
4 files changed, 71 insertions(+), 43 deletions(-)
diff --git a/install/tools/ipa-adtrust-install
b/install/tools/ipa-adtrust-install
index
6678018e6346d75d5042894cfb833d38079d3f21..f1cff30bd439ee07226034b3e49ae17cc59ce6b0
100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -37,8 +37,6 @@ log_file_name = "/var/log/ipaserver-install.log"
def parse_options():
parser = IPAOptionParser(version=version.VERSION)
- parser.add_option("-p", "--ds-password", dest="dm_password",
- sensitive=True, help="directory manager password")
parser.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="print debugging information")
parser.add_option("--ip-address", dest="ip_address",
@@ -194,24 +192,31 @@ def main():
if not options.unattended and ( not netbios_name or not
options.netbios_name):
netbios_name = read_netbios_name(netbios_name)
- dm_password = options.dm_password or read_password("Directory Manager",
- confirm=False, validate=False)
- smb = adtrustinstance.ADTRUSTInstance(fstore, dm_password)
+ try:
+ ctx = krbV.default_context()
+ ccache = ctx.default_ccache()
+ principal = ccache.principal()
+ except krbV.Krb5Error, e:
+ sys.exit("Must have Kerberos credentials to setup AD trusts on server")
- # try the connection
try:
- smb.ldap_connect()
- smb.ldap_disconnect()
- except ldap.INVALID_CREDENTIALS, e:
- sys.exit("Password is not valid!")
+ api.Backend.ldap2.connect(ccache.name)
+ except errors.ACIError, e:
+ sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to
update your ticket")
- if smb.dm_password:
- api.Backend.ldap2.connect(bind_dn="cn=Directory Manager",
bind_pw=smb.dm_password)
- else:
- # See if our LDAP server is up and we can talk to it over GSSAPI
- ccache = krbV.default_context().default_ccache().name
- api.Backend.ldap2.connect(ccache)
+ try:
+ user = api.Command.user_show(unicode(principal[0]))['result']
+ group = api.Command.group_show(u'admins')['result']
+ if not (user['uid'][0] in group['member_user'] and
+ group['cn'][0] in user['memberof_group']):
+ raise errors.RequirementError(name='admins group membership')
+ except errors.RequirementError, e:
+ sys.exit("Must have administrative privileges to setup AD trusts on
server")
+ except Exception, e:
+ sys.exit("Unrecognized error during check of admin rights: %s" %
(str(e)))
+ smb = adtrustinstance.ADTRUSTInstance(fstore)
+ smb.realm = api.env.realm
smb.setup(api.env.host, ip_address, api.env.realm, api.env.domain,
netbios_name, options.rid_base, options.secondary_rid_base,
options.no_msdcs)
diff --git a/install/tools/man/ipa-adtrust-install.1
b/install/tools/man/ipa-adtrust-install.1
index
b61da19088b40d6a9e53784f9a061913ecda4321..22337c3df8827670657bf405b6c49ba2f8624d6d
100644
--- a/install/tools/man/ipa-adtrust-install.1
+++ b/install/tools/man/ipa-adtrust-install.1
@@ -27,9 +27,6 @@ trust to an Active Directory domain. This requires that the
IPA server is
already installed and configured.
.SH "OPTIONS"
.TP
-\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-ds\-password\fR=\fIDM_PASSWORD\fR
-The password to be used by the Directory Server for the Directory Manager user
-.TP
\fB\-d\fR, \fB\-\-debug\fR
Enable debug logging when more verbose output is needed
.TP
diff --git a/ipaserver/install/adtrustinstance.py
b/ipaserver/install/adtrustinstance.py
index
20feec4df309b5793aa1c29fdf18bc5bfe180943..9dcbec2d61d935f90e74cc65b30a0f1d0c0f9d2a
100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -96,10 +96,9 @@ class ADTRUSTInstance(service.Service):
OBJC_GROUP = "ipaNTGroupAttrs"
OBJC_DOMAIN = "ipaNTDomainAttrs"
- def __init__(self, fstore=None, dm_password=None):
+ def __init__(self, fstore=None):
self.fqdn = None
self.ip_address = None
- self.realm_name = None
self.domain_name = None
self.netbios_name = None
self.no_msdcs = None
@@ -118,7 +117,7 @@ class ADTRUSTInstance(service.Service):
self.rid_base = None
self.secondary_rid_base = None
- service.Service.__init__(self, "smb", dm_password=dm_password)
+ service.Service.__init__(self, "smb", dm_password=None, ldapi=True)
if fstore:
self.fstore = fstore
@@ -436,6 +435,8 @@ class ADTRUSTInstance(service.Service):
# We do not let the system start IPA components on its own,
# Instead we reply on the IPA init script to start only enabled
# components as found in our LDAP configuration tree
+ # Note that self.dm_password is None for ADTrustInstance because
+ # we ensure to be called as root and using ldapi to use autobind
try:
self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, \
self.suffix)
@@ -449,7 +450,7 @@ class ADTRUSTInstance(service.Service):
root_logger.info("EXTID Service startup entry already exists.")
def __setup_sub_dict(self):
- self.sub_dict = dict(REALM = self.realm_name,
+ self.sub_dict = dict(REALM = self.realm,
SUFFIX = self.suffix,
NETBIOS_NAME = self.netbios_name,
SMB_DN = self.smb_dn,
@@ -460,16 +461,16 @@ class ADTRUSTInstance(service.Service):
rid_base, secondary_rid_base, no_msdcs=False, smbd_user="samba"):
self.fqdn = fqdn
self.ip_address = ip_address
- self.realm_name = realm_name
+ self.realm = realm_name
self.domain_name = domain_name
self.netbios_name = netbios_name
self.rid_base = rid_base
self.secondary_rid_base = secondary_rid_base
self.no_msdcs = no_msdcs
self.smbd_user = smbd_user
- self.suffix = ipautil.realm_to_suffix(self.realm_name)
+ self.suffix = ipautil.realm_to_suffix(self.realm)
self.ldapi_socket = "%%2fvar%%2frun%%2fslapd-%s.socket" % \
- realm_to_serverid(self.realm_name)
+ realm_to_serverid(self.realm)
self.smb_conf = "/etc/samba/smb.conf"
@@ -479,7 +480,7 @@ class ADTRUSTInstance(service.Service):
self.trust_dn = str(DN(api.env.container_trusts, self.suffix))
self.smb_dom_dn = str(DN(('cn', self.domain_name),
api.env.container_cifsdomains, self.suffix))
- self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
+ self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm
self.cifs_agent = str(DN(('krbprincipalname',
self.cifs_principal.lower()),
api.env.container_service,
self.suffix))
@@ -522,11 +523,11 @@ class ADTRUSTInstance(service.Service):
"range.\nAdd local ID range manually and try " \
"again!")
- entry = ipaldap.Entry(str(DN(('cn', ('%s_id_range' % self.realm_name)),
+ entry = ipaldap.Entry(str(DN(('cn', ('%s_id_range' % self.realm)),
api.env.container_ranges,
self.suffix)))
entry.setValue('objectclass', 'ipaDomainIDRange')
- entry.setValue('cn', ('%s_id_range' % self.realm_name))
+ entry.setValue('cn', ('%s_id_range' % self.realm))
entry.setValue('ipaBaseID', str(base_id))
entry.setValue('ipaIDRangeSize', str(id_range_size))
self.admin_conn.addEntry(entry)
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index
5c2699e3fa4c115c972528d4c2cc6aa170711837..f08fd47306c301d67483959034c163accd6f3b6e
100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -55,7 +55,7 @@ def print_msg(message, output_fd=sys.stdout):
class Service(object):
- def __init__(self, service_name, sstore=None, dm_password=None,
ldapi=False):
+ def __init__(self, service_name, sstore=None, dm_password=None,
ldapi=True):
self.service_name = service_name
self.service = ipaservices.service(service_name)
self.steps = []
@@ -77,12 +77,16 @@ class Service(object):
self.dercert = None
def ldap_connect(self):
- if self.ldapi:
- if not self.realm:
- raise RuntimeError('realm must be set to use ldapi connection')
- self.admin_conn = self.__get_conn(None, None, ldapi=True,
realm=self.realm)
- else:
+ # If DM password is provided, we use it
+ # Otherwise, we attempt LDAPI connection if realm is available and
+ # ldapi was asked for
+ # Finally, we attempt to connect to self.fqdn with GSSAPI
+ if self.dm_password:
self.admin_conn = self.__get_conn(self.fqdn, self.dm_password)
+ elif self.ldapi and self.realm:
+ self.admin_conn = self.__get_conn(None, None, ldapi=True,
realm=self.realm)
+ else:
+ self.admin_conn = self.__get_conn(self.fqdn, None,
ldapi=self.ldapi, realm=self.realm)
def ldap_disconnect(self):
self.admin_conn.unbind()
@@ -93,7 +97,6 @@ class Service(object):
pw_name = None
fd = None
path = ipautil.SHARE_DIR + ldif
- hostname = installutils.get_fqdn()
nologlist=[]
if sub_dict is not None:
@@ -107,15 +110,25 @@ class Service(object):
if sub_dict.has_key('RANDOM_PASSWORD'):
nologlist.append(sub_dict['RANDOM_PASSWORD'])
+ args = ["/usr/bin/ldapmodify", "-v", "-f", path]
+
+ # As we always connect to the local host,
+ # use URI of admin connection
+ if not self.admin_conn:
+ self.ldap_connect()
+ args += ["-H", self.admin_conn._uri]
+
+ auth_parms = []
if self.dm_password:
[pw_fd, pw_name] = tempfile.mkstemp()
os.write(pw_fd, self.dm_password)
os.close(pw_fd)
auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name]
else:
- auth_parms = ["-Y", "GSSAPI"]
+ # always try GSSAPI auth when not using DM password or not being
root
+ if os.getegid() != 0:
+ auth_parms = ["-Y", "GSSAPI"]
- args = ["/usr/bin/ldapmodify", "-h", hostname, "-v", "-f", path]
args += auth_parms
try:
@@ -181,8 +194,19 @@ class Service(object):
This server cert should be in DER format.
"""
- if not self.admin_conn:
- self.ldap_connect()
+ # add_cert_to_service() is relatively rare operation
+ # we actually call it twice during ipa-server-install, for different
+ # instances: ds and cs. Unfortunately, it may happen that admin
+ # connection was created well before add_cert_to_service() is called
+ # If there are other operations in between, it will become stale and
+ # since we are using SimpleLDAPObject, not ReconnectLDAPObject, the
+ # action will fail. Thus, explicitly disconnect and connect again.
+ # Using ReconnectLDAPObject instead of SimpleLDAPObject was considered
+ # but consequences for other parts of the framework are largely
+ # unknown.
+ if self.admin_conn:
+ self.ldap_disconnect()
+ self.ldap_connect()
dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" %
(self.principal, self.suffix)
mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
@@ -270,16 +294,17 @@ class Service(object):
def __get_conn(self, fqdn, dm_password, ldapi=False, realm=None):
# If we are passed a password we'll use it as the DM password
- # otherwise we'll do a GSSAPI bind.
+ # otherwise we'll do a GSSAPI bind unless we are using LDAPI under
+ # root, in which case we'll try autobind and revert to GSSAPI if
+ # that attempt has failed
try:
-# conn = ipaldap.IPAdmin(fqdn, port=636, cacert=CACERT)
if ldapi:
conn = ipaldap.IPAdmin(ldapi=ldapi, realm=realm)
else:
conn = ipaldap.IPAdmin(fqdn, port=389)
if dm_password:
conn.do_simple_bind(bindpw=dm_password)
- elif os.getegid() == 0 and self.ldapi:
+ elif os.getegid() == 0 and ldapi:
try:
# autobind
pw_name = pwd.getpwuid(os.geteuid()).pw_name
--
1.7.11.2
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel