diff options
author | Christian Heimes <cheimes@redhat.com> | 2015-06-23 17:01:00 +0200 |
---|---|---|
committer | Petr Vobornik <pvoborni@redhat.com> | 2015-06-24 10:43:58 +0200 |
commit | 495da412f155603c02907187c21dd4511281df2c (patch) | |
tree | 8bc25d341bfdfb48673fbc24ba3f538ef87b6d41 /install | |
parent | 49d708f00fd13903dbd96193aac2c608e3512398 (diff) | |
download | freeipa-495da412f155603c02907187c21dd4511281df2c.tar.gz freeipa-495da412f155603c02907187c21dd4511281df2c.tar.xz freeipa-495da412f155603c02907187c21dd4511281df2c.zip |
Provide Kerberos over HTTP (MS-KKDCP)
Add integration of python-kdcproxy into FreeIPA to support the MS
Kerberos KDC proxy protocol (MS-KKDCP), to allow KDC and KPASSWD
client requests over HTTP and HTTPS.
- freeipa-server now depends on python-kdcproxy >= 0.3. All kdcproxy
dependencies are already satisfied.
- The service's state is configured in cn=KDC,cn=$FQDN,cn=masters,cn=ipa,
cn=etc,$SUFFIX. It's enabled, when ipaConfigString=kdcProxyEnabled is
present.
- The installers and update create a new Apache config file
/etc/ipa/kdcproxy/ipa-kdc-proxy.conf that mounts a WSGI app on
/KdcProxy. The app is run inside its own WSGI daemon group with
a different uid and gid than the webui.
- A ExecStartPre script in httpd.service symlinks the config file to
/etc/httpd/conf.d/ iff ipaConfigString=kdcProxyEnabled is present.
- The httpd.service also sets KDCPROXY_CONFIG=/etc/ipa/kdcproxy.conf,
so that an existing config is not used. SetEnv from Apache config does
not work here, because it doesn't set an OS env var.
- python-kdcproxy is configured to *not* use DNS SRV lookups. The
location of KDC and KPASSWD servers are read from /etc/krb5.conf.
- The state of the service can be modified with two ldif files for
ipa-ldap-updater. No CLI script is offered yet.
https://www.freeipa.org/page/V4/KDC_Proxy
https://fedorahosted.org/freeipa/ticket/4801
Reviewed-By: Nathaniel McCallum <npmccallum@redhat.com>
Reviewed-By: Simo Sorce <ssorce@redhat.com>
Diffstat (limited to 'install')
-rw-r--r-- | install/conf/Makefile.am | 1 | ||||
-rw-r--r-- | install/conf/ipa-kdc-proxy.conf.template | 30 | ||||
-rw-r--r-- | install/conf/ipa.conf | 6 | ||||
-rw-r--r-- | install/share/Makefile.am | 3 | ||||
-rw-r--r-- | install/share/kdcproxy-disable.uldif | 3 | ||||
-rw-r--r-- | install/share/kdcproxy-enable.uldif | 6 | ||||
-rw-r--r-- | install/share/kdcproxy.conf | 4 | ||||
-rw-r--r-- | install/tools/Makefile.am | 5 | ||||
-rwxr-xr-x | install/tools/ipa-httpd-kdcproxy | 180 |
9 files changed, 235 insertions, 3 deletions
diff --git a/install/conf/Makefile.am b/install/conf/Makefile.am index 65e25bc94..5daac776f 100644 --- a/install/conf/Makefile.am +++ b/install/conf/Makefile.am @@ -3,6 +3,7 @@ NULL = appdir = $(IPA_DATA_DIR) app_DATA = \ ipa.conf \ + ipa-kdc-proxy.conf.template \ ipa-pki-proxy.conf \ ipa-rewrite.conf \ $(NULL) diff --git a/install/conf/ipa-kdc-proxy.conf.template b/install/conf/ipa-kdc-proxy.conf.template new file mode 100644 index 000000000..9290cebba --- /dev/null +++ b/install/conf/ipa-kdc-proxy.conf.template @@ -0,0 +1,30 @@ +# Kerberos over HTTP / MS-KKDCP support (Kerberos KDC Proxy) +# +# The symlink from /etc/ipa/kdcproxy/ to /etc/httpd/conf.d/ is maintained +# by the ExecStartPre script /usr/libexec/ipa/ipa-httpd-kdcproxy in +# httpd.service. The service also sets the environment variable +# KDCPROXY_CONFIG to $KDCPROXY_CONFIG. +# +# Disable KDC Proxy on the current host: +# # ipa-ldap-updater /usr/share/ipa/kdcproxy-disable.uldif +# # systemctl restart httpd.service +# +# Enable KDC Proxy on the current host: +# # ipa-ldap-updater /usr/share/ipa/kdcproxy-enable.uldif +# # systemctl restart httpd.service +# + +WSGIDaemonProcess kdcproxy processes=2 threads=15 maximum-requests=5000 \ + user=kdcproxy group=kdcproxy display-name=%{GROUP} +WSGIImportScript /usr/lib/python2.7/site-packages/kdcproxy/__init__.py \ + process-group=kdcproxy application-group=kdcproxy +WSGIScriptAlias /KdcProxy /usr/lib/python2.7/site-packages/kdcproxy/__init__.py +WSGIScriptReloading Off + +<Location "/KdcProxy"> + Satisfy Any + Order Deny,Allow + Allow from all + WSGIProcessGroup kdcproxy + WSGIApplicationGroup kdcproxy +</Location> diff --git a/install/conf/ipa.conf b/install/conf/ipa.conf index 57de2f1a9..e2b602c85 100644 --- a/install/conf/ipa.conf +++ b/install/conf/ipa.conf @@ -41,9 +41,7 @@ WSGISocketPrefix /run/httpd/wsgi # Configure mod_wsgi handler for /ipa -WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500 -WSGIProcessGroup ipa -WSGIApplicationGroup ipa +WSGIDaemonProcess ipa processes=2 threads=1 maximum-requests=500 display-name=%{GROUP} WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa WSGIScriptAlias /ipa /usr/share/ipa/wsgi.py WSGIScriptReloading Off @@ -70,6 +68,8 @@ WSGIScriptReloading Off GssapiUseS4U2Proxy on Require valid-user ErrorDocument 401 /ipa/errors/unauthorized.html + WSGIProcessGroup ipa + WSGIApplicationGroup ipa </Location> # Turn off Apache authentication for sessions diff --git a/install/share/Makefile.am b/install/share/Makefile.am index 53f0ecf01..80e959a75 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -84,6 +84,9 @@ app_DATA = \ sasl-mapping-fallback.ldif \ schema-update.ldif \ vault.update \ + kdcproxy.conf \ + kdcproxy-enable.uldif \ + kdcproxy-disable.uldif \ $(NULL) EXTRA_DIST = \ diff --git a/install/share/kdcproxy-disable.uldif b/install/share/kdcproxy-disable.uldif new file mode 100644 index 000000000..bfc0b72c1 --- /dev/null +++ b/install/share/kdcproxy-disable.uldif @@ -0,0 +1,3 @@ +# Disable MS-KKDCP protocol for the current host +dn: cn=KDC,cn=$FQDN,cn=masters,cn=ipa,cn=etc,$SUFFIX +remove:ipaConfigString:kdcProxyEnabled diff --git a/install/share/kdcproxy-enable.uldif b/install/share/kdcproxy-enable.uldif new file mode 100644 index 000000000..92297152f --- /dev/null +++ b/install/share/kdcproxy-enable.uldif @@ -0,0 +1,6 @@ +# Enable MS-KKDCP protocol for the current host +dn: cn=KDC,cn=$FQDN,cn=masters,cn=ipa,cn=etc,$SUFFIX +default:objectClass: nsContainer +default:objectClass: ipaConfigObject +default:cn: KDC +add: ipaConfigString: kdcProxyEnabled diff --git a/install/share/kdcproxy.conf b/install/share/kdcproxy.conf new file mode 100644 index 000000000..530703d4a --- /dev/null +++ b/install/share/kdcproxy.conf @@ -0,0 +1,4 @@ +[global] +configs = mit +use_dns = false + diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am index e5d45c479..147ce33a9 100644 --- a/install/tools/Makefile.am +++ b/install/tools/Makefile.am @@ -35,6 +35,11 @@ EXTRA_DIST = \ $(sbin_SCRIPTS) \ $(NULL) +appdir = $(libexecdir)/ipa/ +app_SCRIPTS = \ + ipa-httpd-kdcproxy \ + $(NULL) + MAINTAINERCLEANFILES = \ *~ \ Makefile.in diff --git a/install/tools/ipa-httpd-kdcproxy b/install/tools/ipa-httpd-kdcproxy new file mode 100755 index 000000000..c71f9cccf --- /dev/null +++ b/install/tools/ipa-httpd-kdcproxy @@ -0,0 +1,180 @@ +#!/usr/bin/python2 +# Authors: +# Christian Heimes <cheimes@redhat.com> +# +# 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 <http://www.gnu.org/licenses/>. +# +"""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 os +import sys + +from ipalib import api, errors +from ipapython.ipa_log_manager import standard_logging_setup +from ipapython.ipaldap import IPAdmin +from ipapython.dn import DN +from ipaplatform.paths import paths + + +DEBUG = False +TIME_LIMIT = 2 + + +class CheckError(Exception): + """An unrecoverable error has occured""" + + +class KDCProxyConfig(object): + ipaconfig_flag = 'ipaKDCProxyEnabled' + + def __init__(self, time_limit=TIME_LIMIT): + self.time_limit = time_limit + self.con = None + self.log = api.log + 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""" + self.log.debug('ldap_uri: %s', self.ldap_uri) + try: + self.con = IPAdmin(ldap_uri=self.ldap_uri) + # EXTERNAL bind as root user + self.con.ldapi = True + self.con.do_bind(timeout=self.time_limit) + except errors.NetworkError as e: + msg = 'Failed to get setting from dirsrv: %s' % e + self.log.exception(msg) + raise CheckError(msg) + except Exception as e: + msg = ('Unknown error while retrieving setting from %s: %s' % + (self.ldap_uri, e)) + self.log.exception(msg) + raise CheckError(msg) + + def _find_entry(self, dn, attrs, filter, scope=IPAdmin.SCOPE_BASE): + """Find an LDAP entry, handles NotFound and Limit""" + try: + entries, truncated = self.con.find_entries( + filter, attrs, dn, scope, time_limit=self.time_limit) + if truncated: + raise errors.LimitsExceeded() + except errors.NotFound: + self.log.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)) + self.log.exception(msg) + raise CheckError(msg) + return entries[0] + + def is_host_enabled(self): + """Check replica specific flag""" + self.log.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) + self.log.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 CheckError("'%s' already exists, but it is not a symlink" % + self.conflink) + dest = os.readlink(self.conflink) + if dest != self.conf: + raise CheckError("'%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 CheckError as e: + self.log.warn("Cannot enable KDC proxy: %s " % e) + return False + + if valid: + self.log.debug("Symlink exists and is valid") + return True + + if not os.path.isfile(self.conf): + self.log.warn("'%s' does not exist", self.conf) + return False + + # create the symbolic link + self.log.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: + self.log.warn("Cannot disable KDC proxy: %s " % e) + return False + + if valid: + self.log.debug("Removing symlink '%s'", self.conflink) + os.unlink(self.conflink) + else: + self.log.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='kdcproxyshim', log=None, debug=debug) + standard_logging_setup(verbose=True, debug=debug) + + with KDCProxyConfig(time_limit) as cfg: + if cfg.is_host_enabled(): + if cfg.create_symlink(): + api.log.info('KDC proxy enabled') + else: + if cfg.remove_symlink(): + api.log.info('KDC proxy disabled') + +if __name__ == '__main__': + main() |