diff options
-rwxr-xr-x | install/tools/ipa-ldap-updater | 42 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 1 | ||||
-rw-r--r-- | ipaserver/install/ldapupdate.py | 51 | ||||
-rw-r--r-- | ipaserver/install/upgradeinstance.py | 117 | ||||
-rw-r--r-- | ipaserver/ipaldap.py | 14 |
5 files changed, 192 insertions, 33 deletions
diff --git a/install/tools/ipa-ldap-updater b/install/tools/ipa-ldap-updater index 746cd421d..f3b83ce06 100755 --- a/install/tools/ipa-ldap-updater +++ b/install/tools/ipa-ldap-updater @@ -26,16 +26,12 @@ import sys try: from optparse import OptionParser - from ipapython import entity, ipautil, config + from ipapython import ipautil, config from ipaserver.install import installutils from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax, UPDATES_DIR + from ipaserver.install.upgradeinstance import IPAUpgrade import logging - import re import krbV - import platform - import shlex - import time - import random except ImportError: print >> sys.stderr, """\ There was a problem importing one of the required Python modules. The @@ -56,6 +52,10 @@ def parse_options(): help="Run through the update without changing anything") parser.add_option("-y", dest="password", help="File containing the Directory Manager password") + parser.add_option("-l", '--ldapi', action="store_true", dest="ldapi", + default=False, help="Connect to the LDAP server using the ldapi socket") + parser.add_option("-u", '--upgrade', action="store_true", dest="upgrade", + default=False, help="Upgrade an installed server in offline mode") config.add_standard_options(parser) options, args = parser.parse_args() @@ -79,25 +79,33 @@ def main(): if options.debug: loglevel = logging.DEBUG - logging.basicConfig(level=loglevel, - format='%(levelname)s %(message)s') - dirman_password = "" if options.password: pw = ipautil.template_file(options.password, []) dirman_password = pw.strip() else: - dirman_password = get_dirman_password() - - ld = LDAPUpdate(dm_password=dirman_password, sub_dict={}, live_run=not options.test) + if not options.ldapi and not options.upgrade: + dirman_password = get_dirman_password() - files=[] - if len(args) < 1: - files = ld.get_all_files(UPDATES_DIR) - else: + files = [] + if len(args) > 0: files = args - modified = ld.update(files) + if options.upgrade: + logging.basicConfig(level=loglevel, + format='%(levelname)s %(message)s', + filename='/var/log/ipaupgrade.log') + realm = krbV.default_context().default_realm + upgrade = IPAUpgrade(realm, files, live_run=not options.test) + upgrade.create_instance() + modified = upgrade.modified + else: + logging.basicConfig(level=loglevel, + format='%(levelname)s %(message)s') + ld = LDAPUpdate(dm_password=dirman_password, sub_dict={}, live_run=not options.test, ldapi=options.ldapi) + if len(files) < 1: + files = ld.get_all_files(UPDATES_DIR) + modified = ld.update(files) if modified and options.test: return 2 diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 5049962b9..0767f0c85 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -306,6 +306,7 @@ def get_directive(filename, directive, separator=' '): line = line.strip() result = line.split(separator, 1)[1] result = result.strip('"') + result = result.strip(' ') fd.close() return result fd.close() diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index dff94783c..91ff80f55 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -38,6 +38,7 @@ import platform import time import random import os +import pwd import fnmatch import csv @@ -48,16 +49,23 @@ class BadSyntax(Exception): return repr(self.value) class LDAPUpdate: - def __init__(self, dm_password, sub_dict={}, live_run=True): + def __init__(self, dm_password, sub_dict={}, live_run=True, + online=True, ldapi=False): """dm_password = Directory Manager password sub_dict = substitution dictionary live_run = Apply the changes or just test + online = do an online LDAP update or use an experimental LDIF updater + ldapi = bind using ldapi. This assumes autobind is enabled. """ self.sub_dict = sub_dict self.live_run = live_run self.dm_password = dm_password self.conn = None self.modified = False + self.online = online + self.ldapi = ldapi + + self.pw_name = pwd.getpwuid(os.geteuid()).pw_name krbctx = krbV.default_context() @@ -68,6 +76,7 @@ class LDAPUpdate: domain = ipautil.get_domain_name() libarch = self.__identify_arch() suffix = util.realm_to_suffix(krbctx.default_realm) + self.realm = krbctx.default_realm if not self.sub_dict.get("REALM"): self.sub_dict["REALM"] = krbctx.default_realm @@ -84,17 +93,24 @@ class LDAPUpdate: if not self.sub_dict.get("TIME"): self.sub_dict["TIME"] = int(time.time()) - # Try out the password - try: - conn = ipaldap.IPAdmin(fqdn) - conn.do_simple_bind(bindpw=self.dm_password) - conn.unbind() - except ldap.CONNECT_ERROR: - raise RuntimeError("Unable to connect to LDAP server %s" % fqdn) - except ldap.SERVER_DOWN: - raise RuntimeError("Unable to connect to LDAP server %s" % fqdn) - except ldap.INVALID_CREDENTIALS: - raise RuntimeError("The password provided is incorrect for LDAP server %s" % fqdn) + if online: + # Try out the password + if not self.ldapi: + try: + conn = ipaldap.IPAdmin(fqdn) + conn.do_simple_bind(bindpw=self.dm_password) + conn.unbind() + except ldap.CONNECT_ERROR: + raise RuntimeError("Unable to connect to LDAP server %s" % fqdn) + except ldap.SERVER_DOWN: + raise RuntimeError("Unable to connect to LDAP server %s" % fqdn) + except ldap.INVALID_CREDENTIALS: + raise RuntimeError("The password provided is incorrect for LDAP server %s" % fqdn) + else: + conn = ipaldap.IPAdmin(ldapi=True, realm=self.realm) + conn.do_external_bind(self.pw_name) + else: + raise RuntimeError("Offline updates are not supported.") # The following 2 functions were taken from the Python # documentation at http://docs.python.org/library/csv.html @@ -595,8 +611,15 @@ class LDAPUpdate: """ try: - self.conn = ipaldap.IPAdmin(self.sub_dict['FQDN']) - self.conn.do_simple_bind(bindpw=self.dm_password) + if self.online: + if self.ldapi: + self.conn = ipaldap.IPAdmin(ldapi=True, realm=self.realm) + self.conn.do_external_bind(self.pw_name) + else: + self.conn = ipaldap.IPAdmin(self.sub_dict['FQDN']) + self.conn.do_simple_bind(bindpw=self.dm_password) + else: + raise RuntimeError("Offline updates are not supported.") all_updates = {} dn_list = {} for f in files: diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py new file mode 100644 index 000000000..79a7f9545 --- /dev/null +++ b/ipaserver/install/upgradeinstance.py @@ -0,0 +1,117 @@ +# Authors: Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2010 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import os +import sys + +from ipaserver.install import installutils +from ipaserver.install import dsinstance +from ipaserver.install import ldapupdate +from ipaserver.install import service + +DSBASE = '/etc/dirsrv/slapd-' +DSE = 'dse.ldif' + +class IPAUpgrade(service.Service): + """ + Update the LDAP data in an instance by turning off all network + listeners and updating over ldapi. This way we know the server is + quiet. + """ + def __init__(self, realm_name, files=[], live_run=True): + """ + realm_name: kerberos realm name, used to determine DS instance dir + files: list of update files to process. If none use UPDATEDIR + live_run: boolean that defines if we are in test or live mode. + """ + + service.Service.__init__(self, "dirsrv") + serverid = dsinstance.realm_to_serverid(realm_name) + self.filename = '%s%s/%s' % (DSBASE, serverid, DSE) + self.live_run = live_run + self.files = files + self.modified = False + + def create_instance(self): + self.step("stopping directory server", self.stop) + self.step("saving configuration", self.__save_config) + self.step("disabling listeners", self.__disable_listeners) + self.step("starting directory server", self.start) + self.step("upgrading server", self.__upgrade) + self.step("stopping directory server", self.stop) + self.step("restoring configuration", self.__restore_config) + self.step("starting directory server", self.start) + + self.start_creation("Upgrading IPA:") + + def __save_config(self): + port = installutils.get_directive(self.filename, 'nsslapd-port', + separator=':') + security = installutils.get_directive(self.filename, 'nsslapd-security', + separator=':') + autobind = installutils.get_directive(self.filename, + 'nsslapd-ldapiautobind', separator=':') + + self.backup_state('nsslapd-port', port) + self.backup_state('nsslapd-security', security) + self.backup_state('nsslapd-ldapiautobind', autobind) + + def __restore_config(self): + port = self.restore_state('nsslapd-port') + security = self.restore_state('nsslapd-security') + autobind = self.restore_state('nsslapd-ldapiautobind') + + installutils.set_directive(self.filename, 'nsslapd-port', + port, quotes=False, separator=':') + installutils.set_directive(self.filename, 'nsslapd-security', + security, quotes=False, separator=':') + installutils.set_directive(self.filename, 'nsslapd-ldapiautobind', + autobind, quotes=False, separator=':') + + def __disable_listeners(self): + installutils.set_directive(self.filename, 'nsslapd-port', + 0, quotes=False, separator=':') + installutils.set_directive(self.filename, 'nsslapd-security', + 'off', quotes=False, separator=':') + installutils.set_directive(self.filename, 'nsslapd-ldapiautobind', + 'on', quotes=False, separator=':') + + def __upgrade(self): + ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True, live_run=self.live_run) + if len(self.files) == 0: + self.files = ld.get_all_files(ldapupdate.UPDATES_DIR) + self.modified = ld.update(self.files) + +def main(): + if os.getegid() != 0: + print "Must be root to set up server" + return 1 + + update = IPAUpgrade('EXAMPLE.COM') + update.create_instance() + + return 0 + +try: + if __name__ == "__main__": + sys.exit(main()) +except SystemExit, e: + sys.exit(e) +except KeyboardInterrupt, e: + sys.exit(1) diff --git a/ipaserver/ipaldap.py b/ipaserver/ipaldap.py index 3d0b321e0..578894ab5 100644 --- a/ipaserver/ipaldap.py +++ b/ipaserver/ipaldap.py @@ -217,9 +217,12 @@ class IPAdmin(SimpleLDAPObject): if self.cacert is not None: SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port)) else: - SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port)) + if self.ldapi: + SimpleLDAPObject.__init__(self,'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % "-".join(self.realm.split("."))) + else: + SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port)) - def __init__(self,host,port=389,cacert=None,bindcert=None,bindkey=None,proxydn=None,debug=None): + def __init__(self,host='',port=389,cacert=None,bindcert=None,bindkey=None,proxydn=None,debug=None,ldapi=False,realm=None): """We just set our instance variables and wrap the methods - the real work is done in __localinit. This is separated out this way so that we can call it from places other than instance creation @@ -241,6 +244,8 @@ class IPAdmin(SimpleLDAPObject): self.bindcert = bindcert self.bindkey = bindkey self.proxydn = proxydn + self.ldapi = ldapi + self.realm = realm self.suffixes = {} self.__localinit() @@ -345,6 +350,11 @@ class IPAdmin(SimpleLDAPObject): self.simple_bind_s(binddn, bindpw) self.__lateinit() + def do_external_bind(self, user_name=None): + auth_tokens = ldap.sasl.external(user_name) + self.sasl_interactive_bind_s("", auth_tokens) + self.__lateinit() + def getEntry(self,*args): """This wraps the search function. It is common to just get one entry""" |