From 766b534da0c3a1ed09fe187323eaae0440eb7784 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Mon, 7 Dec 2009 23:17:00 -0500 Subject: 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. --- ipaserver/install/bindinstance.py | 12 ++------ ipaserver/install/cainstance.py | 4 +-- ipaserver/install/certs.py | 12 ++++++++ ipaserver/install/dsinstance.py | 9 ++++-- ipaserver/install/httpinstance.py | 16 ++++++++--- ipaserver/install/krbinstance.py | 38 +++++++++++++++++++++++++ ipaserver/install/ldapupdate.py | 3 +- ipaserver/install/service.py | 60 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 134 insertions(+), 20 deletions(-) (limited to 'ipaserver') diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index e2edcd39..9150c8ed 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 198b0c64..46a337cb 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 c4923a75..a1dffff2 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 c25b9753..33ff053c 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 94e155bf..ee62f81f 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 a33ab233..c4a20b54 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 c90f153d..ad2067c0 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 75868898..5e2eb63d 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 -- cgit