summaryrefslogtreecommitdiffstats
path: root/ipaserver/install
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2014-09-24 16:41:47 +0200
committerMartin Kosek <mkosek@redhat.com>2014-09-30 08:50:47 +0200
commit88083887c994ab505d6e07151e5dd26b56bb7732 (patch)
treefde6a1a529a9c5969082acf081854672154fa22a /ipaserver/install
parent3aa0731fc660ea3d111a44926ab5dea71dc510e7 (diff)
downloadfreeipa-88083887c994ab505d6e07151e5dd26b56bb7732.tar.gz
freeipa-88083887c994ab505d6e07151e5dd26b56bb7732.tar.xz
freeipa-88083887c994ab505d6e07151e5dd26b56bb7732.zip
CA-less installer options usability fixes
The --*_pkcs12 options of ipa-server-install and ipa-replica-prepare have been replaced by --*-cert-file options which accept multiple files. ipa-server-certinstall now accepts multiple files as well. The files are accepted in PEM and DER certificate, PKCS#7 certificate chain, PKCS#8 and raw private key and PKCS#12 formats. The --root-ca-file option of ipa-server-install has been replaced by --ca-cert-file option which accepts multiple files. The files are accepted in PEM and DER certificate and PKCS#7 certificate chain formats. The --*_pin options of ipa-server-install and ipa-replica-prepare have been renamed to --*-pin. https://fedorahosted.org/freeipa/ticket/4489 Reviewed-By: Petr Viktorin <pviktori@redhat.com>
Diffstat (limited to 'ipaserver/install')
-rw-r--r--ipaserver/install/installutils.py126
-rw-r--r--ipaserver/install/ipa_replica_prepare.py150
-rw-r--r--ipaserver/install/ipa_server_certinstall.py26
3 files changed, 176 insertions, 126 deletions
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 395023f6c..757bc5b1b 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -801,72 +801,98 @@ def handle_error(error, log_file_name=None):
return message, 1
-def check_pkcs12(pkcs12_info, ca_file, hostname):
- """Check the given PKCS#12 with server cert and return the cert nickname
-
- This is used for files given to --*_pkcs12 to ipa-server-install and
- ipa-replica-prepare.
+def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
+ host_name):
+ """
+ Load and verify server certificate and private key from multiple files
+
+ The files are accepted in PEM and DER certificate, PKCS#7 certificate
+ chain, PKCS#8 and raw private key and PKCS#12 formats.
+
+ :param cert_files: Names of server certificate and private key files to
+ import
+ :param key_password: Password to decrypt private keys
+ :param key_nickname: Nickname of the private key to import from PKCS#12
+ files
+ :param ca_cert_files: Names of CA certificate files to import
+ :param host_name: Host name of the server
+ :returns: Temporary PKCS#12 file with the server certificate, private key
+ and CA certificate chain, password to unlock the PKCS#12 file and
+ the CA certificate of the CA that issued the server certificate
"""
- pkcs12_filename, pkcs12_passwd = pkcs12_info
- root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
- db_pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
with certs.NSSDatabase() as nssdb:
- nssdb.create_db(db_pwd_file.name)
-
- # Import the CA cert first so it has a known nickname
- # (if it's present in the PKCS#12 it won't be overwritten)
- ca_cert_name = 'The Root CA'
- if ca_file:
- try:
- nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
- except (ValueError, RuntimeError) as e:
- raise ScriptError(str(e))
+ db_password = ipautil.ipa_generate_password()
+ db_pwdfile = ipautil.write_tmp_file(db_password)
+ nssdb.create_db(db_pwdfile.name)
- # Import everything in the PKCS#12
try:
- nssdb.import_pkcs12(
- pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
+ nssdb.import_files(cert_files, db_pwdfile.name,
+ True, key_password, key_nickname)
except RuntimeError as e:
raise ScriptError(str(e))
- # Check we have exactly one server cert (one with a private key)
- server_certs = nssdb.find_server_certs()
- if not server_certs:
- raise ScriptError(
- 'no server certificate found in %s' % pkcs12_filename)
- if len(server_certs) > 1:
- raise ScriptError(
- '%s server certificates found in %s, expecting only one' %
- (len(server_certs), pkcs12_filename))
- [(server_cert_name, _server_cert_trust)] = server_certs
+ if ca_cert_files:
+ try:
+ nssdb.import_files(ca_cert_files, db_pwdfile.name)
+ except RuntimeError as e:
+ raise ScriptError(str(e))
+
+ for nickname, trust_flags in nssdb.list_certs():
+ if 'u' in trust_flags:
+ key_nickname = nickname
+ continue
+ nssdb.trust_root_cert(nickname)
# Check we have the whole cert chain & the CA is in it
- trust_chain = nssdb.get_trust_chain(server_cert_name)
- if len(trust_chain) < 2:
- if ca_file:
- raise ScriptError(
- '%s is not signed by %s, or the full certificate chain is '
- 'not present in the PKCS#12 file' %
- (pkcs12_filename, ca_file))
- else:
- raise ScriptError(
- 'The full certificate chain is not present in %s' %
- pkcs12_filename)
- if ca_file and trust_chain[-2] != ca_cert_name:
+ trust_chain = list(reversed(nssdb.get_trust_chain(key_nickname)))
+ ca_cert = None
+ for nickname in trust_chain[1:]:
+ cert = nssdb.get_cert(nickname)
+ if ca_cert is None:
+ ca_cert = cert
+
+ nss_cert = x509.load_certificate(cert, x509.DER)
+ subject = DN(str(nss_cert.subject))
+ issuer = DN(str(nss_cert.issuer))
+ del nss_cert
+
+ if subject == issuer:
+ break
+ else:
raise ScriptError(
- '%s is not signed by %s' % (pkcs12_filename, ca_file))
- ca_cert_name = trust_chain[-2]
+ "The full certificate chain is not present in %s" %
+ (", ".join(cert_files)))
+
+ for nickname in trust_chain[1:]:
+ try:
+ nssdb.verify_ca_cert_validity(nickname)
+ except ValueError, e:
+ raise ScriptError(
+ "CA certificate %s in %s is not valid: %s" %
+ (subject, ", ".join(cert_files), e))
# Check server validity
- nssdb.trust_root_cert(ca_cert_name)
try:
- nssdb.verify_server_cert_validity(server_cert_name, hostname)
+ nssdb.verify_server_cert_validity(key_nickname, host_name)
except ValueError as e:
raise ScriptError(
- 'The server certificate in %s is not valid: %s' %
- (pkcs12_filename, e))
+ "The server certificate in %s is not valid: %s" %
+ (", ".join(cert_files), e))
+
+ out_file = tempfile.NamedTemporaryFile()
+ out_password = ipautil.ipa_generate_password()
+ out_pwdfile = ipautil.write_tmp_file(out_password)
+ args = [
+ paths.PK12UTIL,
+ '-o', out_file.name,
+ '-n', key_nickname,
+ '-d', nssdb.secdir,
+ '-k', db_pwdfile.name,
+ '-w', out_pwdfile.name,
+ ]
+ ipautil.run(args)
- return nssdb.get_cert(ca_cert_name)
+ return out_file, out_password, ca_cert
@contextmanager
def private_ccache(path=None):
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index e27eb6dd4..7504172c5 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -22,7 +22,7 @@ import os
import shutil
import tempfile
import time
-from optparse import OptionGroup
+from optparse import OptionGroup, SUPPRESS_HELP
from ConfigParser import SafeConfigParser
import dns.resolver
@@ -75,21 +75,39 @@ class ReplicaPrepare(admintool.AdminTool):
group = OptionGroup(parser, "SSL certificate options",
"Only used if the server was installed using custom SSL certificates")
- group.add_option("--dirsrv_pkcs12", dest="dirsrv_pkcs12",
- metavar="FILE",
- help="install certificate for the directory server")
- group.add_option("--http_pkcs12", dest="http_pkcs12",
- metavar="FILE",
- help="install certificate for the http server")
- group.add_option("--pkinit_pkcs12", dest="pkinit_pkcs12",
- metavar="FILE",
- help="install certificate for the KDC")
- group.add_option("--dirsrv_pin", dest="dirsrv_pin", metavar="PIN",
- help="PIN for the Directory Server PKCS#12 file")
- group.add_option("--http_pin", dest="http_pin", metavar="PIN",
- help="PIN for the Apache Server PKCS#12 file")
- group.add_option("--pkinit_pin", dest="pkinit_pin", metavar="PIN",
- help="PIN for the KDC pkinit PKCS#12 file")
+ group.add_option("--dirsrv-cert-file", dest="dirsrv_cert_files",
+ action="append", metavar="FILE",
+ help="File containing the Directory Server SSL certificate and private key")
+ group.add_option("--dirsrv_pkcs12", dest="dirsrv_cert_files",
+ action="append",
+ help=SUPPRESS_HELP)
+ group.add_option("--http-cert-file", dest="http_cert_files",
+ action="append", metavar="FILE",
+ help="File containing the Apache Server SSL certificate and private key")
+ group.add_option("--http_pkcs12", dest="http_cert_files",
+ action="append",
+ help=SUPPRESS_HELP)
+ group.add_option("--pkinit-cert-file", dest="pkinit_cert_files",
+ action="append", metavar="FILE",
+ help="File containing the Kerberos KDC SSL certificate and private key")
+ group.add_option("--pkinit_pkcs12", dest="pkinit_cert_files",
+ action="append",
+ help=SUPPRESS_HELP)
+ group.add_option("--dirsrv-pin", dest="dirsrv_pin", sensitive=True,
+ metavar="PIN",
+ help="The password to unlock the Directory Server private key")
+ group.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True,
+ help=SUPPRESS_HELP)
+ group.add_option("--http-pin", dest="http_pin", sensitive=True,
+ metavar="PIN",
+ help="The password to unlock the Apache Server private key")
+ group.add_option("--http_pin", dest="http_pin", sensitive=True,
+ help=SUPPRESS_HELP)
+ group.add_option("--pkinit-pin", dest="pkinit_pin", sensitive=True,
+ metavar="PIN",
+ help="The password to unlock the Kerberos KDC private key")
+ group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
+ help=SUPPRESS_HELP)
parser.add_option_group(group)
def validate_options(self):
@@ -112,11 +130,11 @@ class ReplicaPrepare(admintool.AdminTool):
options.setup_pkinit = False
# If any of the PKCS#12 options are selected, all are required.
- pkcs12_req = (options.dirsrv_pkcs12, options.http_pkcs12)
- pkcs12_opt = (options.pkinit_pkcs12,)
- if any(pkcs12_req + pkcs12_opt) and not all(pkcs12_req):
+ cert_file_req = (options.dirsrv_cert_files, options.http_cert_files)
+ cert_file_opt = (options.pkinit_cert_files,)
+ if any(cert_file_req + cert_file_opt) and not all(cert_file_req):
self.option_parser.error(
- "--dirsrv_pkcs12 and --http_pkcs12 are required if any "
+ "--dirsrv-cert-file and --http-cert-file are required if any "
"PKCS#12 options are used.")
if len(self.args) < 1:
@@ -134,11 +152,11 @@ class ReplicaPrepare(admintool.AdminTool):
if api.env.host == self.replica_fqdn:
raise admintool.ScriptError("You can't create a replica on itself")
- if not api.env.enable_ra and not options.http_pkcs12:
+ if not api.env.enable_ra and not options.http_cert_files:
raise admintool.ScriptError(
"Cannot issue certificates: a CA is not installed. Use the "
- "--http_pkcs12, --dirsrv_pkcs12 options to provide custom "
- "certificates.")
+ "--http-cert-file, --dirsrv-cert-file options to provide "
+ "custom certificates.")
config_dir = dsinstance.config_dirname(
dsinstance.realm_to_serverid(api.env.realm))
@@ -146,11 +164,13 @@ class ReplicaPrepare(admintool.AdminTool):
raise admintool.ScriptError(
"could not find directory instance: %s" % config_dir)
- def check_pkcs12(self, pkcs12_file, pkcs12_pin):
- return installutils.check_pkcs12(
- pkcs12_info=(pkcs12_file, pkcs12_pin),
- ca_file=CACERT,
- hostname=self.replica_fqdn)
+ def load_pkcs12(self, cert_files, key_password, key_nickname):
+ return installutils.load_pkcs12(
+ cert_files=cert_files,
+ key_password=key_password,
+ key_nickname=key_nickname,
+ ca_cert_files=[CACERT],
+ host_name=self.replica_fqdn)
def ask_for_options(self):
options = self.options
@@ -231,42 +251,52 @@ class ReplicaPrepare(admintool.AdminTool):
if disconnect:
api.Backend.ldap2.disconnect()
- if options.http_pkcs12:
+ self.http_pin = self.dirsrv_pin = self.pkinit_pin = None
+
+ if options.http_cert_files:
if options.http_pin is None:
options.http_pin = installutils.read_password(
- "Enter %s unlock" % options.http_pkcs12,
+ "Enter Apache Server private key unlock",
confirm=False, validate=False)
if options.http_pin is None:
raise admintool.ScriptError(
- "%s unlock password required" % options.http_pkcs12)
- http_ca_cert = self.check_pkcs12(
- options.http_pkcs12, options.http_pin)
+ "Apache Server private key unlock password required")
+ http_pkcs12_file, http_pin, http_ca_cert = self.load_pkcs12(
+ options.http_cert_files, options.http_pin, None)
+ self.http_pkcs12_file = http_pkcs12_file
+ self.http_pin = http_pin
- if options.dirsrv_pkcs12:
+ if options.dirsrv_cert_files:
if options.dirsrv_pin is None:
options.dirsrv_pin = installutils.read_password(
- "Enter %s unlock" % options.dirsrv_pkcs12,
+ "Enter Directory Server private key unlock",
confirm=False, validate=False)
if options.dirsrv_pin is None:
raise admintool.ScriptError(
- "%s unlock password required" % options.dirsrv_pkcs12)
- dirsrv_ca_cert = self.check_pkcs12(
- options.dirsrv_pkcs12, options.dirsrv_pin)
+ "Directory Server private key unlock password required")
+ dirsrv_pkcs12_file, dirsrv_pin, dirsrv_ca_cert = self.load_pkcs12(
+ options.dirsrv_cert_files, options.dirsrv_pin, None)
+ self.dirsrv_pkcs12_file = dirsrv_pkcs12_file
+ self.dirsrv_pin = dirsrv_pin
- if options.pkinit_pkcs12:
+ if options.pkinit_cert_files:
if options.pkinit_pin is None:
options.pkinit_pin = installutils.read_password(
- "Enter %s unlock" % options.pkinit_pkcs12,
+ "Enter Kerberos KDC private key unlock",
confirm=False, validate=False)
if options.pkinit_pin is None:
raise admintool.ScriptError(
- "%s unlock password required" % options.pkinit_pkcs12)
+ "Kerberos KDC private key unlock password required")
+ pkinit_pkcs12_file, pkinit_pin, pkinit_ca_cert = self.load_pkcs12(
+ options.pkinit_cert_files, options.pkinit_pin, None)
+ self.pkinit_pkcs12_file = pkinit_pkcs12_file
+ self.pkinit_pin = pkinit_pin
- if (options.http_pkcs12 and options.dirsrv_pkcs12 and
+ if (options.http_cert_files and options.dirsrv_cert_files and
http_ca_cert != dirsrv_ca_cert):
raise admintool.ScriptError(
- "%s and %s are not signed by the same CA certificate" %
- (options.http_pkcs12, options.dirsrv_pkcs12))
+ "Apache Server SSL certificate and Directory Server SSL "
+ "certificate are not signed by the same CA certificate")
if (not ipautil.file_exists(
dogtag.configured_constants().CS_CFG_PATH) and
@@ -316,13 +346,11 @@ class ReplicaPrepare(admintool.AdminTool):
passwd_fname = os.path.join(self.dir, "dirsrv_pin.txt")
with open(passwd_fname, "w") as fd:
- fd.write("%s\n" % (options.dirsrv_pin or ''))
+ fd.write("%s\n" % (self.dirsrv_pin or ''))
- if options.dirsrv_pkcs12:
- self.log.info(
- "Copying SSL certificate for the Directory Server from %s",
- options.dirsrv_pkcs12)
- self.copy_info_file(options.dirsrv_pkcs12, "dscert.p12")
+ if options.dirsrv_cert_files:
+ self.log.info("Copying SSL certificate for the Directory Server")
+ self.copy_info_file(self.dirsrv_pkcs12_file.name, "dscert.p12")
else:
if ipautil.file_exists(options.ca_file):
# Since it is possible that the Directory Manager password
@@ -339,7 +367,7 @@ class ReplicaPrepare(admintool.AdminTool):
"Creating SSL certificate for the Directory Server")
self.export_certdb("dscert", passwd_fname)
- if not options.dirsrv_pkcs12:
+ if not options.dirsrv_cert_files:
self.log.info(
"Creating SSL certificate for the dogtag Directory Server")
self.export_certdb("dogtagcert", passwd_fname)
@@ -354,13 +382,11 @@ class ReplicaPrepare(admintool.AdminTool):
passwd_fname = os.path.join(self.dir, "http_pin.txt")
with open(passwd_fname, "w") as fd:
- fd.write("%s\n" % (options.http_pin or ''))
+ fd.write("%s\n" % (self.http_pin or ''))
- if options.http_pkcs12:
- self.log.info(
- "Copying SSL certificate for the Web Server from %s",
- options.http_pkcs12)
- self.copy_info_file(options.http_pkcs12, "httpcert.p12")
+ if options.http_cert_files:
+ self.log.info("Copying SSL certificate for the Web Server")
+ self.copy_info_file(self.http_pkcs12_file.name, "httpcert.p12")
else:
self.log.info("Creating SSL certificate for the Web Server")
self.export_certdb("httpcert", passwd_fname)
@@ -373,13 +399,11 @@ class ReplicaPrepare(admintool.AdminTool):
passwd_fname = os.path.join(self.dir, "pkinit_pin.txt")
with open(passwd_fname, "w") as fd:
- fd.write("%s\n" % (options.pkinit_pin or ''))
+ fd.write("%s\n" % (self.pkinit_pin or ''))
- if options.pkinit_pkcs12:
- self.log.info(
- "Copying SSL certificate for the KDC from %s",
- options.pkinit_pkcs12)
- self.copy_info_file(options.pkinit_pkcs12, "pkinitcert.p12")
+ if options.pkinit_cert_files:
+ self.log.info("Copying SSL certificate for the KDC")
+ self.copy_info_file(self.pkinit_pkcs12_file.name, "pkinitcert.p12")
else:
self.log.info("Creating SSL certificate for the KDC")
self.export_certdb("pkinitcert", passwd_fname, is_kdc=True)
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 6300a14ae..1744a6eb8 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -36,7 +36,7 @@ from ipaserver.plugins.ldap2 import ldap2
class ServerCertInstall(admintool.AdminTool):
command_name = 'ipa-server-certinstall'
- usage = "%prog <-d|-w> [options] <PKCS#12 file>"
+ usage = "%prog <-d|-w> [options] <file> ..."
description = "Install new SSL server certificates."
@@ -54,7 +54,7 @@ class ServerCertInstall(admintool.AdminTool):
help="install certificate for the http server")
parser.add_option(
"--pin",
- dest="pin",
+ dest="pin", metavar="PIN", sensitive=True,
help="The password of the PKCS#12 file")
parser.add_option(
"--dirsrv_pin", "--http_pin",
@@ -73,8 +73,8 @@ class ServerCertInstall(admintool.AdminTool):
if not self.options.dirsrv and not self.options.http:
self.option_parser.error("you must specify dirsrv and/or http")
- if len(self.args) != 1:
- self.option_parser.error("you must provide a pkcs12 filename")
+ if not self.args:
+ self.option_parser.error("you must provide certificate filename")
def ask_for_options(self):
super(ServerCertInstall, self).ask_for_options()
@@ -88,17 +88,15 @@ class ServerCertInstall(admintool.AdminTool):
if self.options.pin is None:
self.options.pin = installutils.read_password(
- "Enter %s unlock" % self.args[0], confirm=False, validate=False)
+ "Enter private key unlock", confirm=False, validate=False)
if self.options.pin is None:
raise admintool.ScriptError(
- "%s unlock password required" % self.args[0])
+ "Private key unlock password required")
def run(self):
api.bootstrap(in_server=True)
api.finalize()
- self.pkcs12_fname = self.args[0]
-
if self.options.dirsrv:
self.install_dirsrv_cert()
@@ -154,10 +152,12 @@ class ServerCertInstall(admintool.AdminTool):
os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
- installutils.check_pkcs12(
- pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
- ca_file=CACERT,
- hostname=api.env.host)
+ pkcs12_file, pin, ca_cert = installutils.load_pkcs12(
+ cert_files=self.args,
+ key_password=pkcs12_passwd,
+ key_nickname=None,
+ ca_cert_files=[CACERT],
+ host_name=api.env.host)
cdb = certs.CertDB(api.env.realm, nssdir=dirname)
try:
@@ -165,7 +165,7 @@ class ServerCertInstall(admintool.AdminTool):
cdb.untrack_server_cert(old_cert)
cdb.delete_cert(old_cert)
- cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
+ cdb.import_pkcs12(pkcs12_file.name, pin)
server_cert = cdb.find_server_certs()[0][0]
if api.env.enable_ra: