Hello,

here is my first patch for FreeIPA. The patch integrates python-kdcproxy
for MS-KKDCP support (aka Kerberos over HTTPS).

https://www.freeipa.org/page/V4/KDC_Proxy

Ticket: https://fedorahosted.org/freeipa/ticket/4801

Christian
From 49ad74834ef9036d0678dceb4020784fef389446 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
Date: Thu, 21 May 2015 12:42:27 +0200
Subject: [PATCH] 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.2.1. All kdcproxy
  dependencies are already satisfied.
- The service can be globally toggled with the boolean attribute
  ipaKDCproxyEnabled in cn=ipaConfig,cn=etc. The switch is supported by
  ipa config-mod --enable-kdcproxy.
- The installers and update create a new Apache config file
  ipa-kdc-proxy.conf that mounts a WSGI app at /KdcProxy. The app is run
  inside its own WSGI daemon group.
- The WSGI app at /KdcProxy is a WSGI wrapper, that checks the state of
  the switch. When ipaKDCproxyEnabled is FALSE, a HTTP 404 error is
  returned. For performance reasons the flag is only checked at start
  of the WSGI app. Apache must be reloaded or restarted to read the
  switch state again.
- The WSGI app uses the Apache principal to acquire a Kerberos ticket to
  perform GSSAPI bindings for the LDAP query of ipaKDCproxyEnabled.
- The WSGI wrapper script sets KDCPROXY_CONFIG=/etc/ipa/kdcproxy.conf,
  so that an existing config is not used.
- python-kdcproxy is configured to *not* use DNS SRV lookups. The
  location of KDC and KPASSWD servers are read from /etc/krb5.conf

https://www.freeipa.org/page/V4/KDC_Proxy

https://fedorahosted.org/freeipa/ticket/4801
---
 ACI.txt                                |   2 +-
 API.txt                                |   3 +-
 freeipa.spec.in                        |  11 ++
 install/conf/Makefile.am               |   1 +
 install/conf/ipa-kdc-proxy.conf        |  14 +++
 install/conf/ipa.conf                  |   6 +-
 install/share/60ipaconfig.ldif         |   4 +-
 install/share/Makefile.am              |   2 +
 install/share/kdcproxy.conf            |   4 +
 install/share/kdcproxyshim.py          | 189 +++++++++++++++++++++++++++++++++
 install/tools/ipa-replica-install      |   6 ++
 install/tools/ipa-server-install       |   7 ++
 install/ui/src/freeipa/serverconfig.js |   4 +
 install/updates/50-ipaconfig.update    |   4 +
 ipalib/plugins/config.py               |   8 +-
 ipaplatform/base/paths.py              |   1 +
 ipaserver/install/ipa_backup.py        |   1 +
 ipaserver/install/kdcproxyinstance.py  | 105 ++++++++++++++++++
 ipaserver/install/server.py            |   8 ++
 19 files changed, 373 insertions(+), 7 deletions(-)
 create mode 100644 install/conf/ipa-kdc-proxy.conf
 create mode 100644 install/share/kdcproxy.conf
 create mode 100644 install/share/kdcproxyshim.py
 create mode 100644 ipaserver/install/kdcproxyinstance.py

diff --git a/ACI.txt b/ACI.txt
index 3c4ebde5b3ac2eb0b8e9465c5f2bd74f5bdbfb01..8576f56c3c89311dec92bcd7fe2932e42be3d510 100644
--- a/ACI.txt
+++ b/ACI.txt
@@ -23,7 +23,7 @@ aci: (targetattr = "automountmapname || description")(targetfilter = "(objectcla
 dn: cn=automount,dc=ipa,dc=example
 aci: (targetfilter = "(objectclass=automountmap)")(version 3.0;acl "permission:System: Remove Automount Maps";allow (delete) groupdn = "ldap:///cn=System: Remove Automount Maps,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example
-aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";;)
+aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakdcproxyenabled || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";;)
 dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
 aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
diff --git a/API.txt b/API.txt
index da69f32de5c12c0d85a7d61d9027385aa3c0ee05..a5ba9b4129f79f1443628e159b54cfeb9dcdc1eb 100644
--- a/API.txt
+++ b/API.txt
@@ -514,7 +514,7 @@ args: 0,1,1
 option: Str('version?', exclude='webui')
 output: Output('result', None, None)
 command: config_mod
-args: 0,25,3
+args: 0,26,3
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('delattr*', cli_name='delattr', exclude='webui')
@@ -525,6 +525,7 @@ option: Str('ipadefaultprimarygroup', attribute=True, autofill=False, cli_name='
 option: Str('ipagroupobjectclasses', attribute=True, autofill=False, cli_name='groupobjectclasses', csv=True, multivalue=True, required=False)
 option: IA5Str('ipagroupsearchfields', attribute=True, autofill=False, cli_name='groupsearch', multivalue=False, required=False)
 option: IA5Str('ipahomesrootdir', attribute=True, autofill=False, cli_name='homedirectory', multivalue=False, required=False)
+option: Bool('ipakdcproxyenabled', attribute=True, autofill=False, cli_name='enable_kdcproxy', multivalue=False, required=False)
 option: StrEnum('ipakrbauthzdata', attribute=True, autofill=False, cli_name='pac_type', csv=True, multivalue=True, required=False, values=(u'MS-PAC', u'PAD', u'nfs:NONE'))
 option: Int('ipamaxusernamelength', attribute=True, autofill=False, cli_name='maxusername', minvalue=1, multivalue=False, required=False)
 option: Bool('ipamigrationenabled', attribute=True, autofill=False, cli_name='enable_migration', multivalue=False, required=False)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 7dc576256865fb04b3f322b2094a5e3ece7776a5..637725a73117acdc8d5ea34de629336ce0fba4df 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -95,6 +95,7 @@ BuildRequires:  p11-kit-devel
 BuildRequires:  pki-base >= 10.2.1-0.1
 BuildRequires:  python-pytest-multihost >= 0.5
 BuildRequires:  python-pytest-sourceorder
+BuildRequires:  python-kdcproxy >= 0.2.1
 
 %description
 IPA is an integrated solution to provide centrally managed Identity (machine,
@@ -140,6 +141,7 @@ Requires: pki-kra >= 10.2.1-0.1
 Requires(preun): python systemd-units
 Requires(postun): python systemd-units
 Requires: python-dns >= 1.11.1
+Requires: python-kdcproxy >= 0.2.1
 Requires: zip
 Requires: policycoreutils >= 2.1.12-5
 Requires: tar
@@ -429,6 +431,7 @@ ln -s ../../../..%{_sysconfdir}/ipa/html/browserconfig.html \
 # So we can own our Apache configuration
 mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d/
 /bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa.conf
+/bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa-kdc-proxy.conf
 /bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa-pki-proxy.conf
 /bin/touch %{buildroot}%{_sysconfdir}/httpd/conf.d/ipa-rewrite.conf
 mkdir -p %{buildroot}%{_usr}/share/ipa/html/
@@ -458,6 +461,9 @@ install daemons/dnssec/ipa-ods-exporter %{buildroot}%{_libexecdir}/ipa/ipa-ods-e
 # Web UI plugin dir
 mkdir -p %{buildroot}%{_usr}/share/ipa/ui/js/plugins
 
+# KDC proxy config (kdcproxyshim.py sets KDCPROXY_CONFIG to load this file)
+install -m 644 install/share/kdcproxy.conf %{buildroot}%{_sysconfdir}/ipa/kdcproxy.conf
+
 # NOTE: systemd specific section
 mkdir -p %{buildroot}%{_tmpfilesdir}
 install -m 0644 init/systemd/ipa.conf.tmpfiles %{buildroot}%{_tmpfilesdir}/%{name}.conf
@@ -686,6 +692,7 @@ fi
 %config(noreplace) %{_sysconfdir}/sysconfig/ipa_memcached
 %config(noreplace) %{_sysconfdir}/sysconfig/ipa-dnskeysyncd
 %config(noreplace) %{_sysconfdir}/sysconfig/ipa-ods-exporter
+%config(noreplace) %{_sysconfdir}/ipa/kdcproxy.conf
 %dir %attr(0700,apache,apache) %{_localstatedir}/run/ipa_memcached/
 %dir %attr(0700,root,root) %{_localstatedir}/run/ipa/
 %dir %attr(0700,apache,apache) %{_localstatedir}/run/httpd/ipa/
@@ -712,6 +719,7 @@ fi
 %attr(755,root,root) %{_libdir}/ipa/certmonger/*
 %dir %{_usr}/share/ipa
 %{_usr}/share/ipa/wsgi.py*
+%{_usr}/share/ipa/kdcproxyshim.py*
 %{_usr}/share/ipa/copy-schema-to-ca.py*
 %{_usr}/share/ipa/*.ldif
 %{_usr}/share/ipa/*.uldif
@@ -773,10 +781,13 @@ fi
 %config(noreplace) %{_sysconfdir}/ipa/html/browserconfig.html
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-rewrite.conf
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa.conf
+%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-kdc-proxy.conf
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/httpd/conf.d/ipa-pki-proxy.conf
 %{_usr}/share/ipa/ipa.conf
 %{_usr}/share/ipa/ipa-rewrite.conf
+%{_usr}/share/ipa/ipa-kdc-proxy.conf
 %{_usr}/share/ipa/ipa-pki-proxy.conf
+%{_usr}/share/ipa/kdcproxy.conf
 %ghost %attr(0644,root,apache) %config(noreplace) %{_usr}/share/ipa/html/ca.crt
 %ghost %attr(0644,root,apache) %{_usr}/share/ipa/html/configure.jar
 %ghost %attr(0644,root,apache) %{_usr}/share/ipa/html/kerberosauth.xpi
diff --git a/install/conf/Makefile.am b/install/conf/Makefile.am
index 65e25bc94682d85428ccfbcba87137c0d4edc3d0..cd064f3d30089cb95f40237bfcaa9b222a5e8d87 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		\
 	ipa-pki-proxy.conf		\
 	ipa-rewrite.conf		\
 	$(NULL)
diff --git a/install/conf/ipa-kdc-proxy.conf b/install/conf/ipa-kdc-proxy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..a4572407eeece5238fe5243bb98ab549e2c5f114
--- /dev/null
+++ b/install/conf/ipa-kdc-proxy.conf
@@ -0,0 +1,14 @@
+WSGIDaemonProcess kdcproxy processes=2 threads=15 maximum-requests=5000 \
+  display-name=%{GROUP}
+WSGIImportScript /usr/share/ipa/kdcproxyshim.py \
+  process-group=kdcproxy application-group=kdcproxy
+WSGIScriptAlias /KdcProxy /usr/share/ipa/kdcproxyshim.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 57de2f1a9543e1395f3eb46b045334f86cc8e79f..e2b602c8573078f517badac00a8c8c5bd593db28 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/60ipaconfig.ldif b/install/share/60ipaconfig.ldif
index 692690714aabad6b5e34328fe24cfab62bc0d70c..506058c809d8117a101eb4757c734e584c25950b 100644
--- a/install/share/60ipaconfig.ldif
+++ b/install/share/60ipaconfig.ldif
@@ -43,11 +43,13 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.23 NAME 'ipaCertificateSubjectBase' SY
 attributeTypes: (2.16.840.1.113730.3.8.3.16 NAME 'ipaConfigString' DESC 'Generic configuration stirng' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
 attributeTypes: ( 2.16.840.1.113730.3.8.3.26 NAME 'ipaSELinuxUserMapDefault' DESC 'Default SELinux user' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
 attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+# ipaKDCProxyEnabled - if TRUE enable KDC Proxy service
+attributeTypes: ( 2.16.840.1.113730.3.8.3.28 NAME 'ipaKDCProxyEnabled' DESC 'Enable KDC over HTTPS proxy.' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.2' )
 ###############################################
 ##
 ## ObjectClasses
 ##
 ## ipaGuiConfig - GUI config parameters objectclass
-objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData ) )
+objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaKDCProxyEnabled ) )
 ## ipaConfigObject - Generic config strings object holder
 objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' )
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 8d336690f184025f8199ed1d2c57d8274f0d3886..62303656bb7ee0f489c9d24a652f6ccf79335da4 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -80,6 +80,8 @@ app_DATA =				\
 	copy-schema-to-ca.py		\
 	sasl-mapping-fallback.ldif	\
 	schema-update.ldif		\
+	kdcproxyshim.py			\
+	kdcproxy.conf			\
 	$(NULL)
 
 EXTRA_DIST =				\
diff --git a/install/share/kdcproxy.conf b/install/share/kdcproxy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..530703d4aa67765a6a6a9cc82c81df7448c43608
--- /dev/null
+++ b/install/share/kdcproxy.conf
@@ -0,0 +1,4 @@
+[global]
+configs = mit
+use_dns = false
+
diff --git a/install/share/kdcproxyshim.py b/install/share/kdcproxyshim.py
new file mode 100644
index 0000000000000000000000000000000000000000..44ea36971d195bb49a36db7173a2237ff5a14629
--- /dev/null
+++ b/install/share/kdcproxyshim.py
@@ -0,0 +1,189 @@
+# Authors:
+#   Christian Heimes <chei...@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/>.
+#
+"""
+WSGI appliction for KDC Proxy
+"""
+import errno
+import os
+from subprocess import check_call
+import sys
+
+from ipalib import api, errors
+from ipalib.session import get_ipa_ccache_name
+from ipapython.ipa_log_manager import standard_logging_setup
+from ipalib.krb_utils import krb5_format_service_principal_name
+from ipalib.krb_utils import krb5_parse_ccache
+from ipapython.ipaldap import IPAdmin
+from ipapython.dn import DN
+from ipapython import ipautil
+from ipaplatform.paths import paths
+
+
+DEBUG = True
+TIME_LIMIT = 2
+KDCPROXY_CONFIG = '/etc/ipa/kdcproxy.conf'
+
+
+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.principal = krb5_format_service_principal_name(
+            'HTTP', api.env.host, api.env.realm)
+        self.keytab = paths.IPA_KEYTAB
+        # Force FILE scheme to have a CCACHE for each PID.
+        self.ccache = get_ipa_ccache_name(scheme='FILE')
+
+        self.ipaconfig_dn = DN(('cn', 'ipaConfig'), ('cn', 'etc'),
+                               api.env.basedn)
+
+    def _kinit(self):
+        """Get a krb5 ticket with Apache's keytab"""
+        self.log.debug('krb5 principal %s, keytab %s, ccache %s',
+                       self.principal, self.keytab, self.ccache)
+        try:
+            os.environ['KRB5CCNAME'] = self.ccache
+            ipautil.kinit_keytab(str(self.principal), self.keytab, self.ccache)
+        except Exception as e:
+            msg = "kinit failed: %s" % e
+            self.log.exception(msg)
+            raise CheckError(msg)
+
+    def _kdestroy(self):
+        """Release krb5 ccache"""
+        self.log.debug('kdestroy %s', self.ccache)
+        scheme, name = krb5_parse_ccache(self.ccache)
+        if scheme == 'FILE':
+            try:
+                os.unlink(name)
+            except OSError as e:
+                if e.errno != errno.ENOENT:
+                    raise
+        else:
+            check_call([paths.KDESTROY, '-A', '-q'])
+        del os.environ['KRB5CCNAME']
+
+    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)
+            self.con.do_sasl_gssapi_bind()
+        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 _get_entry(self, dn, attrs):
+        """Get an LDAP entry, handles NotFound"""
+        try:
+            return self.con.get_entry(dn,
+                                      attrs,
+                                      time_limit=self.time_limit)
+        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)
+
+    def ipaconfig_enabled(self):
+        """Check global ipaKDCProxyEnabled switch"""
+        self.log.debug('Read settings from %s dn: %s',
+                       self.ipaconfig_flag, self.ipaconfig_dn)
+        entry = self._get_entry(self.ipaconfig_dn,
+                                [self.ipaconfig_flag])
+        if entry is not None:
+            value = entry.single_value.get(self.ipaconfig_flag)
+        else:
+            value = None
+        self.log.debug('%s==%s in %s', self.ipaconfig_flag, value,
+                       self.ipaconfig_dn)
+        if value == 'TRUE':
+            return True
+        elif value == 'FALSE':
+            return False
+        else:
+            return None
+
+    def __enter__(self):
+        self._kinit()
+        self._ldap_con()
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        self._kdestroy()
+        if self.con is not None:
+            self.con.unbind()
+            self.con = None
+
+
+def check_enabled(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.ipaconfig_enabled():
+            api.log.info('kdcproxy ENABLED')
+            return True
+        else:
+            api.log.info('kdcproxy DISABLED')
+            return False
+
+
+ENABLED = check_enabled()
+
+# override config location
+if 'kdcproxy' in sys.modules:
+    raise CheckError('kdcproxy already imported')
+os.environ['KDCPROXY_CONFIG'] = KDCPROXY_CONFIG
+import kdcproxy
+
+
+def application(environ, start_response):
+    if not ENABLED:
+        code = b'404 Not Found'
+        msg = b'KDC over HTTPS proxy service is not available.'
+        headers = [
+            ('Content-Type', 'text/plain; charset=utf-8'),
+            ('Content-Length', str(len(msg))),
+        ]
+        start_response(code, headers)
+        return [msg]
+    else:
+        return kdcproxy.application(environ, start_response)
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 1df782b7304b3303f8e67ad943f0f2fbf1e96f9c..cf698d30409793fd34533b84cb0de8f7badfab42 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -35,6 +35,7 @@ from ipaserver.install import dsinstance, installutils, krbinstance, service
 from ipaserver.install import bindinstance, httpinstance, ntpinstance
 from ipaserver.install import memcacheinstance, dnskeysyncinstance
 from ipaserver.install import otpdinstance
+from ipaserver.install import kdcproxyinstance
 from ipaserver.install.replication import replica_conn_check, ReplicationManager
 from ipaserver.install.installutils import (
     create_replica_config, private_ccache)
@@ -670,6 +671,11 @@ def main():
     otpd.create_instance('OTPD', config.host_name, config.dirman_password,
                          ipautil.realm_to_suffix(config.realm_name))
 
+    kdcproxy = kdcproxyinstance.KDCProxyInstance(fstore)
+    kdcproxy.create_instance('KDCPROXY', config.host_name,
+                             config.dirman_password,
+                             ipautil.realm_to_suffix(config.realm_name))
+
     if CA:
         CA.configure_certmonger_renewal()
         CA.import_ra_cert(config.dir + "/ra.p12")
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index c7d7c7bff7d5a5e818eaa4a8cb93be94eae7d0c1..a612e48fa251a3fdeb71e8f4803e850469601ef3 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -53,6 +53,7 @@ from ipaserver.install import httpinstance
 from ipaserver.install import ntpinstance
 from ipaserver.install import certs
 from ipaserver.install import cainstance
+from ipaserver.install import kdcproxyinstance
 from ipaserver.install import memcacheinstance
 from ipaserver.install import otpdinstance
 from ipaserver.install import sysupgrade
@@ -598,6 +599,7 @@ def uninstall():
 
     dns_installer.uninstall()
 
+    kdcproxyinstance.KDCProxyInstance(fstore).uninstall()
     httpinstance.HTTPInstance(fstore).uninstall()
     krbinstance.KrbInstance(fstore).uninstall()
     dsinstance.DsInstance(fstore=fstore).uninstall()
@@ -1268,6 +1270,11 @@ def main():
             ca_is_configured=setup_ca)
     tasks.restore_context(paths.CACHE_IPA_SESSIONS)
 
+    # Create KDCProxyInstance
+    kdcproxy = kdcproxyinstance.KDCProxyInstance(fstore)
+    kdcproxy.create_instance('KDCPROXY', host_name, dm_password,
+                             ipautil.realm_to_suffix(realm_name))
+
     # Export full CA chain
     ca_db = certs.CertDB(realm_name)
     os.chmod(CACERT, 0644)
diff --git a/install/ui/src/freeipa/serverconfig.js b/install/ui/src/freeipa/serverconfig.js
index efe1805698372b45afae38d1f9dd883034ee03c6..7eadcf612de5b7b82c6bbdf97f9714832483b72c 100644
--- a/install/ui/src/freeipa/serverconfig.js
+++ b/install/ui/src/freeipa/serverconfig.js
@@ -122,6 +122,10 @@ return {
                             name: 'ipakrbauthzdata',
                             $type: 'checkboxes',
                             options: IPA.create_options(['MS-PAC', 'PAD', 'nfs:NONE'])
+                        },
+                        {
+                            $type: 'checkbox',
+                            name: 'ipakdcproxyenabled'
                         }
                     ]
                 }
diff --git a/install/updates/50-ipaconfig.update b/install/updates/50-ipaconfig.update
index 89a1726f4fb275868b18b9ab06c8498061f2e904..06ae371295db6310a924e810b39d5b9b6745f9f2 100644
--- a/install/updates/50-ipaconfig.update
+++ b/install/updates/50-ipaconfig.update
@@ -4,3 +4,7 @@ add:ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023
 add:ipaUserObjectClasses: ipasshuser
 remove:ipaConfigString:AllowLMhash
 add:objectClass: ipaUserAuthTypeClass
+
+addifnew:ipaKDCProxyEnabled: TRUE
+remove:aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";;)
+add:aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakdcproxyenabled || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";;)
diff --git a/ipalib/plugins/config.py b/ipalib/plugins/config.py
index 6267313d5e9af2d97f45f987115de143d7aa7915..ecd290461d7eee6d1a4480770489397af1e8a33d 100644
--- a/ipalib/plugins/config.py
+++ b/ipalib/plugins/config.py
@@ -96,7 +96,7 @@ class config(LDAPObject):
         'ipamigrationenabled', 'ipacertificatesubjectbase',
         'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
         'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
-        'ipauserauthtype'
+        'ipauserauthtype', 'ipakdcproxyenabled',
     ]
     container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
     permission_filter_objectclasses = ['ipaguiconfig']
@@ -117,6 +117,7 @@ class config(LDAPObject):
                 'ipasearchrecordslimit', 'ipasearchtimelimit',
                 'ipauserauthtype', 'ipauserobjectclasses',
                 'ipausersearchfields', 'ipacustomfields',
+                'ipakdcproxyenabled',
             },
         },
     }
@@ -231,6 +232,11 @@ class config(LDAPObject):
             values=(u'password', u'radius', u'otp', u'disabled'),
             csv=True,
         ),
+        Bool('ipakdcproxyenabled',
+            cli_name='enable_kdcproxy',
+            label=_('Enable KDC requests over HTTPS'),
+            doc=_('Enable KDC requests over HTTPS'),
+        ),
     )
 
     def get_dn(self, *keys, **kwargs):
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 9ba87523b5619188f02bdad6c23d2446a2c4b0f2..898d524e5935fbd4bb31b1e446e2bc7b3827df55 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -49,6 +49,7 @@ class BasePathNamespace(object):
     ALIAS_CACERT_ASC = "/etc/httpd/alias/cacert.asc"
     ALIAS_PWDFILE_TXT = "/etc/httpd/alias/pwdfile.txt"
     HTTPD_CONF_D_DIR = "/etc/httpd/conf.d/"
+    HTTPD_IPA_KDC_PROXY_CONF = "/etc/httpd/conf.d/ipa-kdc-proxy.conf"
     HTTPD_IPA_PKI_PROXY_CONF = "/etc/httpd/conf.d/ipa-pki-proxy.conf"
     HTTPD_IPA_REWRITE_CONF = "/etc/httpd/conf.d/ipa-rewrite.conf"
     HTTPD_IPA_CONF = "/etc/httpd/conf.d/ipa.conf"
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index c3611b241d4818bc718d8bcd83da8d3c1755457e..639373b9be5d38e1f165892823931796fb1ca4c4 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -146,6 +146,7 @@ class Backup(admintool.AdminTool):
         paths.LIMITS_CONF,
         paths.HTTPD_PASSWORD_CONF,
         paths.IPA_KEYTAB,
+        paths.HTTPD_IPA_KDC_PROXY_CONF,
         paths.HTTPD_IPA_PKI_PROXY_CONF,
         paths.HTTPD_IPA_REWRITE_CONF,
         paths.HTTPD_NSS_CONF,
diff --git a/ipaserver/install/kdcproxyinstance.py b/ipaserver/install/kdcproxyinstance.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d33c6ca2fbc6fba137ee47107084ca794af22ee
--- /dev/null
+++ b/ipaserver/install/kdcproxyinstance.py
@@ -0,0 +1,105 @@
+# Authors: Christian Heimes <chei...@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/>.
+#
+import os
+
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipapython import ipautil
+from ipapython import sysrestore
+
+import installutils
+import service
+
+
+class KDCProxyInstance(service.SimpleServiceInstance):
+    def __init__(self, fstore):
+        service.SimpleServiceInstance.__init__(self, "kdcproxy")
+        if fstore:
+            self.fstore = fstore
+        else:
+            self.fstore = sysrestore.FileStore(paths.SYSRESTORE)
+        # KDC proxy runs inside Apache HTTPD
+        self.service = None
+        self.httpd_service_name = 'httpd'
+        self.httpd_service = services.service(self.httpd_service_name)
+
+    # HTTPD
+    def start_httpd(self, instance_name="", capture_output=True, wait=True):
+        self.httpd_service.start(instance_name, capture_output=capture_output, wait=wait)
+
+    def stop_httpd(self, instance_name="", capture_output=True):
+        self.httpd_service.stop(instance_name, capture_output=capture_output)
+
+    def restart_httpd(self, instance_name="", capture_output=True, wait=True):
+        self.httpd_service.restart(instance_name,
+                                   capture_output=capture_output,
+                                   wait=wait)
+
+    def is_httpd_running(self):
+        return self.httpd_service.is_running()
+
+    def is_httpd_enabled(self):
+        return self.httpd_service.is_enabled()
+
+    def is_configured(self):
+        return os.path.isfile(paths.HTTPD_IPA_KDC_PROXY_CONF)
+
+    def disable(self):
+        pass
+
+    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.sub_dict = {}
+
+        self.step("configuring httpd", self.__configure_http)
+        self.step("(re)starting %s " % self.httpd_service_name,
+                  self.__restart_httpd)
+        self.start_creation("Configuring %s" % self.service_name)
+
+    def __configure_http(self):
+        target_fname = paths.HTTPD_IPA_KDC_PROXY_CONF
+        http_txt = ipautil.template_file(
+            ipautil.SHARE_DIR + "ipa-kdc-proxy.conf", self.sub_dict)
+        self.fstore.backup_file(target_fname)
+        with open(target_fname, 'w') as f:
+            f.write(http_txt)
+        os.chmod(target_fname, 0644)
+
+    def __restart_httpd(self):
+        self.backup_state("running", self.is_httpd_running())
+        self.restart_httpd()
+
+    def uninstall(self):
+        if self.is_configured():
+            self.print_msg("Unconfiguring %s" % self.service_name)
+
+        self.stop_httpd()
+
+        running = self.restore_state("running")
+        installutils.remove_file(paths.HTTPD_IPA_KDC_PROXY_CONF)
+
+        if running:
+            self.start_httpd()
diff --git a/ipaserver/install/server.py b/ipaserver/install/server.py
index c08b74828bf811c560963cc4f9e267d3afbeedb3..27142bca5b81d4a24e71d8db75e1e5bcce2d7edb 100644
--- a/ipaserver/install/server.py
+++ b/ipaserver/install/server.py
@@ -31,6 +31,7 @@ from ipaserver.install import certs
 from ipaserver.install import otpdinstance
 from ipaserver.install import sysupgrade
 from ipaserver.install import dnskeysyncinstance
+from ipaserver.install import kdcproxyinstance
 
 class KpasswdInstance(service.SimpleServiceInstance):
     def __init__(self):
@@ -1328,6 +1329,13 @@ def upgrade_configuration():
                 dnskeysyncd.create_instance(fqdn, api.env.realm)
                 dnskeysyncd.start_dnskeysyncd()
 
+    # Install KDCProxy configuration file
+    kdcproxy = kdcproxyinstance.KDCProxyInstance(fstore)
+    if not kdcproxy.is_configured():
+        kdcproxy.create_instance('KDCPROXY', fqdn, None,
+                                 ipautil.realm_to_suffix(api.env.realm),
+                                 realm=api.env.realm)
+
     cleanup_kdc(fstore)
     cleanup_adtrust(fstore)
     setup_firefox_extension(fstore)
-- 
2.4.1

Attachment: signature.asc
Description: OpenPGP digital signature

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to