@PYTHONSHEBANG@ # Authors: # Christian Heimes # # Copyright (C) 2015 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, either version 3 of the License, or # (at your option) any later version. # # 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, see . # """ipa-httpd-kdproxy This script creates or removes the symlink from /etc/ipa/ipa-kdc-proxy.conf to /etc/httpd/conf.d/. It's called from ExecStartPre hook in httpd.service. """ import logging import os import socket import sys from ipalib import api, errors from ipapython.ipa_log_manager import standard_logging_setup from ipapython.ipaldap import LDAPClient from ipapython.dn import DN from ipaplatform.paths import paths logger = logging.getLogger(os.path.basename(__file__)) DEBUG = False TIME_LIMIT = 2 class Error(Exception): """Base error class""" class ConfigFileError(Error): """Something is wrong with the config file""" class CheckError(Error): """An unrecoverable error has occured The exit code is 0. """ class FatalError(Error): """A fatal error has occured Fatal errors cause the command to exit with a non-null exit code. """ class KDCProxyConfig(object): ipaconfig_flag = 'ipaKDCProxyEnabled' def __init__(self, time_limit=TIME_LIMIT): self.time_limit = time_limit self.con = None self.ldap_uri = api.env.ldap_uri self.kdc_dn = DN(('cn', 'KDC'), ('cn', api.env.host), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) self.conf = paths.HTTPD_IPA_KDCPROXY_CONF self.conflink = paths.HTTPD_IPA_KDCPROXY_CONF_SYMLINK def _ldap_con(self): """Establish LDAP connection""" logger.debug('ldap_uri: %s', self.ldap_uri) try: self.con = LDAPClient(self.ldap_uri) self.con.external_bind() except (errors.NetworkError, socket.timeout) as e: msg = 'Unable to connect to dirsrv: %s' % e raise CheckError(msg) except errors.AuthorizationError as e: msg = 'Authorization error: %s' % e raise CheckError(msg) except Exception as e: msg = ('Unknown error while retrieving setting from %s: %s' % (self.ldap_uri, e)) logger.exception('%s', msg) raise FatalError(msg) def _find_entry(self, dn, attrs, filter, scope=LDAPClient.SCOPE_BASE): """Find an LDAP entry, handles NotFound and Limit""" try: entries = self.con.get_entries( dn, scope, filter, attrs, time_limit=self.time_limit) except errors.NotFound: logger.debug('Entry not found: %s', dn) return None except Exception as e: msg = ('Unknown error while retrieving setting from %s: %s' % (self.ldap_uri, e)) logger.exception('%s', msg) raise FatalError(msg) return entries[0] def is_host_enabled(self): """Check replica specific flag""" logger.debug('Read settings from dn: %s', self.kdc_dn) srcfilter = self.con.make_filter( {'ipaConfigString': u'kdcProxyEnabled'} ) entry = self._find_entry(self.kdc_dn, ['cn'], srcfilter) logger.debug('%s ipaConfigString: %s', self.kdc_dn, entry) return entry is not None def validate_symlink(self): """Validate symlink in Apache conf.d""" if not os.path.exists(self.conflink): return False if not os.path.islink(self.conflink): raise ConfigFileError( "'%s' already exists, but it is not a symlink" % self.conflink) dest = os.readlink(self.conflink) if dest != self.conf: raise ConfigFileError( "'%s' points to '%s', expected '%s'" % (self.conflink, dest, self.conf)) return True def create_symlink(self): """Create symlink to enable KDC proxy support""" try: valid = self.validate_symlink() except ConfigFileError as e: logger.warning("Cannot enable KDC proxy: %s ", e) return False if valid: logger.debug("Symlink exists and is valid") return True if not os.path.isfile(self.conf): logger.warning("'%s' does not exist", self.conf) return False # create the symbolic link logger.debug("Creating symlink from '%s' to '%s'", self.conf, self.conflink) os.symlink(self.conf, self.conflink) return True def remove_symlink(self): """Delete symlink to disable KDC proxy support""" try: valid = self.validate_symlink() except CheckError as e: logger.warning("Cannot disable KDC proxy: %s ", e) return False if valid: logger.debug("Removing symlink '%s'", self.conflink) os.unlink(self.conflink) else: logger.debug("Symlink '%s' has already been removed.", self.conflink) return True def __enter__(self): self._ldap_con() return self def __exit__(self, exc_type, exc_value, traceback): if self.con is not None: self.con.unbind() self.con = None def main(debug=DEBUG, time_limit=TIME_LIMIT): # initialize API without file logging if not api.isdone('bootstrap'): api.bootstrap(context='server', confdir=paths.ETC_IPA, log=None, debug=debug) standard_logging_setup(verbose=True, debug=debug) try: cfg = KDCProxyConfig(time_limit) with cfg: if cfg.is_host_enabled(): if cfg.create_symlink(): logger.info('KDC proxy enabled') return 0 else: if cfg.remove_symlink(): logger.info('KDC proxy disabled') return 0 except CheckError as e: logger.warning('%s', str(e)) logger.warning('Disabling KDC proxy') cfg.remove_symlink() return 0 except Exception as e: logger.error('%s', str(e)) return 1 if __name__ == '__main__': sys.exit(main())