From 036c0837f4181aa379edadfaa9171bc8c4b7273e Mon Sep 17 00:00:00 2001
From: Christian Heimes <cheimes@redhat.com>
Date: Mon, 7 Sep 2020 18:13:21 +0200
Subject: [PATCH] Run KDC as non-privileged user

Signed-off-by: Christian Heimes <cheimes@redhat.com>
---
 .../ipa-slapi-plugins/ipa-pwd-extop/prepost.c |   6 +
 doc/designs/index.rst                         |   1 +
 doc/designs/krb-kdc-unprivileged.md           | 182 ++++++++++++++++++
 freeipa.spec.in                               |   8 +
 init/tmpfilesd/ipa.conf.in                    |   1 +
 install/share/Makefile.am                     |   3 +
 install/share/kadmin-override.conf            |   7 +
 install/share/kdc.conf.template               |   5 +
 install/share/krb5kdc-override.conf           |   7 +
 install/share/krb5kdc.ldif                    |  16 ++
 install/share/root-autobind.ldif              |   6 +
 ipaplatform/base/constants.py                 |   3 +
 ipaplatform/base/paths.py                     |   8 +-
 ipaserver/install/dsinstance.py               |   1 +
 ipaserver/install/ipa_backup.py               |   2 +
 ipaserver/install/krbinstance.py              |  38 +++-
 ipaserver/install/upgradeinstance.py          |   1 -
 17 files changed, 290 insertions(+), 5 deletions(-)
 create mode 100644 doc/designs/krb-kdc-unprivileged.md
 create mode 100644 install/share/kadmin-override.conf
 create mode 100644 install/share/krb5kdc-override.conf
 create mode 100644 install/share/krb5kdc.ldif

diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index b24a5ffab0..cadd8bbcb0 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -366,6 +366,12 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
         /* Check Bind DN */
         slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn);
 
+        /* HACK */
+#define KRB5KDC_SYSACCOUNT "cn=krb5kdc,cn=sysaccounts,cn=etc,"
+        if (strncasecmp(KRB5KDC_SYSACCOUNT, binddn, strlen(KRB5KDC_SYSACCOUNT)) == 0) {
+            pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR;
+        }
+
         /* if it is a passsync manager we also need to skip resets */
         for (i = 0; i < krbcfg->num_passsync_mgrs; i++) {
             if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) {
diff --git a/doc/designs/index.rst b/doc/designs/index.rst
index 708abe4dfc..6359f4fd30 100644
--- a/doc/designs/index.rst
+++ b/doc/designs/index.rst
@@ -14,3 +14,4 @@ FreeIPA design documentation
    membermanager.md
    hidden-replicas.md
    disable-stale-users.md
+   krb-kdc-unprivileged.md
diff --git a/doc/designs/krb-kdc-unprivileged.md b/doc/designs/krb-kdc-unprivileged.md
new file mode 100644
index 0000000000..f192b5a0f1
--- /dev/null
+++ b/doc/designs/krb-kdc-unprivileged.md
@@ -0,0 +1,182 @@
+# Run Kerberos server processes as unprivileged user
+
+**DRAFT**
+
+## Abstract
+
+This design document proposes to run Kerberos server as a less
+privileged service user and LDAP instead of privileged root user and
+Directory Manager account.
+
+## Rational
+
+The two main benefits for running less processes as root are
+
+1. general security hardening
+2. better integration with containers
+
+...
+
+## Terminology
+
+Kerberos is a core component of FreeIPA. Every FreeIPA server runs two
+Kerberos daemons in systemd which listen and process incoming requests.
+The key distribution center (``krb5kdc``) handles authentication and
+ticket requests like ``kinit``. The Kerberos administrative server
+(``kadmind``) deals with password changes and ``kadmin`` requests.
+
+Both services run as privileged root user. Both processes connect to a
+local 389-DS LDAP server instance over Unix domain socket to read and
+write all account data and most of their configuration. The LDAP server
+is configured to use LDAPI AutoBind feature (``SO_PEERCRED``) to
+authenticate connection by uid 0 (root) as ``cn=Directory Manager``.
+The Directory Manager has unrestricted read/write access.
+
+## Implementation details
+
+### LDAPI AutoBind
+
+389-DS's [auto bind feature](
+https://directory.fedoraproject.org/docs/389ds/FAQ/ldapi-and-autobind.html)
+for LDAPI connections maps effective uid and gid of a client process
+to an LDAP account. Currently autobind is configured to map only
+root/root to ``cn=Directory Manager``. In order to map other euid/egid
+combinations ``nsslapd-ldapientrysearchbase`` in ``cn=config`` must
+be reconfigured and an LDAP entry with matching ``uidNumber`` and
+``gidNumber`` must exist. On LDAPI connection with SASL EXTERNAL auth
+the LDAP server performs a query
+``(&(uidNumber=<uid>)(gidNumber=<gid>)`` with base DN
+``nsslapd-ldapientrysearchbase``. The search base is currently not set.
+To prevent mapping of standard user accounts the search base for LDAPI
+is limited to system accounts ``cn=sysaccounts,cn=etc,$SUFFIX``.
+
+```raw
+dn: cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX
+objectClass: ipaIDobject
+objectClass: top
+cn: krb5kdc
+uidNumber: $KRB5KDC_UID
+gidNumber: $KRB5KDC_GID
+```
+
+XXX: ``objectClass: groupofnames`` for ``memberOf``
+
+### LDAP ACIs
+
+The KDC is configured and started very early. FreeIPA's RBAC
+permissions and privileges are not available yet. Therefore the
+``krb5kdc`` cannot use permissions and requires dedicated ACIs.
+
+#### read, search, compare
+
+* Kerberos ticket policy ``objectClass=krbticketpolicyaux``
+* Kerberos principal ``objectClass=krbPrincipal`` / ``objectClass=krbPrincipalAux``
+* IPA Kerberos principal ``objectClass=ipaKrbPrincipal``
+* Kerberos realm ``objectClass=krbRealmContainer``
+* password policy ``objectClass=krbPwdPolicy``
+* S4U2Proxy delecation ACLs ``objectClass=ipaKrb5DelegationACL``
+* OTP tokens ``objectClass=ipaToken``
+* COS templates ``objectClass=costemplate``
+* ipaConfigString, ipaKrbAuthzData, ipaUserAuthType, ``cn=ipaConfig,cn=etc,$SUFFIX``
+* POSIX accounts (covered by krbticketpolicyaux ???) ``objectClass=posixaccount``
+* Samba and NT passwords ???
+* ...
+
+#### write
+
+* principal key, password history, last login, ...
+
+### User account with soft-static UID and GID
+
+Since the ``krb5kdc`` LDAP sysaccount is replicated, the uid and
+primary gid of the system account must be equal on all servers. The
+account cannot be stored in LDAP because the KDC is configured before
+SSSD and user accounts are set up. Therefore a [soft-static allocated](
+https://docs.fedoraproject.org/en-US/packaging-guidelines/UsersAndGroups/)
+uid and gid must be requested from Fedora Packaging Committee.
+
+### Binding to low ports
+
+The KDC listens on 88/TCP and 88/UDP, the kadmin daemon on 464/TCP,
+464/UDP, and 749/TCP. By default Linux prevents non-root users to bind
+to low ports < 1024. systemd has an option to grant net bind service
+capability to a process so it can bind to low ports.
+
+```ini
+[Service]
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+```
+
+### KDC config files
+
+By default the config files and ``kdc.key`` under
+``/var/kerberos/krb5kdc/`` are readable by root user only. The
+``krb5kdc`` system user must be allowed to read these files.
+
+### Pid files
+
+``krb5kdc`` and ``kadmind`` are forking daemons and therefore require
+a PID file to interact with systemd correctly. By default the processes
+store their pid files in ``/run/`` (aka ``/var/run/``). The directory
+is only writable by root. Therefore the pid file location must be
+changed to a dedicated directory ``/run/krb5kdc/`` which is writable
+by ``krb5kdc`` system account. Since ``/run`` is a volatile file system
+the directory must be created with ``systemd-tmpfiles``.
+
+The pid file location can be changed by systemd unit drop-in.
+
+### Log files
+
+Both daemons store their log files in ``/var/log`` by default. Like in
+the pid files case the location is not writable by non-root. Log files
+must be written to a new directory ``/var/log/krb5kdc`` that is owned
+by ``krb5kdc`` system account. The log file location is configured in
+``/var/kerberos/krb5kdc/kdc.conf``.
+
+The logrotate service and SOS reporter must be updated to use the new
+file locations.
+
+### ipa-pwd-extop
+
+Password changes from ``cn=krb5kdc,cn=sysaccounts`` must be treated as
+``IPA_CHANGETYPE_DSMGR`` so the plugin does not expire passwords
+on change.
+
+### Other files
+
+``/var/cache/krb5rcache`` ?
+
+## Dump
+
+```raw
+dn: $SUFFIX
+changetype: modify
+add: aci
+# cn=System: Read Default Kerberos Ticket Policy,cn=permissions,cn=pbac,$SUFFIX
+aci: (targetattr = "createtimestamp || entryusn || krbauthindmaxrenewableage || krbauthindmaxticketlife || krbdefaultencsalttypes || krbmaxrenewableage || krbmaxticketlife || krbsupportedencsalttypes || modifytimestamp || objectclass")(targetfilter = "(objectclass=krbticketpolicyaux)")(version 3.0;acl "KRB5KDC:System: Read Default Kerberos Ticket Policy";allow (compare,read,search) userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# cn=System: Read Group Password Policy costemplate,cn=permissions,cn=pbac,$SUFFIX
+aci: (targetattr = "cn || cospriority || createtimestamp || entryusn || krbpwdpolicyreference || modifytimestamp || objectclass")(targetfilter = "(objectclass=costemplate)")(version 3.0;acl "KRB5KDC:System: Read Group Password Policy costemplate";allow (compare,read,search)  userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# cn=System: Read User Kerberos Login Attributes,cn=permissions,cn=pbac,$SUFFIX
+aci: (targetattr = "krblastadminunlock || krblastfailedauth || krblastpwdchange || krblastsuccessfulauth || krbloginfailedcount || krbpwdpolicyreference || krbticketpolicyreference || krbupenabled")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "KRB5KDC:System: Read User Kerberos Login Attributes";allow (compare,read,search)  userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# cn=System: Read system trust accounts,cn=permissions,cn=pbac,$SUFFIX
+aci: (targetattr = "gidnumber || krbprincipalname || uidnumber")(version 3.0;acl "permission:System: Read system trust accounts";allow (compare,read,search) groupdn = "ldap:///cn=System: Read system trust accounts,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+# extra KRB stuff
+aci: (targetattr = "ipakrbauthzdata || ipakrbprincipalalias || ipauniqueid || krbcanonicalname || krbobjectreferences || krbpasswordexpiration || krbprincipalaliases || krbprincipalauthind || krbprincipalexpiration || krbprincipalname")(version 3.0;acl "KRB5KDC:System: Read Other Kerberos Attributes";allow (compare,read,search)  userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# ALL
+aci:(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMKey || krbPrincipalName || krbCanonicalName || krbPwdHistory || krbLastPwdChange || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || ipaNTHash")(version 3.0; acl "KRB5 KDC can read/write credentials"; allow (all) userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# read tokens
+aci: (targetfilter = "(objectClass=ipaToken)")(targetattrs = "objectclass || description || managedBy || ipatokenUniqueID || ipatokenDisabled || ipatokenNotBefore || ipatokenNotAfter || ipatokenVendor || ipatokenModel || ipatokenSerial || ipatokenOwner")(version 3.0; acl "KRB5KDC:System: KDC can read basic token inf"; allow (read, search, compare) userdn="ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+# XXX
+# aci: (targetattr = "krbPrincipalName || krbCanonicalName || krbUPEnabled || krbPrincipalKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbPrincipalAuthInd || krbExtraData || krbLastAdminUnlock || krbObjectReferences || krbTicketFlags || krbMaxTicketLife || krbMaxRenewableAge || nsaccountlock || passwordHistory || ipaKrbAuthzData || ipaUserAuthType || ipatokenRadiusConfigLink || krbAuthIndMaxTicketLife || krbAuthIndMaxRenewableAge || objectClass")(version 3.0; acl "System: KDC"; allow (read, search, compare) userdn="ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+
+dn: cn=$REALM,cn=kerberos,$SUFFIX
+changetype: modify
+add: aci
+# read password policies
+aci:(targetfilter = "(objectClass=krbPwdPolicy)")(targetattr = "krbMaxPwdLife || krbMinPwdLife || krbPwdMinDiffChars || krbPwdMinLength || krbPwdHistoryLength || objectClass || cn")(version 3.0;acl "KRB5KDC: KDC can read password policies"; allow (read, search, compare) userdn="ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+
+dn: cn=s4u2proxy,cn=etc,$SUFFIX
+changetype: modify
+add: aci
+aci: (targetfilter = "(objectClass=ipaKrb5DelegationACL)")(targetattr = "objectClass || cn || ipaAllowToImpersonate || ipaAllowedTarget || memberPrincipal")(version 3.0;acl "KRB5KDC: KDC can read KRB5 delegation ACLs"; allow (read, search, compare) userdn="ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+```
\ No newline at end of file
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 0e7a51f445..bdc8477503 100755
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -931,6 +931,9 @@ mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d/
 mkdir -p %{buildroot}%{_libdir}/krb5/plugins/libkrb5
 touch %{buildroot}%{_libdir}/krb5/plugins/libkrb5/winbind_krb5_locator.so
 
+# custom log directory for KRB5 KDC
+mkdir -p %{buildroot}%{_localstatedir}/log/krb5kdc
+
 # ONLY_CLIENT
 %endif
 
@@ -1013,6 +1016,9 @@ getent group ipaapi >/dev/null || groupadd -f -r ipaapi
 getent passwd ipaapi >/dev/null || useradd -r -g ipaapi -s /sbin/nologin -d / -c "IPA Framework User" ipaapi
 # add apache to ipaaapi group
 id -Gn apache | grep '\bipaapi\b' >/dev/null || usermod apache -a -G ipaapi
+# create krb5kdc group and user
+getent group krb5kdc >/dev/null || groupadd -f -r krb5kdc
+getent passwd krb5kdc >/dev/null || useradd -r -g krb5kdc -s /sbin/nologin -d / -c "Kerberos KDC" krb5kdc
 
 
 %post server-dns
@@ -1291,6 +1297,7 @@ fi
 %license COPYING
 %ghost %verify(not owner group) %dir %{_sharedstatedir}/kdcproxy
 %dir %attr(0755,root,root) %{_sysconfdir}/ipa/kdcproxy
+%dir %attr(0755,krb5kdc,krb5kdc) %{_localstatedir}/log/krb5kdc
 %config(noreplace) %{_sysconfdir}/ipa/kdcproxy/kdcproxy.conf
 # NOTE: systemd specific section
 %{_tmpfilesdir}/ipa.conf
@@ -1300,6 +1307,7 @@ fi
 %{_usr}/share/ipa/wsgi.py*
 %{_usr}/share/ipa/kdcproxy.wsgi
 %{_usr}/share/ipa/ipaca*.ini
+%{_usr}/share/ipa/*.conf
 %{_usr}/share/ipa/*.ldif
 %exclude %{_datadir}/ipa/ipa-cldap-conf.ldif
 %{_usr}/share/ipa/*.uldif
diff --git a/init/tmpfilesd/ipa.conf.in b/init/tmpfilesd/ipa.conf.in
index 183ceeda67..bd0b70684e 100644
--- a/init/tmpfilesd/ipa.conf.in
+++ b/init/tmpfilesd/ipa.conf.in
@@ -1,2 +1,3 @@
 d /run/ipa 0711 root root
 d /run/ipa/ccaches 0770 ipaapi ipaapi
+d /run/krb5kdc 0755 krb5kdc krb5kdc
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 026d83035c..28d93e1272 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -44,6 +44,9 @@ dist_app_DATA =				\
 	dnssec.ldif			\
 	domainlevel.ldif			\
 	kerberos.ldif			\
+	krb5kdc.ldif			\
+	krb5kdc-override.conf	\
+	kadmin-override.conf	\
 	indices.ldif			\
 	bind.ipa-ext.conf.template		\
 	bind.ipa-options-ext.conf.template	\
diff --git a/install/share/kadmin-override.conf b/install/share/kadmin-override.conf
new file mode 100644
index 0000000000..0518e6e8ea
--- /dev/null
+++ b/install/share/kadmin-override.conf
@@ -0,0 +1,7 @@
+[Service]
+PIDFile=/run/krb5kdc/kadmind.pid
+ExecStart=
+ExecStart=/usr/sbin/kadmind -P /run/krb5kdc/kadmind.pid $KADMIND_ARGS
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+User=krb5kdc
+Group=krb5kdc
diff --git a/install/share/kdc.conf.template b/install/share/kdc.conf.template
index 232fedc445..e650d65c63 100644
--- a/install/share/kdc.conf.template
+++ b/install/share/kdc.conf.template
@@ -21,3 +21,8 @@
   spake_preauth_indicator = hardened
   encrypted_challenge_indicator = hardened
  }
+
+[logging]
+ default = DEVICE:/dev/null
+ admin_server = FILE:$KADMIND_LOG
+ kdc = FILE:$KRB5KDC_LOG
diff --git a/install/share/krb5kdc-override.conf b/install/share/krb5kdc-override.conf
new file mode 100644
index 0000000000..3aa40b3edb
--- /dev/null
+++ b/install/share/krb5kdc-override.conf
@@ -0,0 +1,7 @@
+[Service]
+PIDFile=/run/krb5kdc/krb5kdc.pid
+ExecStart=
+ExecStart=/usr/sbin/krb5kdc -P /run/krb5kdc/krb5kdc.pid $KRB5KDC_ARGS
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+User=krb5kdc
+Group=krb5kdc
diff --git a/install/share/krb5kdc.ldif b/install/share/krb5kdc.ldif
new file mode 100644
index 0000000000..f4ecd3a05e
--- /dev/null
+++ b/install/share/krb5kdc.ldif
@@ -0,0 +1,16 @@
+# Kerberos KDC service user
+dn: cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX
+changetype: add
+objectClass: ipaIDobject
+objectClass: top
+cn: krb5kdc
+uidNumber: $KRB5KDC_UID
+gidNumber: $KRB5KDC_GID
+# cannot use memberOf permission here because permissions are set up too late
+
+# HACK: allow KRB5KDC to read/write/search/compare everything until I have
+# figured out better ACIs.
+dn: $SUFFIX
+changetype: modify
+add: aci
+aci: (targetattr = "*")(version 3.0;acl "KRB5KDC: Allow all";allow (all) userdn = "ldap:///cn=krb5kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
diff --git a/install/share/root-autobind.ldif b/install/share/root-autobind.ldif
index ecce11511d..ebf163c7cd 100644
--- a/install/share/root-autobind.ldif
+++ b/install/share/root-autobind.ldif
@@ -17,3 +17,9 @@ changetype: modify
 replace: nsslapd-ldapimaptoentries
 nsslapd-ldapimaptoentries: on
 
+# search suffix for krb5kdc account
+# the search base deliberately excludes standard user accounts
+dn: cn=config
+changetype: modify
+replace: nsslapd-ldapientrysearchbase
+nsslapd-ldapientrysearchbase: cn=sysaccounts,cn=etc,$SUFFIX
diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
index 08b34708a2..54102c38d1 100644
--- a/ipaplatform/base/constants.py
+++ b/ipaplatform/base/constants.py
@@ -81,5 +81,8 @@ class BaseConstantsNamespace:
     # remote password, and DSA cert authentication.
     TLS_HIGH_CIPHERS = "HIGH:!aNULL:!eNULL:!MD5:!RC4:!3DES:!PSK:!SRP:!aDSS"
 
+    KRB5KDC_USER = "krb5kdc"
+    KRB5KDC_GROUP = "krb5kdc"
+
 
 constants = BaseConstantsNamespace()
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 6310869450..8a69de5351 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -165,6 +165,10 @@ class BasePathNamespace:
     SYSTEMD_PKI_TOMCAT_SERVICE = "/etc/systemd/system/pki-tomcatd.target.wants/pki-tomcatd@pki-tomcat.service"
     SYSTEMD_PKI_TOMCAT_IPA_CONF = \
         "/etc/systemd/system/pki-tomcatd@pki-tomcat.service.d/ipa.conf"
+    SYSTEMD_SYSTEM_KRB5KDC_OVERRIDE = \
+        "/etc/systemd/system/krb5kdc.service.d/ipa.conf"
+    SYSTEMD_SYSTEM_KADMIN_OVERRIDE = \
+        "/etc/systemd/system/kadmin.service.d/ipa.conf"
     ETC_TMPFILESD_DIRSRV = "/etc/tmpfiles.d/dirsrv-%s.conf"
     DNSSEC_TRUSTED_KEY = "/etc/trusted-key.key"
     HOME_DIR = "/home"
@@ -354,8 +358,8 @@ class BasePathNamespace:
     IPAUPGRADE_LOG = "/var/log/ipaupgrade.log"
     IPATRUSTENABLEAGENT_LOG = "/var/log/ipatrust-enable-agent.log"
     IPAEPN_LOG = "/var/log/ipaepn.log"
-    KADMIND_LOG = "/var/log/kadmind.log"
-    KRB5KDC_LOG = "/var/log/krb5kdc.log"
+    KADMIND_LOG = "/var/log/krb5kdc/kadmind.log"
+    KRB5KDC_LOG = "/var/log/krb5kdc/krb5kdc.log"
     MESSAGES = "/var/log/messages"
     VAR_LOG_PKI_DIR = "/var/log/pki/"
     BIN_TOMCAT = "/usr/sbin/tomcat"
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 3fc0de371e..dbbba9d6dc 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -1253,6 +1253,7 @@ def add_ca_cert(self, cacert_fname, cacert_name=''):
     def __root_autobind(self):
         self._ldap_mod(
             "root-autobind.ldif",
+            sub_dict=self.sub_dict,
             ldap_uri=ipaldap.get_ldap_uri(realm=self.realm, protocol='ldapi'),
             # must simple bind until auto bind is configured
             dm_password=self.dm_password
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 64806db4c8..75c7bb3651 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -195,6 +195,8 @@ class Backup(admintool.AdminTool):
         paths.GSSPROXY_CONF,
         paths.HOSTS,
         paths.SYSTEMD_PKI_TOMCAT_IPA_CONF,
+        paths.SYSTEMD_SYSTEM_KRB5KDC_OVERRIDE,
+        paths.SYSTEMD_SYSTEM_KADMIN_OVERRIDE,
     ) + tuple(
         os.path.join(paths.IPA_NSSDB_DIR, file)
         for file in (certdb.NSS_DBM_FILES + certdb.NSS_SQL_FILES)
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 1910ff3740..58e58549ea 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -23,6 +23,8 @@
 import logging
 import os
 import pwd
+import grp
+import shutil
 import socket
 import dbus
 
@@ -203,6 +205,7 @@ def create_instance(self, realm_name, host_name, domain_name, admin_password, ma
         self.step("configuring KDC", self.__configure_instance)
         self.step("initialize kerberos container", self.__init_ipa_kdb)
         self.step("adding default ACIs", self.__add_default_acis)
+        self.step("adding KRB5 KDC sysaccount", self._add_krb5kdc_sysaccount)
         self.step("creating a keytab for the directory", self.__create_ds_keytab)
         self.step("creating a keytab for the machine", self.__create_host_keytab)
         self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
@@ -257,6 +260,9 @@ def __setup_sub_dict(self):
         else:
             includes = ''
 
+        krb5kdc_uid = pwd.getpwnam(constants.KRB5KDC_USER).pw_uid
+        krb5kdc_gid = grp.getgrnam(constants.KRB5KDC_GROUP).gr_gid
+
         self.sub_dict = dict(FQDN=self.fqdn,
                              IP=self.ip,
                              PASSWORD=self.kdc_password,
@@ -274,7 +280,12 @@ def __setup_sub_dict(self):
                              KDC_CA_BUNDLE_PEM=paths.KDC_CA_BUNDLE_PEM,
                              CA_BUNDLE_PEM=paths.CA_BUNDLE_PEM,
                              INCLUDES=includes,
-                             FIPS='#' if tasks.is_fips_enabled() else '')
+                             FIPS='#' if tasks.is_fips_enabled() else '',
+                             KRB5KDC_UID=krb5kdc_uid,
+                             KRB5KDC_GID=krb5kdc_gid,
+                             KADMIND_LOG=paths.KADMIND_LOG,
+                             KRB5KDC_LOG=paths.KRB5KDC_LOG,
+                             )
 
         # IPA server/KDC is not a subdomain of default domain
         # Proper domain-realm mapping needs to be specified
@@ -312,6 +323,22 @@ def __add_krb_container(self):
     def __add_default_acis(self):
         self._ldap_mod("default-aci.ldif", self.sub_dict)
 
+    def _add_krb5kdc_sysaccount(self):
+        self._ldap_mod("krb5kdc.ldif", self.sub_dict)
+        # allow krb5kdc user to read config files
+        os.chown(paths.KRB5KDC_KDC_CONF, 0, self.sub_dict["KRB5KDC_GID"])
+        os.chmod(paths.KRB5KDC_KDC_CONF, 0o640)
+        os.chown(paths.KRB5KDC_KADM5_ACL, 0, self.sub_dict["KRB5KDC_GID"])
+        os.chmod(paths.KRB5KDC_KADM5_ACL, 0o640)
+        # modify systemd services
+        self._copy_file(
+            "krb5kdc-override.conf", paths.SYSTEMD_SYSTEM_KRB5KDC_OVERRIDE
+        )
+        self._copy_file(
+            "kadmin-override.conf", paths.SYSTEMD_SYSTEM_KADMIN_OVERRIDE
+        )
+        tasks.systemd_daemon_reload()
+
     def __template_file(self, path, chmod=0o644, client_template=False):
         if client_template:
             sharedir = paths.USR_SHARE_IPA_CLIENT_DIR
@@ -326,6 +353,11 @@ def __template_file(self, path, chmod=0o644, client_template=False):
                 os.fchmod(f.fileno(), chmod)
             f.write(conf)
 
+    def _copy_file(self, name, dst):
+        directory = os.path.dirname(dst)
+        os.makedirs(directory, exist_ok=True)
+        shutil.copyfile(os.path.join(paths.USR_SHARE_IPA_DIR, name), dst)
+
     def __init_ipa_kdb(self):
         # kdb5_util may take a very long time when entropy is low
         installutils.check_entropy()
@@ -347,7 +379,7 @@ def __init_ipa_kdb(self):
             raise RuntimeError("Failed to initialize kerberos container")
 
     def __configure_instance(self):
-        self.__template_file(paths.KRB5KDC_KDC_CONF, chmod=None)
+        self.__template_file(paths.KRB5KDC_KDC_CONF, chmod=0o640)
         self.__template_file(paths.KRB5_CONF)
         self.__template_file(paths.KRB5_FREEIPA_SERVER)
         self.__template_file(paths.KRB5_FREEIPA, client_template=True)
@@ -632,6 +664,8 @@ def uninstall(self):
         # stop tracking and remove certificates
         self.stop_tracking_certs()
         ipautil.remove_file(paths.CACERT_PEM)
+        ipautil.remove_file(paths.SYSTEMD_SYSTEM_KRB5KDC_OVERRIDE)
+        ipautil.remove_file(paths.SYSTEMD_SYSTEM_KADMIN_OVERRIDE)
         self.delete_pkinit_cert()
 
         if running:
diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index 35041a9256..5b5d2512cc 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -232,7 +232,6 @@ def __disable_listeners(self):
                 parser = installutils.ModifyLDIF(in_file, out_file)
                 parser.replace_value("cn=config", "nsslapd-port", [b"0"])
                 parser.replace_value("cn=config", "nsslapd-security", [b"off"])
-                parser.remove_value("cn=config", "nsslapd-ldapientrysearchbase")
                 parser.parse()
 
         shutil.copy2(ldif_outfile, self.filename)
