mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Add support for configuring KDC certs for PKINIT
This patch adds support only for the selfsign case. Replica support is also still missing at this stage.
This commit is contained in:
@@ -24,6 +24,8 @@ app_DATA = \
|
||||
bind.zone.db.template \
|
||||
certmap.conf.template \
|
||||
kdc.conf.template \
|
||||
kdc_extensions.template \
|
||||
kdc_req.conf.template \
|
||||
krb5.conf.template \
|
||||
krb5.ini.template \
|
||||
krb.con.template \
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
dict_file = /usr/share/dict/words
|
||||
default_principal_flags = +preauth
|
||||
; admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
|
||||
pkinit_identity = FILE:/var/kerberos/krb5kdc/kdc.pem
|
||||
pkinit_anchors = FILE:/var/kerberos/krb5kdc/cacert.pem
|
||||
}
|
||||
|
||||
32
install/share/kdc_extensions.template
Normal file
32
install/share/kdc_extensions.template
Normal file
@@ -0,0 +1,32 @@
|
||||
[ kdc_cert ]
|
||||
basicConstraints=CA:FALSE
|
||||
|
||||
# Here are some examples of the usage of nsCertType. If it is omitted
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyAgreement
|
||||
|
||||
#Pkinit EKU
|
||||
extendedKeyUsage = 1.3.6.1.5.2.3.5
|
||||
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
|
||||
# Copy subject details
|
||||
|
||||
issuerAltName=issuer:copy
|
||||
|
||||
# Add id-pkinit-san (pkinit subjectAlternativeName)
|
||||
# Also add the KDC fqdn, for good measure.
|
||||
subjectAltName=otherName:1.3.6.1.5.2.2;SEQUENCE:kdc_princ_name,DNS:${ENV::HOST_FQDN}
|
||||
|
||||
[kdc_princ_name]
|
||||
realm = EXP:0, GeneralString:${ENV::REALM}
|
||||
principal_name = EXP:1, SEQUENCE:kdc_principal_seq
|
||||
|
||||
[kdc_principal_seq]
|
||||
name_type = EXP:0, INTEGER:1
|
||||
name_string = EXP:1, SEQUENCE:kdc_principals
|
||||
|
||||
[kdc_principals]
|
||||
princ1 = GeneralString:krbtgt
|
||||
princ2 = GeneralString:${ENV::REALM}
|
||||
|
||||
14
install/share/kdc_req.conf.template
Normal file
14
install/share/kdc_req.conf.template
Normal file
@@ -0,0 +1,14 @@
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
prompt = no
|
||||
output_password = $PASSWORD
|
||||
|
||||
[ req_distinguished_name ]
|
||||
$SUBJBASE
|
||||
$CERTNAME
|
||||
|
||||
[ req_attributes ]
|
||||
challengePassword = A challenge password
|
||||
|
||||
@@ -106,14 +106,20 @@ def parse_options():
|
||||
default=False, help="uninstall an existing installation")
|
||||
parser.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false",
|
||||
help="do not configure ntp", default=True)
|
||||
parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
|
||||
default=True, help="disables pkinit setup steps")
|
||||
parser.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
|
||||
help="PKCS#12 file containing the Directory Server SSL certificate")
|
||||
parser.add_option("--http_pkcs12", dest="http_pkcs12",
|
||||
help="PKCS#12 file containing the Apache Server SSL certificate")
|
||||
parser.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12",
|
||||
help="PKCS#12 file containing the Kerberos KDC SSL certificate")
|
||||
parser.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True,
|
||||
help="The password of the Directory Server PKCS#12 file")
|
||||
parser.add_option("--http_pin", dest="http_pin", sensitive=True,
|
||||
help="The password of the Apache Server PKCS#12 file")
|
||||
parser.add_option("--pkinit_pin", dest="pkinit_pin",
|
||||
help="The password of the Kerberos KDC PKCS#12 file")
|
||||
parser.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
|
||||
default=False,
|
||||
help="Do not use DNS for hostname lookup during installation")
|
||||
@@ -503,6 +509,8 @@ def main():
|
||||
print " * Configure Apache (httpd)"
|
||||
if options.setup_dns:
|
||||
print " * Configure DNS (bind)"
|
||||
if options.setup_pkinit:
|
||||
print " * Configure the KDC to enable PKINIT"
|
||||
if not options.conf_ntp:
|
||||
print ""
|
||||
print "Excluded by options:"
|
||||
@@ -529,6 +537,12 @@ def main():
|
||||
print "Aborting installation"
|
||||
return 1
|
||||
|
||||
# check the pkinit plugin is installed
|
||||
if options.setup_pkinit:
|
||||
if not krbinstance.check_pkinit_plugin():
|
||||
print "Aborting installation"
|
||||
return 1
|
||||
|
||||
# check the hostname is correctly configured, it must be as the kldap
|
||||
# utilities just use the hostname as returned by gethostbyname to set
|
||||
# up some of the standard entries
|
||||
@@ -722,9 +736,29 @@ def main():
|
||||
else:
|
||||
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, self_signed_ca=options.selfsign, uidstart=options.uidstart, gidstart=options.gidstart, subject_base=options.subject, hbac_allow=not options.hbac_allow)
|
||||
|
||||
if options.pkinit_pin:
|
||||
[pw_fd, pw_name] = tempfile.mkstemp()
|
||||
os.write(pw_fd, options.dirsrv_pin)
|
||||
os.close(pw_fd)
|
||||
|
||||
# Create a kerberos instance
|
||||
krb = krbinstance.KrbInstance(fstore)
|
||||
krb.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, master_password)
|
||||
if options.pkinit_pkcs12:
|
||||
pkcs12_info = (options.pkinit_pkcs12, pw_name)
|
||||
krb.create_instance(ds_user, realm_name, host_name, domain_name,
|
||||
dm_password, master_password,
|
||||
setup_pkinit=options.setup_pkinit,
|
||||
pkcs12_info=pkcs12_info,
|
||||
subject_base=options.subject)
|
||||
else:
|
||||
krb.create_instance(ds_user, realm_name, host_name, domain_name,
|
||||
dm_password, master_password,
|
||||
setup_pkinit=options.setup_pkinit,
|
||||
self_signed_ca=options.selfsign,
|
||||
subject_base=options.subject)
|
||||
|
||||
if options.pkinit_pin:
|
||||
os.remove(pw_name)
|
||||
|
||||
# The DS instance is created before the keytab, add the SSL cert we
|
||||
# generated
|
||||
|
||||
@@ -180,6 +180,7 @@ class CertDB(object):
|
||||
self.certreq_fname = None
|
||||
self.certder_fname = None
|
||||
self.host_name = host_name
|
||||
self.subject_base = subject_base
|
||||
try:
|
||||
self.cwd = os.getcwd()
|
||||
except OSError, e:
|
||||
@@ -187,10 +188,9 @@ class CertDB(object):
|
||||
|
||||
self.self_signed_ca = ipa_self_signed()
|
||||
|
||||
if subject_base:
|
||||
self.subject_format = "CN=%%s,%s" % subject_base
|
||||
else:
|
||||
self.subject_format = "CN=%s,O=IPA"
|
||||
if not subject_base:
|
||||
self.subject_base = "O=IPA"
|
||||
self.subject_format = "CN=%%s,%s" % self.subject_base
|
||||
|
||||
self.cacert_name = get_ca_nickname(self.realm)
|
||||
self.valid_months = "120"
|
||||
@@ -937,6 +937,86 @@ class CertDB(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
def create_kdc_cert(self, nickname, hostname, destdir):
|
||||
"""Create a new certificate with the spcial othername encoding needed
|
||||
by a KDC certificate.
|
||||
|
||||
nickname: the CN name set in the certificate
|
||||
destdir: the location where cert and key are to be installed
|
||||
|
||||
destdir will contain kdc.pem if the operation is successful
|
||||
"""
|
||||
|
||||
reqcfg = "kdc_req.conf"
|
||||
extcfg = ipautil.SHARE_DIR + "kdc_extensions.template"
|
||||
key_fname = destdir + "/kdckey.pem"
|
||||
cert_fname = destdir + "/kdccert.pem"
|
||||
key_cert_fname = destdir + "/kdc.pem"
|
||||
|
||||
# Setup the temp dir
|
||||
self.setup_cert_request()
|
||||
|
||||
# Copy the CA password file because openssl apparently can't use
|
||||
# the same file twice within the same command and throws an error
|
||||
ca_pwd_file = self.reqdir + "pwdfile.txt"
|
||||
shutil.copyfile(self.passwd_fname, ca_pwd_file)
|
||||
|
||||
# Extract the cacert.pem file used by openssl to sign the certs
|
||||
ipautil.run(["/usr/bin/openssl", "pkcs12",
|
||||
"-in", self.pk12_fname,
|
||||
"-passin", "file:" + self.passwd_fname,
|
||||
"-passout", "file:" + ca_pwd_file,
|
||||
"-out", "cacert.pem"])
|
||||
|
||||
# Create the kdc key
|
||||
ipautil.run(["/usr/bin/openssl", "genrsa",
|
||||
"-out", key_fname, "2048"])
|
||||
|
||||
# Prepare a simple cert request
|
||||
req_dict = dict(PASSWORD=self.gen_password(),
|
||||
SUBJBASE=self.subject_base,
|
||||
CERTNAME="CN="+nickname)
|
||||
req_template = ipautil.SHARE_DIR + reqcfg + ".template"
|
||||
conf = ipautil.template_file(req_template, req_dict)
|
||||
fd = open(reqcfg, "w+")
|
||||
fd.write(conf)
|
||||
fd.close()
|
||||
|
||||
base = self.subject_base.replace(",", "/")
|
||||
esc_subject = "CN=%s/%s" % (nickname, base)
|
||||
|
||||
ipautil.run(["/usr/bin/openssl", "req", "-new",
|
||||
"-config", reqcfg,
|
||||
"-subj", esc_subject,
|
||||
"-key", key_fname,
|
||||
"-out", "kdc.req"])
|
||||
|
||||
# Finally, sign the cert using the extensions file to set the
|
||||
# special name
|
||||
ipautil.run(["/usr/bin/openssl", "x509", "-req",
|
||||
"-CA", "cacert.pem",
|
||||
"-extfile", extcfg,
|
||||
"-extensions", "kdc_cert",
|
||||
"-passin", "file:" + ca_pwd_file,
|
||||
"-set_serial", next_serial(),
|
||||
"-in", "kdc.req",
|
||||
"-out", cert_fname],
|
||||
env = { 'REALM':self.realm, 'HOST_FQDN':hostname })
|
||||
|
||||
# Merge key and cert in a single file
|
||||
fd = open(key_fname, "r")
|
||||
key = fd.read()
|
||||
fd.close()
|
||||
fd = open(cert_fname, "r")
|
||||
cert = fd.read()
|
||||
fd.close()
|
||||
fd = open(key_cert_fname, "w")
|
||||
fd.write(key)
|
||||
fd.write(cert)
|
||||
fd.close()
|
||||
os.unlink(key_fname)
|
||||
os.unlink(cert_fname)
|
||||
|
||||
def backup_files(self):
|
||||
self.fstore.backup_file(self.noise_fname)
|
||||
self.fstore.backup_file(self.passwd_fname)
|
||||
|
||||
@@ -44,8 +44,21 @@ import pyasn1.codec.ber.encoder
|
||||
import pyasn1.codec.ber.decoder
|
||||
import struct
|
||||
|
||||
import certs
|
||||
import httpinstance
|
||||
|
||||
KRBMKEY_DENY_ACI = '(targetattr = "krbMKey")(version 3.0; acl "No external access"; deny (read,write,search,compare) userdn != "ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)'
|
||||
|
||||
def check_pkinit_plugin():
|
||||
LIB32 = '/usr/lib/krb5/plugins/preauth/pkinit.so'
|
||||
LIB64 = '/usr/lib64/krb5/plugins/preauth/pkinit.so'
|
||||
if not os.path.exists(LIB32) and not os.path.exists(LIB64):
|
||||
print "The pkinit plugin is missing"
|
||||
print "Please install the 'krb5-pkinit-openssl' package and start the installation again"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def update_key_val_in_file(filename, key, val):
|
||||
if os.path.exists(filename):
|
||||
pattern = "^[\s#]*%s\s*=\s*%s\s*" % (re.escape(key), re.escape(val))
|
||||
@@ -83,6 +96,8 @@ class KrbInstance(service.Service):
|
||||
self.suffix = None
|
||||
self.kdc_password = None
|
||||
self.sub_dict = None
|
||||
self.pkcs12_info = None
|
||||
self.self_signed_ca = None
|
||||
|
||||
if fstore:
|
||||
self.fstore = fstore
|
||||
@@ -158,8 +173,11 @@ class KrbInstance(service.Service):
|
||||
self.step("starting the KDC", self.__start_instance)
|
||||
self.step("configuring KDC to start on boot", self.__enable)
|
||||
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, admin_password, master_password):
|
||||
def create_instance(self, ds_user, realm_name, host_name, domain_name, admin_password, master_password, setup_pkinit=False, pkcs12_info=None, self_signed_ca=False, subject_base=None):
|
||||
self.master_password = master_password
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.self_signed_ca = self_signed_ca
|
||||
self.subject_base = subject_base
|
||||
|
||||
self.__common_setup(ds_user, realm_name, host_name, domain_name, admin_password)
|
||||
|
||||
@@ -175,6 +193,8 @@ class KrbInstance(service.Service):
|
||||
self.step("exporting the kadmin keytab", self.__export_kadmin_changepw_keytab)
|
||||
self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
|
||||
self.step("adding the kerberos master key to the directory", self.__add_master_key)
|
||||
if setup_pkinit:
|
||||
self.step("creating X509 Certificate for PKINIT", self.__setup_pkinit)
|
||||
|
||||
self.__common_post_setup()
|
||||
|
||||
@@ -477,6 +497,30 @@ class KrbInstance(service.Service):
|
||||
self.fstore.backup_file("/etc/sysconfig/ipa_kpasswd")
|
||||
update_key_val_in_file("/etc/sysconfig/ipa_kpasswd", "export KRB5_KTNAME", "/var/kerberos/krb5kdc/kpasswd.keytab")
|
||||
|
||||
def __setup_pkinit(self):
|
||||
if self.self_signed_ca:
|
||||
ca_db = certs.CertDB(httpinstance.NSS_DIR, self.realm,
|
||||
subject_base=self.subject_base)
|
||||
else:
|
||||
ca_db = certs.CertDB(httpinstance.NSS_DIR, self.realm,
|
||||
host_name=self.fqdn,
|
||||
subject_base=self.subject_base)
|
||||
if self.pkcs12_info:
|
||||
|
||||
raise RuntimeError("Using PKCS12 Certs not supported yet\n")
|
||||
|
||||
else:
|
||||
if self.self_signed_ca:
|
||||
ca_db.create_kdc_cert("KDC-Cert", self.fqdn,
|
||||
"/var/kerberos/krb5kdc")
|
||||
else:
|
||||
raise RuntimeError("Using PKCS12 Certs not supported yet\n")
|
||||
|
||||
# Finally copy the cacert in the krb directory so we don't
|
||||
# have any selinux issues with the file context
|
||||
shutil.copyfile("/usr/share/ipa/html/ca.crt",
|
||||
"/var/kerberos/krb5kdc/cacert.pem")
|
||||
|
||||
def uninstall(self):
|
||||
if self.is_configured():
|
||||
self.print_msg("Unconfiguring %s" % self.service_name)
|
||||
|
||||
Reference in New Issue
Block a user