summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2009-12-07 23:17:00 -0500
committerJason Gerard DeRose <jderose@redhat.com>2009-12-11 23:06:08 -0700
commit766b534da0c3a1ed09fe187323eaae0440eb7784 (patch)
tree8eebfdf577f4d64da9fbaa2fea3d5c955514bca7
parent7105a0c0d62583384c6a2d20bc508e35bd227347 (diff)
downloadfreeipa-766b534da0c3a1ed09fe187323eaae0440eb7784.tar.gz
freeipa-766b534da0c3a1ed09fe187323eaae0440eb7784.tar.xz
freeipa-766b534da0c3a1ed09fe187323eaae0440eb7784.zip
Make the IPA server host and its services "real" IPA entries
We use kadmin.local to bootstrap the creation of the kerberos principals for the IPA server machine: host, HTTP and ldap. This works fine and has the side-effect of protecting the services from modification by an admin (which would likely break the server). Unfortunately this also means that the services can't be managed by useful utilities such as certmonger. So we have to create them as "real" services instead.
-rw-r--r--install/share/default-aci.ldif2
-rwxr-xr-xinstall/tools/ipa-replica-install6
-rwxr-xr-xinstall/tools/ipa-server-install8
-rw-r--r--ipaserver/install/bindinstance.py12
-rw-r--r--ipaserver/install/cainstance.py4
-rw-r--r--ipaserver/install/certs.py12
-rw-r--r--ipaserver/install/dsinstance.py9
-rw-r--r--ipaserver/install/httpinstance.py16
-rw-r--r--ipaserver/install/krbinstance.py38
-rw-r--r--ipaserver/install/ldapupdate.py3
-rw-r--r--ipaserver/install/service.py60
11 files changed, 146 insertions, 24 deletions
diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 7f08518b8..784377e9f 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -8,7 +8,7 @@ aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || samba
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword")(version 3.0; acl "Self can write own password"; allow (write) userdn="ldap:///self";)
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admins can write passwords"; allow (add,delete,write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
-aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+aci: (targetattr = "userPassword || krbPrincipalKey || krbPasswordExpiration || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetattr = "krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "KDC System Account can update some fields"; allow (write) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetattr = "krbPrincipalName || krbUPEnabled || krbMKey || krbTicketPolicyReference || krbPrincipalExpiration || krbPasswordExpiration || krbPwdPolicyReference || krbPrincipalType || krbPwdHistory || krbLastPwdChange || krbPrincipalAliases || krbExtraData || krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount")(version 3.0; acl "Only the KDC System Account has access to kerberos material"; allow (read, search, compare) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetfilter = "(|(objectClass=person)(objectClass=krbPrincipalAux)(objectClass=posixAccount)(objectClass=groupOfNames)(objectClass=posixGroup))")(targetattr != "aci || userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Account Admins can manage Users and Groups"; allow (add, delete, read, write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 33d3726c2..766957862 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -200,7 +200,7 @@ def install_http(config):
config.dir + "/http_pin.txt")
http = httpinstance.HTTPInstance()
- http.create_instance(config.realm_name, config.host_name, config.domain_name, False, pkcs12_info, self_signed_ca=True)
+ http.create_instance(config.realm_name, config.host_name, config.domain_name, config.dirman_password, False, pkcs12_info, self_signed_ca=True)
# Now copy the autoconfiguration files
if ipautil.file_exists(config.dir + "/preferences.html"):
@@ -347,6 +347,10 @@ def main():
CA.fix_ra_perms()
service.restart("httpd")
+ # The DS instance is created before the keytab, add the SSL cert we
+ # generated
+ ds.add_cert_to_service()
+
# Create the management framework config file
fd = open("/etc/ipa/default.conf", "w")
fd.write("[global]\n")
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 0694d6ed5..c92989a47 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -729,6 +729,10 @@ def main():
krb = krbinstance.KrbInstance(fstore)
krb.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, master_password)
+ # The DS instance is created before the keytab, add the SSL cert we
+ # generated
+ ds.add_cert_to_service()
+
# Render webui assets:
ipautil.run(["/sbin/restorecon", ASSETS_DIR])
render_assets()
@@ -743,10 +747,10 @@ def main():
http = httpinstance.HTTPInstance(fstore)
if options.http_pkcs12:
pkcs12_info = (options.http_pkcs12, pw_name)
- http.create_instance(realm_name, host_name, domain_name, autoconfig=False, pkcs12_info=pkcs12_info)
+ http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=False, pkcs12_info=pkcs12_info)
os.remove(pw_name)
else:
- http.create_instance(realm_name, host_name, domain_name, autoconfig=True, self_signed_ca=not options.ca)
+ http.create_instance(realm_name, host_name, domain_name, dm_password, autoconfig=True, self_signed_ca=not options.ca)
ipautil.run(["/sbin/restorecon", "/var/cache/ipa/sessions"])
# Create the management framework config file
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index e2edcd392..9150c8edf 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -201,6 +201,7 @@ class BindInstance(service.Service):
# Store the keytab on disk
self.fstore.backup_file("/etc/named.keytab")
installutils.create_keytab("/etc/named.keytab", dns_principal)
+ dns_principal = self.move_service(dns_principal)
# Make sure access is strictly reserved to the named user
pent = pwd.getpwnam(self.named_user)
@@ -220,17 +221,8 @@ class BindInstance(service.Service):
logging.critical("Could not connect to the Directory Server on %s" % self.fqdn)
raise e
- dns_princ_dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (dns_principal, self.realm, self.suffix)
- mod = [(ldap.MOD_ADD, 'objectClass', 'ipaService')]
-
- try:
- conn.modify_s(dns_princ_dn, mod)
- except Exception, e:
- logging.critical("Could not modify principal's %s entry" % dns_principal)
- raise e
-
dns_group = "cn=dnsserver,cn=rolegroups,cn=accounts,%s" % self.suffix
- mod = [(ldap.MOD_ADD, 'member', dns_princ_dn)]
+ mod = [(ldap.MOD_ADD, 'member', dns_principal)]
try:
conn.modify_s(dns_group, mod)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 198b0c644..46a337cb0 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -810,7 +810,7 @@ class CAInstance(service.Service):
os.close(f)
os.chmod(self.ra_agent_pwd, stat.S_IRUSR)
- stdout, stderr = self.__run_certutil(["-N"])
+ (stdout, stderr, returncode) = self.__run_certutil(["-N"])
def __get_ca_chain(self):
try:
@@ -886,7 +886,7 @@ class CAInstance(service.Service):
# Generate our CSR. The result gets put into stdout
try:
- (stdout, stderr) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"])
+ (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", "CN=RA Subsystem Certificate,OU=pki-ipa,O=%s" % self.domain_name, "-z", noise_name, "-a"])
finally:
os.remove(noise_name)
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index c4923a751..a1dffff24 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -26,6 +26,7 @@ import urllib
import xml.dom.minidom
import pwd
import fcntl
+import base64
from ipapython import nsslib
from ipapython import sysrestore
@@ -459,9 +460,20 @@ class CertDB(object):
(out, err) = self.request_cert(subject)
cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
+ fd = open(self.certder_fname, "r")
+ dercert = fd.read()
+ fd.close()
+
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
+ # On the off-chance the certificate is base64-encoded
+ try:
+ dercert = base64.b64decode(dercert)
+ except:
+ pass
+ return dercert
+
def create_signing_cert(self, nickname, hostname, other_certdb=None, subject=None):
cdb = other_certdb
if not cdb:
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index c25b97538..33ff053c3 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -146,6 +146,7 @@ class DsInstance(service.Service):
self.host_name = None
self.pkcs12_info = None
self.ds_user = None
+ self.dercert = None
if realm_name:
self.suffix = util.realm_to_suffix(self.realm_name)
self.__setup_sub_dict()
@@ -164,6 +165,7 @@ class DsInstance(service.Service):
self.self_signed_ca = self_signed_ca
self.uidstart = uidstart
self.gidstart = gidstart
+ self.principal = "ldap/%s@%s" % (self.host_name, self.realm_name)
self.__setup_sub_dict()
self.step("creating directory server user", self.__create_ds_user)
@@ -203,7 +205,7 @@ class DsInstance(service.Service):
REALM=self.realm_name, USER=self.ds_user,
SERVER_ROOT=server_root, DOMAIN=self.domain,
TIME=int(time.time()), UIDSTART=self.uidstart,
- GIDSTART=self.gidstart)
+ GIDSTART=self.gidstart, HOST=self.host_name)
def __create_ds_user(self):
user_exists = True
@@ -335,19 +337,20 @@ class DsInstance(service.Service):
# We only handle one server cert
nickname = server_certs[0][0]
+ self.dercert = dsdb.get_cert_from_db(nickname)
else:
nickname = "Server-Cert"
cadb = certs.CertDB(httpinstance.NSS_DIR, host_name=self.host_name)
if self.self_signed_ca:
cadb.create_self_signed()
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
- dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
+ self.dercert = dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
dsdb.create_pin_file()
else:
# FIXME, need to set this nickname in the RA plugin
cadb.export_ca_cert('ipaCert', False)
dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
- dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
+ self.dercert = dsdb.create_server_cert("Server-Cert", self.host_name, cadb)
dsdb.create_pin_file()
conn = ipaldap.IPAdmin("127.0.0.1")
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 94e155bf0..ee62f81f2 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -30,6 +30,7 @@ import dsinstance
import installutils
from ipapython import sysrestore
from ipapython import ipautil
+from ipalib import util
HTTPD_DIR = "/etc/httpd"
SSL_CONF = HTTPD_DIR + "/conf.d/ssl.conf"
@@ -55,12 +56,16 @@ class HTTPInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
- def create_instance(self, realm, fqdn, domain_name, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
+ def create_instance(self, realm, fqdn, domain_name, dm_password=None, autoconfig=True, pkcs12_info=None, self_signed_ca=False):
self.fqdn = fqdn
self.realm = realm
self.domain = domain_name
+ self.dm_password = dm_password
+ self.suffix = util.realm_to_suffix(self.realm)
self.pkcs12_info = pkcs12_info
self.self_signed_ca = self_signed_ca
+ self.principal = "HTTP/%s@%s" % (self.fqdn, self.realm)
+ self.dercert = None
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
@@ -68,11 +73,11 @@ class HTTPInstance(service.Service):
self.step("Setting mod_nss password file", self.__set_mod_nss_passwordfile)
self.step("Adding URL rewriting rules", self.__add_include)
self.step("configuring httpd", self.__configure_http)
- self.step("creating a keytab for httpd", self.__create_http_keytab)
self.step("Setting up ssl", self.__setup_ssl)
if autoconfig:
self.step("Setting up browser autoconfig", self.__setup_autoconfig)
self.step("publish CA cert", self.__publish_ca_cert)
+ self.step("creating a keytab for httpd", self.__create_http_keytab)
self.step("configuring SELinux for httpd", self.__selinux_config)
self.step("restarting httpd", self.__start)
self.step("configuring httpd to start on boot", self.__enable)
@@ -117,6 +122,8 @@ class HTTPInstance(service.Service):
http_principal = "HTTP/" + self.fqdn + "@" + self.realm
installutils.kadmin_addprinc(http_principal)
installutils.create_keytab("/etc/httpd/conf/ipa.keytab", http_principal)
+ self.move_service(http_principal)
+ self.add_cert_to_service()
pent = pwd.getpwnam("apache")
os.chown("/etc/httpd/conf/ipa.keytab", pent.pw_uid, pent.pw_gid)
@@ -170,16 +177,17 @@ class HTTPInstance(service.Service):
db.create_password_conf()
# We only handle one server cert
nickname = server_certs[0][0]
+ self.dercert = db.get_cert_from_db(nickname)
self.__set_mod_nss_nickname(nickname)
else:
if self.self_signed_ca:
db.create_from_cacert(ca_db.cacert_fname)
db.create_password_conf()
- db.create_server_cert("Server-Cert", self.fqdn, ca_db)
+ self.dercert = db.create_server_cert("Server-Cert", self.fqdn, ca_db)
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
else:
- db.create_server_cert("Server-Cert", self.fqdn, ca_db)
+ self.dercert = db.create_server_cert("Server-Cert", self.fqdn, ca_db)
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
db.create_password_conf()
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index a33ab233c..c4a20b545 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -32,6 +32,7 @@ from ipapython import sysrestore
from ipapython import ipautil
from ipalib import util
from ipalib import errors
+from ipalib import uuid
from ipaserver import ipaldap
@@ -91,6 +92,40 @@ class KrbInstance(service.Service):
else:
self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+ def move_service_to_host(self, principal):
+ """
+ Used to move a host/ service principal created by kadmin.local from
+ cn=kerberos to reside under the host entry.
+ """
+ conn = None
+
+ service_dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, self.realm, self.suffix)
+ try:
+ conn = ipaldap.IPAdmin("127.0.0.1")
+ conn.simple_bind_s("cn=directory manager", self.admin_password)
+ except Exception, e:
+ logging.critical("Could not connect to the Directory Server on %s" % self.fqdn)
+ raise e
+ service_entry = conn.getEntry(service_dn, ldap.SCOPE_BASE)
+ conn.deleteEntry(service_dn)
+
+ # Create a host entry for this master
+ host_dn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, self.suffix)
+ host_entry = ipaldap.Entry(host_dn)
+ host_entry.setValues('objectclass', ['top', 'ipaobject', 'nshost', 'ipahost', 'pkiuser', 'krbprincipalaux', 'krbprincipal', 'krbticketpolicyaux'])
+ host_entry.setValue('krbextradata', service_entry.getValue('krbextradata'))
+ host_entry.setValue('krblastpwdchange', service_entry.getValue('krblastpwdchange'))
+ host_entry.setValue('krbpasswordexpiration', service_entry.getValue('krbpasswordexpiration'))
+ host_entry.setValue('krbprincipalname', service_entry.getValue('krbprincipalname'))
+ host_entry.setValue('krbticketflags', service_entry.getValue('krbticketflags'))
+ host_entry.setValue('krbprincipalkey', service_entry.getValue('krbprincipalkey'))
+ host_entry.setValue('serverhostname', self.fqdn.split('.',1)[0])
+ host_entry.setValue('cn', self.fqdn)
+ host_entry.setValue('fqdn', self.fqdn)
+ host_entry.setValue('ipauniqueid', str(uuid.uuid1()))
+ conn.addEntry(host_entry)
+ conn.unbind()
+
def __common_setup(self, ds_user, realm_name, host_name, domain_name, admin_password):
self.ds_user = ds_user
self.fqdn = host_name
@@ -404,6 +439,7 @@ class KrbInstance(service.Service):
def __create_ds_keytab(self):
ldap_principal = "ldap/" + self.fqdn + "@" + self.realm
installutils.kadmin_addprinc(ldap_principal)
+ self.move_service(ldap_principal)
self.fstore.backup_file("/etc/dirsrv/ds.keytab")
installutils.create_keytab("/etc/dirsrv/ds.keytab", ldap_principal)
@@ -424,6 +460,8 @@ class KrbInstance(service.Service):
os.chown("/etc/krb5.keytab", 0, 0)
os.chmod("/etc/krb5.keytab", 0600)
+ self.move_service_to_host(host_principal)
+
def __export_kadmin_changepw_keytab(self):
installutils.kadmin_modprinc("kadmin/changepw", "+requires_preauth")
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index c90f153db..ad2067c09 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -28,7 +28,7 @@ import sys
from ipaserver.install import installutils
from ipaserver import ipaldap
from ipapython import entity, ipautil
-from ipalib import util
+from ipalib import util, uuid
from ipalib import errors
import ldap
import logging
@@ -124,6 +124,7 @@ class LDAPUpdate:
def _template_str(self, s):
try:
+ self.sub_dict["UUID"] = str(uuid.uuid1())
return ipautil.template_str(s, self.sub_dict)
except KeyError, e:
raise BadSyntax("Unknown template keyword %s" % e)
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index 758688982..5e2eb63dc 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -22,6 +22,10 @@ import os
import tempfile
from ipapython import sysrestore
from ipapython import ipautil
+from ipalib import uuid, errors
+import ldap
+from ipaserver import ipaldap
+import base64
def stop(service_name, instance_name=""):
@@ -98,6 +102,7 @@ class Service:
path = ipautil.SHARE_DIR + ldif
if sub_dict is not None:
+ sub_dict['UUID'] = str(uuid.uuid1())
txt = ipautil.template_file(path, sub_dict)
fd = ipautil.write_tmp_file(txt)
path = fd.name
@@ -120,6 +125,61 @@ class Service:
if fd is not None:
fd.close()
+ def move_service(self, principal):
+ """
+ Used to move a principal entry created by kadmin.local from
+ cn=kerberos to cn=services
+ """
+ dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, self.realm, self.suffix)
+ try:
+ conn = ipaldap.IPAdmin("127.0.0.1")
+ conn.simple_bind_s("cn=directory manager", self.dm_password)
+ except Exception, e:
+ logging.critical("Could not connect to the Directory Server on %s: %s" % (self.fqdn, str(e)))
+ raise e
+ try:
+ entry = conn.getEntry(dn, ldap.SCOPE_BASE)
+ except errors.NotFound:
+ # There is no service in the wrong location, nothing to do.
+ # This can happen when installing a replica
+ conn.unbind()
+ return
+ newdn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, self.suffix)
+ conn.deleteEntry(dn)
+ entry.dn = newdn
+ classes = entry.getValues("objectclass")
+ classes = classes + ["ipaobject", "ipaservice", "pkiuser"]
+ entry.setValues("objectclass", list(set(classes)))
+ entry.setValue("ipauniqueid", str(uuid.uuid1()))
+ conn.addEntry(entry)
+ conn.unbind()
+ return newdn
+
+ def add_cert_to_service(self):
+ """
+ Add a certificate to a service
+
+ This should be passed in DER format but we'll be nice and convert
+ a base64-encoded cert if needed.
+ """
+ try:
+ self.dercert = base64.b64decode(self.dercert)
+ except Exception:
+ pass
+ dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (self.principal, self.suffix)
+ try:
+ conn = ipaldap.IPAdmin("127.0.0.1")
+ conn.simple_bind_s("cn=directory manager", self.dm_password)
+ except Exception, e:
+ logging.critical("Could not connect to the Directory Server on %s: %s" % (self.fqdn, str(e)))
+ raise e
+ mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
+ try:
+ conn.modify_s(dn, mod)
+ except Exception, e:
+ logging.critical("Could not add certificate to service %s entry: %s" % (self.principal, str(e)))
+ conn.unbind()
+
def set_output(self, fd):
self.output_fd = fd