summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2012-07-13 18:12:48 +0300
committerMartin Kosek <mkosek@redhat.com>2012-07-31 17:44:35 +0200
commit68d5fe1ec7d785f127b3513f84cc632cdb1f9167 (patch)
treec0723e680c929f19f4fd2cb61eb7dfd93287d267
parent16ca564b1004eb672fe4ca3573e542f5a3ce014b (diff)
downloadfreeipa-68d5fe1ec7d785f127b3513f84cc632cdb1f9167.tar.gz
freeipa-68d5fe1ec7d785f127b3513f84cc632cdb1f9167.tar.xz
freeipa-68d5fe1ec7d785f127b3513f84cc632cdb1f9167.zip
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
-rwxr-xr-xinstall/tools/ipa-adtrust-install48
-rw-r--r--install/tools/man/ipa-adtrust-install.13
-rw-r--r--ipaserver/install/adtrustinstance.py21
-rw-r--r--ipaserver/install/bindinstance.py2
-rw-r--r--ipaserver/install/cainstance.py3
-rw-r--r--ipaserver/install/dsinstance.py2
-rw-r--r--ipaserver/install/krbinstance.py2
-rw-r--r--ipaserver/install/service.py114
8 files changed, 117 insertions, 78 deletions
diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 6678018e6..02a309306 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -24,7 +24,7 @@
from ipaserver.plugins.ldap2 import ldap2
from ipaserver.install import adtrustinstance
from ipaserver.install.installutils import *
-from ipaserver.install import installutils
+from ipaserver.install import service
from ipapython import version
from ipapython import ipautil, sysrestore
from ipalib import api, errors, util
@@ -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",
@@ -98,7 +96,7 @@ def main():
root_logger.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
root_logger.debug("missing options might be asked for interactively later\n")
- installutils.check_server_configuration()
+ check_server_configuration()
global fstore
fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
@@ -194,24 +192,34 @@ 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:
+ api.Backend.ldap2.connect(ccache.name)
+ except errors.ACIError, e:
+ sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket")
+ except errors.DatabaseError, e:
+ sys.exit("Cannot connect to the LDAP database. Please check if IPA is running")
- # try the connection
try:
- smb.ldap_connect()
- smb.ldap_disconnect()
- except ldap.INVALID_CREDENTIALS, e:
- sys.exit("Password is not valid!")
-
- 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)
+ 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.autobind = service.ENABLED
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)
@@ -250,5 +258,5 @@ information"""
return 0
if __name__ == '__main__':
- installutils.run_script(main, log_file_name=log_file_name,
+ run_script(main, log_file_name=log_file_name,
operation_name='ipa-adtrust-install')
diff --git a/install/tools/man/ipa-adtrust-install.1 b/install/tools/man/ipa-adtrust-install.1
index b61da1908..22337c3df 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 20feec4df..9dcbec2d6 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/bindinstance.py b/ipaserver/install/bindinstance.py
index c348cdbb2..f320202ea 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -448,7 +448,7 @@ class DnsBackup(object):
class BindInstance(service.Service):
def __init__(self, fstore=None, dm_password=None):
- service.Service.__init__(self, "named", dm_password=dm_password)
+ service.Service.__init__(self, "named", dm_password=dm_password, ldapi=False, autobind=service.DISABLED)
self.dns_backup = DnsBackup(self)
self.named_user = None
self.domain = None
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 2644689a0..dc4374cce 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -225,10 +225,9 @@ def get_outputList(data):
class CADSInstance(service.Service):
def __init__(self, host_name=None, realm_name=None, domain_name=None, dm_password=None):
- service.Service.__init__(self, "pkids")
+ service.Service.__init__(self, "pkids", dm_password=dm_password, ldapi=False, autobind=service.DISABLED)
self.serverid = "PKI-IPA"
self.realm_name = realm_name
- self.dm_password = dm_password
self.sub_dict = None
self.domain = domain_name
self.fqdn = host_name
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 25c449a6e..9f3ae7252 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -160,7 +160,7 @@ info: IPA V2.0
class DsInstance(service.Service):
def __init__(self, realm_name=None, domain_name=None, dm_password=None, fstore=None):
- service.Service.__init__(self, "dirsrv", dm_password=dm_password)
+ service.Service.__init__(self, "dirsrv", dm_password=dm_password, ldapi=False, autobind=service.DISABLED)
self.realm_name = realm_name
self.sub_dict = None
self.domain = domain_name
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 2faf8e196..8cc50fba4 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -178,7 +178,7 @@ class KrbInstance(service.Service):
self.start_creation("Configuring Kerberos KDC", 30)
self.kpasswd = KpasswdInstance()
- self.kpasswd.create_instance('KPASSWD', self.fqdn, self.admin_password, self.suffix)
+ self.kpasswd.create_instance('KPASSWD', self.fqdn, self.admin_password, self.suffix, realm=self.realm)
def create_replica(self, realm_name,
master_fqdn, host_name,
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index 5c2699e3f..198cb3870 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -35,6 +35,11 @@ from ipapython.ipa_log_manager import *
CACERT = "/etc/ipa/ca.crt"
+# Autobind modes
+AUTO = 1
+ENABLED = 2
+DISABLED = 3
+
# The service name as stored in cn=masters,cn=ipa,cn=etc. In the tuple
# the first value is the *nix service name, the second the start order.
SERVICE_LIST = {
@@ -55,13 +60,14 @@ 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, autobind=AUTO):
self.service_name = service_name
self.service = ipaservices.service(service_name)
self.steps = []
self.output_fd = sys.stdout
self.dm_password = dm_password
self.ldapi = ldapi
+ self.autobind = autobind
self.fqdn = socket.gethostname()
self.admin_conn = None
@@ -77,12 +83,44 @@ 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:
- self.admin_conn = self.__get_conn(self.fqdn, self.dm_password)
+ # If DM password is provided, we use it
+ # If autobind was requested, attempt autobind when root and ldapi
+ # If autobind was disabled or not succeeded, go with GSSAPI
+ # LDAPI can be used with either autobind or GSSAPI
+ # LDAPI requires realm to be set
+ try:
+ if self.ldapi:
+ if not self.realm:
+ raise errors.NotFound(reason="realm is missing for %s" % (self))
+ conn = ipaldap.IPAdmin(ldapi=self.ldapi, realm=self.realm)
+ else:
+ conn = ipaldap.IPAdmin(self.fqdn, port=389)
+ if self.dm_password:
+ conn.do_simple_bind(bindpw=self.dm_password)
+ elif self.autobind in [AUTO, ENABLED]:
+ if os.getegid() == 0 and self.ldapi:
+ try:
+ # autobind
+ pw_name = pwd.getpwuid(os.geteuid()).pw_name
+ conn.do_external_bind(pw_name)
+ except errors.NotFound, e:
+ if self.autobind == AUTO:
+ # Fall back
+ conn.do_sasl_gssapi_bind()
+ else:
+ # autobind was required and failed, raise
+ # exception that it failed
+ raise e
+ else:
+ conn.do_sasl_gssapi_bind()
+ else:
+ conn.do_sasl_gssapi_bind()
+ except Exception, e:
+ root_logger.debug("Could not connect to the Directory Server on %s: %s" % (self.fqdn, str(e)))
+ raise e
+
+ self.admin_conn = conn
+
def ldap_disconnect(self):
self.admin_conn.unbind()
@@ -93,7 +131,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 +144,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 +228,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)]
@@ -268,33 +326,6 @@ class Service(object):
self.steps = []
- 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.
- 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:
- try:
- # autobind
- pw_name = pwd.getpwuid(os.geteuid()).pw_name
- conn.do_external_bind(pw_name)
- except errors.NotFound:
- # Fall back
- conn.do_sasl_gssapi_bind()
- else:
- conn.do_sasl_gssapi_bind()
- except Exception, e:
- root_logger.debug("Could not connect to the Directory Server on %s: %s" % (fqdn, str(e)))
- raise e
-
- return conn
-
def ldap_enable(self, name, fqdn, dm_password, ldap_suffix):
self.disable()
if not self.admin_conn:
@@ -318,11 +349,14 @@ class Service(object):
raise e
class SimpleServiceInstance(Service):
- def create_instance(self, gensvc_name=None, fqdn=None, dm_password=None, ldap_suffix=None):
+ def create_instance(self, gensvc_name=None, fqdn=None, dm_password=None, ldap_suffix=None, realm=None):
self.gensvc_name = gensvc_name
self.fqdn = fqdn
self.dm_password = dm_password
self.suffix = ldap_suffix
+ self.realm = realm
+ if not realm:
+ self.ldapi = False
self.step("starting %s " % self.service_name, self.__start)
self.step("configuring %s to start on boot" % self.service_name, self.__enable)