URL: https://github.com/freeipa/freeipa/pull/245
Author: frasertweedale
 Title: #245: Allow full customisability of IPA CA subject DN
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/245/head:pr245
git checkout pr245
From e6a36026124536bb454f71e34ccaafb0abb8001d Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Fri, 11 Nov 2016 18:54:01 +1000
Subject: [PATCH 1/6] Remove unused function argument

Remove an unused function argument.  Also rename the function to
have a more accurate name.

Part of: https://fedorahosted.org/freeipa/ticket/2614
---
 ipaserver/install/server/install.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index b8a46f5..215cb2a 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -238,7 +238,7 @@ def check_dirsrv(unattended):
         raise ScriptError(msg)
 
 
-def set_subject_in_config(realm_name, dm_password, suffix, subject_base):
+def set_subject_base_in_config(realm_name, dm_password, subject_base):
         ldapuri = 'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % (
             installutils.realm_to_serverid(realm_name)
         )
@@ -842,8 +842,7 @@ def install(installer):
     os.chmod(CACERT, 0o644)
     ca_db.publish_ca_cert(CACERT)
 
-    set_subject_in_config(realm_name, dm_password,
-                          ipautil.realm_to_suffix(realm_name), options.subject)
+    set_subject_base_in_config(realm_name, dm_password, options.subject_base)
 
     # Apply any LDAP updates. Needs to be done after the configuration file
     # is created. DS is restarted in the process.

From 5c26acdfebf37ee7ca07845dac1eb4e0ee51a152 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 16 Nov 2016 19:31:19 +1000
Subject: [PATCH 2/6] installutils: remove hardcoded subject DN assumption

`installutils.load_external_cert` assumes that the IPA CA subject
DN is `CN=Certificate Authority, {subject_base}`.  In preparation
for full customisability of IPA CA subject DN, push this assumption
out of this function to call sites (which will be updated in a
subsequent commit).

Part of: https://fedorahosted.org/freeipa/ticket/2614
---
 ipaserver/install/ca.py                | 4 +++-
 ipaserver/install/installutils.py      | 7 ++++---
 ipaserver/install/ipa_cacert_manage.py | 7 +++++--
 3 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 4bbcb12..2605363 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -99,7 +99,9 @@ def install_check(standalone, replica_config, options):
                   "--external-ca.")
 
         external_cert_file, external_ca_file = installutils.load_external_cert(
-            options.external_cert_files, options.subject)
+            options.external_cert_files,
+            DN(('CN', 'Certificate Authority'), options.subject)
+        )
     elif options.external_ca:
         if cainstance.is_step_one_done():
             raise ScriptError(
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 9e509e4..b2fd570 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -1019,7 +1019,8 @@ def check_entropy():
     except ValueError as e:
         root_logger.debug("Invalid value in %s %s", paths.ENTROPY_AVAIL, e)
 
-def load_external_cert(files, subject_base):
+
+def load_external_cert(files, ca_subject):
     """
     Load and verify external CA certificate chain from multiple files.
 
@@ -1027,7 +1028,7 @@ def load_external_cert(files, subject_base):
     chain formats.
 
     :param files: Names of files to import
-    :param subject_base: Subject name base for IPA certificates
+    :param ca_subject: IPA CA subject DN
     :returns: Temporary file with the IPA CA certificate and temporary file
         with the external CA certificate chain
     """
@@ -1041,7 +1042,7 @@ def load_external_cert(files, subject_base):
         except RuntimeError as e:
             raise ScriptError(str(e))
 
-        ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
+        ca_subject = DN(ca_subject)
         ca_nickname = None
         cache = {}
         for nickname, _trust_flags in nssdb.list_certs():
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index 0dcb70f..ac62e75 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -191,8 +191,6 @@ def renew_external_step_2(self, ca, old_cert_der):
 
         options = self.options
         conn = api.Backend.ldap2
-        cert_file, ca_file = installutils.load_external_cert(
-            options.external_cert_files, x509.subject_base())
 
         old_cert_obj = x509.load_certificate(old_cert_der, x509.DER)
         old_der_subject = x509.get_der_subject(old_cert_der, x509.DER)
@@ -201,6 +199,11 @@ def renew_external_step_2(self, ca, old_cert_der):
             serialization.PublicFormat.SubjectPublicKeyInfo
         )
 
+        cert_file, ca_file = installutils.load_external_cert(
+            options.external_cert_files,
+            DN(('CN', 'Certificate Authority'), x509.subject_base())
+        )
+
         with open(cert_file.name) as f:
             new_cert_data = f.read()
         new_cert_der = x509.normalize_certificate(new_cert_data)

From 97faa934aec7a458ebeec01875140b1aa49ccd28 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 16 Nov 2016 19:59:58 +1000
Subject: [PATCH 3/6] installer: rename --subject to --subject-base

The --subject option is actually used to provide the "subject base".
We are also going to add an option for fully specifying the IPA CA
subject DN in a subsequent commit.  So to avoid confusion, rename
--subject to --subject-base, retaining --subject as a deprecated
alias.

Part of: https://fedorahosted.org/freeipa/ticket/2614
---
 install/tools/ipa-ca-install               |  4 ++--
 install/tools/man/ipa-server-install.1     |  4 ++--
 ipaserver/install/ca.py                    | 17 +++++++++--------
 ipaserver/install/server/install.py        | 18 +++++++++---------
 ipaserver/install/server/replicainstall.py |  4 ++--
 5 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index 043ab32..a0dda5b 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -167,7 +167,7 @@ def install_replica(safe_options, options, filename):
     options.domain_name = config.domain_name
     options.dm_password = config.dirman_password
     options.host_name = config.host_name
-    options.subject = config.subject_base
+    options.subject_base = config.subject_base
     if os.path.exists(cafile):
         options.ca_cert_file = cafile
     else:
@@ -196,7 +196,7 @@ def install_master(safe_options, options):
     options.domain_name = api.env.domain
     options.dm_password = dm_password
     options.host_name = api.env.host
-    options.subject = subject_base
+    options.subject_base = subject_base
 
     ca.install_check(True, None, options)
     ca.install(True, None, options)
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 69316fb..e7f3ff4 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -129,8 +129,8 @@ Name of the Kerberos KDC SSL certificate to install
 \fB\-\-ca\-cert\-file\fR=\fIFILE\fR
 File containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC certificates. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. Use this option if the CA certificate is not present in the certificate files.
 .TP
-\fB\-\-subject\fR=\fISUBJECT\fR
-The certificate subject base (default O=REALM.NAME)
+\fB\-\-subject\-base\fR=\fISUBJECT\fR
+The subject base for certificates issued by IPA (default O=REALM.NAME)
 .TP
 \fB\-\-ca\-signing\-algorithm\fR=\fIALGORITHM\fR
 Signing algorithm of the IPA CA certificate. Possible values are SHA1withRSA, SHA256withRSA, SHA512withRSA. Default value is SHA256withRSA. Use this option with --external-ca if the external CA does not support the default signing algorithm.
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 2605363..a146d6a 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -56,7 +56,7 @@ def install_check(standalone, replica_config, options):
 
     realm_name = options.realm_name
     host_name = options.host_name
-    subject_base = options.subject
+    subject_base = options.subject_base
 
     if replica_config is not None:
         if standalone and api.env.ra_plugin == 'selfsign':
@@ -100,7 +100,7 @@ def install_check(standalone, replica_config, options):
 
         external_cert_file, external_ca_file = installutils.load_external_cert(
             options.external_cert_files,
-            DN(('CN', 'Certificate Authority'), options.subject)
+            DN(('CN', 'Certificate Authority'), options.subject_base)
         )
     elif options.external_ca:
         if cainstance.is_step_one_done():
@@ -154,7 +154,7 @@ def install_step_0(standalone, replica_config, options):
     host_name = options.host_name
 
     if replica_config is None:
-        subject_base = options.subject
+        subject_base = options.subject_base
 
         ca_signing_algorithm = options.ca_signing_algorithm
         if options.external_ca:
@@ -226,7 +226,7 @@ def install_step_1(standalone, replica_config, options):
 
     realm_name = options.realm_name
     host_name = options.host_name
-    subject_base = options.subject
+    subject_base = options.subject_base
 
     basedn = ipautil.realm_to_suffix(realm_name)
 
@@ -370,14 +370,15 @@ def external_cert_files(self, value):
         if any(not os.path.isabs(path) for path in value):
             raise ValueError("must use an absolute path")
 
-    subject = knob(
+    subject_base = knob(
         str, None,
         description="The certificate subject base (default O=<realm-name>)",
+        cli_deprecated_names=['--subject'],
     )
-    subject = master_install_only(subject)
+    subject_base = master_install_only(subject_base)
 
-    @subject.validator
-    def subject(self, value):
+    @subject_base.validator
+    def subject_base(self, value):
         v = unicode(value, 'utf-8')
         if any(ord(c) < 0x20 for c in v):
             raise ValueError("must not contain control characters")
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index 215cb2a..2b42ae5 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -481,8 +481,8 @@ def install_check(installer):
     else:
         realm_name = options.realm_name.upper()
 
-    if not options.subject:
-        options.subject = DN(('O', realm_name))
+    if not options.subject_base:
+        options.subject_base = DN(('O', realm_name))
 
     if options.http_cert_files:
         if options.http_pin is None:
@@ -735,7 +735,7 @@ def install(installer):
             ds.create_instance(realm_name, host_name, domain_name,
                                dm_password, dirsrv_pkcs12_info,
                                idstart=options.idstart, idmax=options.idmax,
-                               subject_base=options.subject,
+                               subject_base=options.subject_base,
                                hbac_allow=not options.no_hbac_allow)
         else:
             ds = dsinstance.DsInstance(fstore=fstore,
@@ -745,7 +745,7 @@ def install(installer):
             ds.create_instance(realm_name, host_name, domain_name,
                                dm_password,
                                idstart=options.idstart, idmax=options.idmax,
-                               subject_base=options.subject,
+                               subject_base=options.subject_base,
                                hbac_allow=not options.no_hbac_allow)
 
         ntpinstance.ntp_ldap_enable(host_name, ds.suffix, realm_name)
@@ -757,7 +757,7 @@ def install(installer):
         installer._ds = ds
         ds.init_info(
             realm_name, host_name, domain_name, dm_password,
-            options.subject, 1101, 1100, None)
+            options.subject_base, 1101, 1100, None)
 
     if setup_ca:
         if not options.external_cert_files and options.external_ca:
@@ -792,12 +792,12 @@ def install(installer):
                             dm_password, master_password,
                             setup_pkinit=not options.no_pkinit,
                             pkcs12_info=pkinit_pkcs12_info,
-                            subject_base=options.subject)
+                            subject_base=options.subject_base)
     else:
         krb.create_instance(realm_name, host_name, domain_name,
                             dm_password, master_password,
                             setup_pkinit=not options.no_pkinit,
-                            subject_base=options.subject)
+                            subject_base=options.subject_base)
 
     # restart DS to enable ipa-pwd-extop plugin
     print("Restarting directory server to enable password extension plugin")
@@ -826,13 +826,13 @@ def install(installer):
     if options.http_cert_files:
         http.create_instance(
             realm_name, host_name, domain_name,
-            pkcs12_info=http_pkcs12_info, subject_base=options.subject,
+            pkcs12_info=http_pkcs12_info, subject_base=options.subject_base,
             auto_redirect=not options.no_ui_redirect,
             ca_is_configured=setup_ca)
     else:
         http.create_instance(
             realm_name, host_name, domain_name,
-            subject_base=options.subject,
+            subject_base=options.subject_base,
             auto_redirect=not options.no_ui_redirect,
             ca_is_configured=setup_ca)
     tasks.restore_context(paths.CACHE_IPA_SESSIONS)
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index a7b333c..db8f3bf 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -791,7 +791,7 @@ def install_check(installer):
         if ca_enabled:
             options.realm_name = config.realm_name
             options.host_name = config.host_name
-            options.subject = config.subject_base
+            options.subject_base = config.subject_base
             ca.install_check(False, config, options)
 
         if kra_enabled:
@@ -1193,7 +1193,7 @@ def promote_check(installer):
         if ca_enabled:
             options.realm_name = config.realm_name
             options.host_name = config.host_name
-            options.subject = config.subject_base
+            options.subject_base = config.subject_base
             ca.install_check(False, config, options)
 
         if kra_enabled:

From 14e898537919df4fc15a755f969686b215216662 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 16 Nov 2016 20:39:23 +1000
Subject: [PATCH 4/6] Extract function for computing default subject base

Part of: https://fedorahosted.org/freeipa/ticket/2614
---
 ipaserver/install/cainstance.py            | 8 ++++----
 ipaserver/install/dsinstance.py            | 3 ++-
 ipaserver/install/installutils.py          | 4 ++++
 ipaserver/install/krainstance.py           | 8 ++++----
 ipaserver/install/server/install.py        | 2 +-
 ipaserver/install/server/replicainstall.py | 2 +-
 6 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index c7a117d..1b96d9d 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -353,10 +353,10 @@ def configure_instance(self, host_name, dm_password, admin_password,
             self.clone = True
         self.master_host = master_host
         self.master_replication_port = master_replication_port
-        if subject_base is None:
-            self.subject_base = DN(('O', self.realm))
-        else:
-            self.subject_base = subject_base
+
+        self.subject_base = \
+            subject_base or installutils.default_subject_base(self.realm)
+
         if ca_signing_algorithm is None:
             self.ca_signing_algorithm = 'SHA256withRSA'
         else:
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index a604010..9ae9d22 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -1233,7 +1233,8 @@ def _request_service_keytab(self):
                                          replacevars=vardict)
 
     def __get_ds_cert(self):
-        subject = self.subject_base or DN(('O', self.realm))
+        subject = self.subject_base \
+                or installutils.default_subject_base(self.realm)
         nssdb_dir = config_dirname(self.serverid)
         db = certs.CertDB(self.realm, nssdir=nssdb_dir, subject_base=subject)
         db.create_from_cacert(paths.IPA_CA_CRT)
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index b2fd570..9b63a46 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -1400,3 +1400,7 @@ def restart_dirsrv(instance_name="", capture_output=True):
                                           capture_output=capture_output,
                                           wait=True, ldapi=True)
     api.Backend.ldap2.connect()
+
+
+def default_subject_base(realm_name):
+    return DN(('O', realm_name))
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index 90b1369..879de68 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -91,10 +91,10 @@ def configure_instance(self, realm_name, host_name, dm_password,
         if self.pkcs12_info is not None or promote:
             self.clone = True
         self.master_host = master_host
-        if subject_base is None:
-            self.subject_base = DN(('O', self.realm))
-        else:
-            self.subject_base = subject_base
+
+        self.subject_base = \
+            subject_base or installutils.default_subject_base(realm_name)
+
         self.realm = realm_name
         self.suffix = ipautil.realm_to_suffix(realm_name)
 
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index 2b42ae5..b1157dc 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -482,7 +482,7 @@ def install_check(installer):
         realm_name = options.realm_name.upper()
 
     if not options.subject_base:
-        options.subject_base = DN(('O', realm_name))
+        options.subject_base = installutils.default_subject_base(realm_name)
 
     if options.http_cert_files:
         if options.http_pin is None:
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index db8f3bf..f0bb4ae 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -77,7 +77,7 @@ def install_http_certs(host_name, realm_name, subject_base):
     principal = 'HTTP/%s@%s' % (host_name, realm_name)
     # Obtain certificate for the HTTP service
     nssdir = certs.NSS_DIR
-    subject = subject_base or DN(('O', realm_name))
+    subject = subject_base or installutils.default_subject_base(realm_name)
     db = certs.CertDB(realm_name, nssdir=nssdir, subject_base=subject)
     db.request_service_cert('Server-Cert', principal, host_name, True)
 

From 92036581eed61b37150f71ce36c8a06bea759850 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 16 Nov 2016 20:49:36 +1000
Subject: [PATCH 5/6] Allow full customisability of IPA CA subject DN

Currently only the "subject base" of the IPA CA subject DN can be
customised, via the installer's --subject-base option.  The RDN
"CN=Certificate Authority" is appended to form the subject DN, and
this composition is widely assumed.

Some administrators need more control over the CA subject DN,
especially to satisfy expectations of external CAs when the IPA CA
is to be externally signed.

This patch adds full customisability of the CA subject DN.
Specifically:

- Add the --ca-subject option for specifying the full IPA CA subject
  DN.  Defaults to "CN=Certificate Authority, O=$SUBJECT_BASE".

- Add the --ca-subject and --subject-base options to ipa-ca-install.

- ipa-ca-install, when installing a CA in a previous CA-less
  topology, updates DS certmap.conf and IPA config values with the
  new CA subject DN and subject_base.

- DsInstance.find_subject_base no longer looks in certmap.conf,
  because the CA subject DN can be unrelated to the subject base.

Fixes: https://fedorahosted.org/freeipa/ticket/2614
---
 install/share/certmap.conf.template        |  2 +-
 install/tools/ipa-ca-install               | 68 ++++++++++++++++++++---
 install/tools/man/ipa-ca-install.1         |  6 +++
 install/tools/man/ipa-server-install.1     |  3 ++
 ipaserver/install/ca.py                    | 87 +++++++++++++++++++++---------
 ipaserver/install/cainstance.py            | 14 +++--
 ipaserver/install/certs.py                 | 20 ++++---
 ipaserver/install/dsinstance.py            | 57 ++++++++------------
 ipaserver/install/installutils.py          |  4 ++
 ipaserver/install/ipa_cacert_manage.py     |  4 +-
 ipaserver/install/krainstance.py           |  7 ++-
 ipaserver/install/server/install.py        | 12 ++++-
 ipaserver/install/server/replicainstall.py | 15 ++++--
 13 files changed, 209 insertions(+), 90 deletions(-)

diff --git a/install/share/certmap.conf.template b/install/share/certmap.conf.template
index e76bf3c..d59b095 100644
--- a/install/share/certmap.conf.template
+++ b/install/share/certmap.conf.template
@@ -41,6 +41,6 @@ certmap default         default
 #default:InitFn         <Init function's name>
 default:DNComps
 default:FilterComps     uid
-certmap ipaca           CN=Certificate Authority,$SUBJECT_BASE
+certmap ipaca           $ISSUER_DN
 ipaca:CmapLdapAttr      seeAlso
 ipaca:verifycert        on
diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index a0dda5b..1ca162b 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -20,19 +20,26 @@
 
 import sys
 import os
+import re
 import shutil
 import tempfile
-from ipapython import ipautil
+
+import six
 
 from ipaserver.install import installutils
 from ipaserver.install.installutils import create_replica_config
 from ipaserver.install.installutils import check_creds, ReplicaConfig
 from ipaserver.install import dsinstance, ca
 from ipaserver.install import cainstance, service
+from ipaserver.install import certs, sysupgrade
+from ipaserver.install.server.install import set_subject_base_in_config
 from ipapython import version
-from ipalib import api
+from ipalib import api, x509
 from ipalib.constants import DOMAIN_LEVEL_0
+from ipapython import ipautil
+from ipapython.certdb import get_ca_nickname
 from ipapython.config import IPAOptionParser
+from ipapython.dn import DN
 from ipapython.ipa_log_manager import root_logger, standard_logging_setup
 from ipaplatform.paths import paths
 
@@ -71,6 +78,16 @@ def parse_options():
                       help="Signing algorithm of the IPA CA certificate")
     parser.add_option("-P", "--principal", dest="principal", sensitive=True,
                       default=None, help="User allowed to manage replicas")
+    parser.add_option("--subject-base", dest="subject_base",
+                      default=None,
+                      help=(
+                          "The certificate subject base "
+                          "(default O=<realm-name>)"))
+    parser.add_option("--ca-subject", dest="ca_subject",
+                      default=None,
+                      help=(
+                          "The CA certificate subject DN "
+                          "(default CN=Certificate Authority,O=<realm-name>)"))
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -104,6 +121,30 @@ def get_dirman_password():
         "Directory Manager (existing master)", confirm=False, validate=False)
 
 
+def reset_subject_config(dm_password, realm, ca_subject, subject_base):
+    """(Re)set CA subject and subject base config in all relevant places."""
+    # LDAP
+    set_subject_base_in_config(realm, dm_password, subject_base)
+
+    # sysupgrade
+    sysupgrade.set_upgrade_state('certmap.conf', 'subject_base', subject_base)
+
+    # certmap.conf
+    serverid = installutils.realm_to_serverid(realm)
+    ds_dirname = dsinstance.config_dirname(serverid)
+    certmap_filename = os.path.join(ds_dirname, "certmap.conf")
+    with open(certmap_filename) as f:
+        certmap_conf = f.read()
+    certmap_conf = re.sub(
+        r'^(certmap ipaca\s+)(.*)$',
+        r'\1{}'.format(ca_subject),
+        certmap_conf,
+        0,
+        re.MULTILINE)
+    with open(certmap_filename, 'w') as f:
+        f.write(certmap_conf)
+
+
 def install_replica(safe_options, options, filename):
     if options.promote:
         if filename is not None:
@@ -167,12 +208,17 @@ def install_replica(safe_options, options, filename):
     options.domain_name = config.domain_name
     options.dm_password = config.dirman_password
     options.host_name = config.host_name
-    options.subject_base = config.subject_base
     if os.path.exists(cafile):
         options.ca_cert_file = cafile
     else:
         options.ca_cert_file = None
 
+    # look up CA subject name (needed for DS certmap.conf)
+    ipa_ca_nickname = get_ca_nickname(config.realm_name)
+    db = certs.CertDB(config.realm_name, nssdir=paths.IPA_NSSDB_DIR)
+    cert = db.get_cert_from_db(ipa_ca_nickname)
+    options.ca_subject = six.text_type(DN(x509.load_certificate(cert).subject))
+
     ca.install_check(True, config, options)
     ca.install(True, config, options)
 
@@ -189,16 +235,24 @@ def install_master(safe_options, options):
         if dm_password is None:
             sys.exit("Directory Manager password required")
 
-    config = api.Command['config_show']()['result']
-    subject_base = config['ipacertificatesubjectbase'][0]
-
     options.realm_name = api.env.realm
     options.domain_name = api.env.domain
     options.dm_password = dm_password
     options.host_name = api.env.host
-    options.subject_base = subject_base
+
+    if not options.subject_base:
+        options.subject_base = installutils.default_subject_base(api.env.realm)
+    if not options.ca_subject:
+        options.ca_subject = installutils.default_ca_subject_dn(
+            options.subject_base)
 
     ca.install_check(True, None, options)
+    reset_subject_config(
+        options.password,
+        api.env.realm,
+        options.ca_subject,
+        options.subject_base,
+    )
     ca.install(True, None, options)
 
 
diff --git a/install/tools/man/ipa-ca-install.1 b/install/tools/man/ipa-ca-install.1
index aa18698..16e5431 100644
--- a/install/tools/man/ipa-ca-install.1
+++ b/install/tools/man/ipa-ca-install.1
@@ -46,6 +46,12 @@ Type of the external CA. Possible values are "generic", "ms-cs". Default value i
 \fB\-\-external\-cert\-file\fR=\fIFILE\fR
 File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times.
 .TP
+\fB\-\-ca\-subject\fR=\fISUBJECT\fR
+The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME)
+.TP
+\fB\-\-subject\-base\fR=\fISUBJECT\fR
+The subject base for certificates issued by IPA (default O=REALM.NAME)
+.TP
 \fB\-\-ca\-signing\-algorithm\fR=\fIALGORITHM\fR
 Signing algorithm of the IPA CA certificate. Possible values are SHA1withRSA, SHA256withRSA, SHA512withRSA. Default value is SHA256withRSA. Use this option with --external-ca if the external CA does not support the default signing algorithm.
 .TP
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index e7f3ff4..9bab143 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -129,6 +129,9 @@ Name of the Kerberos KDC SSL certificate to install
 \fB\-\-ca\-cert\-file\fR=\fIFILE\fR
 File containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC certificates. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. Use this option if the CA certificate is not present in the certificate files.
 .TP
+\fB\-\-ca\-subject\fR=\fISUBJECT\fR
+The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME)
+.TP
 \fB\-\-subject\-base\fR=\fISUBJECT\fR
 The subject base for certificates issued by IPA (default O=REALM.NAME)
 .TP
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index a146d6a..00bdd66 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -35,18 +35,41 @@
 if six.PY3:
     unicode = str
 
-VALID_SUBJECT_ATTRS = ['st', 'o', 'ou', 'dnqualifier', 'c',
-                       'serialnumber', 'l', 'title', 'sn', 'givenname',
-                       'initials', 'generationqualifier', 'dc', 'mail',
-                       'uid', 'postaladdress', 'postalcode', 'postofficebox',
-                       'houseidentifier', 'e', 'street', 'pseudonym',
-                       'incorporationlocality', 'incorporationstate',
-                       'incorporationcountry', 'businesscategory']
+VALID_SUBJECT_BASE_ATTRS = {
+    'st', 'o', 'ou', 'dnqualifier', 'c', 'serialnumber', 'l', 'title', 'sn',
+    'givenname', 'initials', 'generationqualifier', 'dc', 'mail', 'uid',
+    'postaladdress', 'postalcode', 'postofficebox', 'houseidentifier', 'e',
+    'street', 'pseudonym', 'incorporationlocality', 'incorporationstate',
+    'incorporationcountry', 'businesscategory',
+}
+VALID_SUBJECT_ATTRS = {'cn'} | VALID_SUBJECT_BASE_ATTRS
 
 external_cert_file = None
 external_ca_file = None
 
 
+def _subject_validator(valid_attrs, value):
+    v = unicode(value, 'utf-8')
+    if any(ord(c) < 0x20 for c in v):
+        raise ValueError("must not contain control characters")
+    if '&' in v:
+        raise ValueError("must not contain an ampersand (\"&\")")
+    try:
+        dn = DN(v)
+        for rdn in dn:
+            if rdn.attr.lower() not in valid_attrs:
+                raise ValueError("invalid attribute: \"%s\"" % rdn.attr)
+    except ValueError as e:
+        raise ValueError("invalid subject base format: %s" % e)
+
+
+def lookup_ca_subject(realm_name):
+    ipa_ca_nickname = certdb.get_ca_nickname(realm_name)
+    db = certs.CertDB(realm_name, nssdir=paths.IPA_NSSDB_DIR)
+    cert = db.get_cert_from_db(ipa_ca_nickname)
+    return unicode(DN(x509.load_certificate(cert).subject))
+
+
 def install_check(standalone, replica_config, options):
     global external_cert_file
     global external_ca_file
@@ -56,9 +79,14 @@ def install_check(standalone, replica_config, options):
 
     realm_name = options.realm_name
     host_name = options.host_name
-    subject_base = options.subject_base
 
-    if replica_config is not None:
+    if replica_config is None:
+        ca_subject = options.ca_subject
+        subject_base = options.subject_base
+    else:
+        ca_subject = lookup_ca_subject(options.realm_name)
+        subject_base = replica_config.subject_base
+
         if standalone and api.env.ra_plugin == 'selfsign':
             raise ScriptError('A selfsign CA can not be added')
 
@@ -99,9 +127,7 @@ def install_check(standalone, replica_config, options):
                   "--external-ca.")
 
         external_cert_file, external_ca_file = installutils.load_external_cert(
-            options.external_cert_files,
-            DN(('CN', 'Certificate Authority'), options.subject_base)
-        )
+            options.external_cert_files, ca_subject)
     elif options.external_ca:
         if cainstance.is_step_one_done():
             raise ScriptError(
@@ -136,7 +162,7 @@ def install_check(standalone, replica_config, options):
                 if not cert:
                     continue
                 subject = DN(x509.load_certificate(cert).subject)
-                if subject in (DN('CN=Certificate Authority', subject_base),
+                if subject in (DN(ca_subject),
                                DN('CN=IPA RA', subject_base)):
                     raise ScriptError(
                         "Certificate with subject %s is present in %s, "
@@ -154,6 +180,7 @@ def install_step_0(standalone, replica_config, options):
     host_name = options.host_name
 
     if replica_config is None:
+        ca_subject = options.ca_subject
         subject_base = options.subject_base
 
         ca_signing_algorithm = options.ca_signing_algorithm
@@ -187,6 +214,7 @@ def install_step_0(standalone, replica_config, options):
                 cafile,
                 replica_config.dirman_password)
 
+        ca_subject = lookup_ca_subject(options.realm_name)
         subject_base = replica_config.subject_base
 
         ca_signing_algorithm = None
@@ -206,6 +234,7 @@ def install_step_0(standalone, replica_config, options):
                                host_name=host_name)
     ca.configure_instance(host_name, dm_password, dm_password,
                           subject_base=subject_base,
+                          subject=ca_subject,
                           ca_signing_algorithm=ca_signing_algorithm,
                           ca_type=ca_type,
                           csr_file=csr_file,
@@ -226,7 +255,11 @@ def install_step_1(standalone, replica_config, options):
 
     realm_name = options.realm_name
     host_name = options.host_name
-    subject_base = options.subject_base
+
+    if replica_config is None:
+        subject_base = options.subject_base
+    else:
+        subject_base = replica_config.subject_base
 
     basedn = ipautil.realm_to_suffix(realm_name)
 
@@ -379,18 +412,20 @@ def external_cert_files(self, value):
 
     @subject_base.validator
     def subject_base(self, value):
-        v = unicode(value, 'utf-8')
-        if any(ord(c) < 0x20 for c in v):
-            raise ValueError("must not contain control characters")
-        if '&' in v:
-            raise ValueError("must not contain an ampersand (\"&\")")
-        try:
-            dn = DN(v)
-            for rdn in dn:
-                if rdn.attr.lower() not in VALID_SUBJECT_ATTRS:
-                    raise ValueError("invalid attribute: \"%s\"" % rdn.attr)
-        except ValueError as e:
-            raise ValueError("invalid subject base format: %s" % e)
+        _subject_validator(VALID_SUBJECT_BASE_ATTRS, value)
+
+    ca_subject = knob(
+        str, None,
+        description=(
+            "The CA certificate subject DN "
+            "(default CN=Certificate Authority,O=<realm-name>)"
+        ),
+    )
+    ca_subject = master_install_only(ca_subject)
+
+    @ca_subject.validator
+    def ca_subject(self, value):
+        _subject_validator(VALID_SUBJECT_ATTRS, value)
 
     ca_signing_algorithm = knob(
         CASigningAlgorithm, None,
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 1b96d9d..b87bbba 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -331,7 +331,8 @@ def configure_instance(self, host_name, dm_password, admin_password,
                            pkcs12_info=None, master_host=None, csr_file=None,
                            cert_file=None, cert_chain_file=None,
                            master_replication_port=None,
-                           subject_base=None, ca_signing_algorithm=None,
+                           subject_base=None, subject=None,
+                           ca_signing_algorithm=None,
                            ca_type=None, ra_p12=None, ra_only=False,
                            promote=False, use_ldaps=False):
         """Create a CA instance.
@@ -356,6 +357,8 @@ def configure_instance(self, host_name, dm_password, admin_password,
 
         self.subject_base = \
             subject_base or installutils.default_subject_base(self.realm)
+        self.subject = \
+            subject or installutils.default_ca_subject_dn(self.subject_base)
 
         if ca_signing_algorithm is None:
             self.ca_signing_algorithm = 'SHA256withRSA'
@@ -515,8 +518,9 @@ def __spawn_instance(self):
             str(DN(('cn', self.fqdn), self.subject_base)))
         config.set("CA", "pki_audit_signing_subject_dn",
             str(DN(('cn', 'CA Audit'), self.subject_base)))
-        config.set("CA", "pki_ca_signing_subject_dn",
-            str(DN(('cn', 'Certificate Authority'), self.subject_base)))
+        config.set(
+            "CA", "pki_ca_signing_subject_dn",
+            str(self.subject))
 
         # Certificate nicknames
         config.set("CA", "pki_subsystem_nickname", "subsystemCert cert-pki-ca")
@@ -695,7 +699,7 @@ def __create_ca_agent(self):
             userCertificate=[cert_data],
             description=['2;%s;%s;%s' % (
                 cert.serial,
-                DN(('CN', 'Certificate Authority'), self.subject_base),
+                DN(self.subject),
                 DN(('CN', 'IPA RA'), self.subject_base))])
         conn.add_entry(entry)
 
@@ -758,7 +762,7 @@ def __import_ca_chain(self):
         st = 1
         en = 0
         subid = 0
-        ca_dn = DN(('CN','Certificate Authority'), self.subject_base)
+        ca_dn = DN(self.subject)
         while st > 0:
             st = certlist.find('-----BEGIN', en)
             en = certlist.find('-----END', en+1)
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 852bcec..e5efe45 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -71,9 +71,19 @@ class CertDB(object):
 
     This class knows IPA-specific details such as nssdir location, or the
     CA cert name.
+
+    ``subject_base``
+      Realm subject base DN.  This argument is required when creating
+      server or object signing certs.
+    ``ca_subject``
+      IPA CA subject DN.  This argument is required when importing
+      CA certificates into the certificate database.
+
     """
     # TODO: Remove all selfsign code
-    def __init__(self, realm, nssdir=NSS_DIR, fstore=None, host_name=None, subject_base=None):
+    def __init__(
+            self, realm, nssdir=NSS_DIR, fstore=None, host_name=None,
+            subject_base=None, ca_subject=None):
         self.nssdb = NSSDatabase(nssdir)
 
         self.secdir = nssdir
@@ -92,15 +102,13 @@ def __init__(self, realm, nssdir=NSS_DIR, fstore=None, host_name=None, subject_b
         self.certreq_fname = None
         self.certder_fname = None
         self.host_name = host_name
+        self.ca_subject = ca_subject
         self.subject_base = subject_base
         try:
             self.cwd = os.getcwd()
         except OSError as e:
             raise RuntimeError("Unable to determine the current directory: %s" % str(e))
 
-        if not subject_base:
-            self.subject_base = DN(('O', 'IPA'))
-
         self.cacert_name = get_ca_nickname(self.realm)
         self.valid_months = "120"
         self.keysize = "1024"
@@ -119,6 +127,7 @@ def __init__(self, realm, nssdir=NSS_DIR, fstore=None, host_name=None, subject_b
         else:
             self.fstore = sysrestore.FileStore(paths.SYSRESTORE)
 
+    ca_subject = ipautil.dn_attribute_property('_ca_subject')
     subject_base = ipautil.dn_attribute_property('_subject_base')
 
     def __del__(self):
@@ -252,13 +261,12 @@ def load_cacert(self, cacert_fname, trust_flags):
         certs = fd.read()
         fd.close()
 
-        ca_dn = DN(('CN','Certificate Authority'), self.subject_base)
         st = 0
         while True:
             try:
                 (cert, st) = find_cert_from_txt(certs, st)
                 _rdn, subject_dn = get_cert_nickname(cert)
-                if subject_dn == ca_dn:
+                if subject_dn == self.ca_subject:
                     nick = get_ca_nickname(self.realm)
                 else:
                     nick = str(subject_dn)
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 9ae9d22..11864ad 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -243,6 +243,7 @@ def __init__(self, realm_name=None, domain_name=None, fstore=None,
         self.dercert = None
         self.idstart = None
         self.idmax = None
+        self.subject = None
         self.subject_base = None
         self.open_ports = []
         self.run_init_memberof = True
@@ -303,7 +304,8 @@ def __common_post_setup(self):
         self.step("configuring directory to start on boot", self.__enable)
 
     def init_info(self, realm_name, fqdn, domain_name, dm_password,
-                  subject_base, idstart, idmax, pkcs12_info, ca_file=None):
+                  subject_base, subject,
+                  idstart, idmax, pkcs12_info, ca_file=None):
         self.realm = realm_name.upper()
         self.serverid = installutils.realm_to_serverid(self.realm)
         self.suffix = ipautil.realm_to_suffix(self.realm)
@@ -311,6 +313,7 @@ def init_info(self, realm_name, fqdn, domain_name, dm_password,
         self.dm_password = dm_password
         self.domain = domain_name
         self.subject_base = subject_base
+        self.subject = subject
         self.idstart = idstart
         self.idmax = idmax
         self.pkcs12_info = pkcs12_info
@@ -322,11 +325,13 @@ def init_info(self, realm_name, fqdn, domain_name, dm_password,
 
     def create_instance(self, realm_name, fqdn, domain_name,
                         dm_password, pkcs12_info=None,
-                        idstart=1100, idmax=999999, subject_base=None,
+                        idstart=1100, idmax=999999,
+                        subject_base=None, subject=None,
                         hbac_allow=True, ca_file=None):
         self.init_info(
             realm_name, fqdn, domain_name, dm_password,
-            subject_base, idstart, idmax, pkcs12_info, ca_file=ca_file)
+            subject_base, subject,
+            idstart, idmax, pkcs12_info, ca_file=ca_file)
 
         self.__common_setup()
         self.step("restarting directory server", self.__restart_instance)
@@ -360,8 +365,9 @@ def enable_ssl(self):
         self.start_creation(runtime=10)
 
     def create_replica(self, realm_name, master_fqdn, fqdn,
-                       domain_name, dm_password, subject_base, api,
-                       pkcs12_info=None, ca_file=None,
+                       domain_name, dm_password,
+                       subject_base, subject,
+                       api, pkcs12_info=None, ca_file=None,
                        ca_is_configured=None, promote=False):
         # idstart and idmax are configured so that the range is seen as
         # depleted by the DNA plugin and the replica will go and get a
@@ -376,6 +382,7 @@ def create_replica(self, realm_name, master_fqdn, fqdn,
             domain_name=domain_name,
             dm_password=dm_password,
             subject_base=subject_base,
+            subject=subject,
             idstart=idstart,
             idmax=idmax,
             pkcs12_info=pkcs12_info,
@@ -761,7 +768,9 @@ def generate_random(self):
 
     def __enable_ssl(self):
         dirname = config_dirname(self.serverid)
-        dsdb = certs.CertDB(self.realm, nssdir=dirname, subject_base=self.subject_base)
+        dsdb = certs.CertDB(
+                self.realm, nssdir=dirname,
+                subject_base=self.subject_base, ca_subject=self.subject)
         if self.pkcs12_info:
             if self.ca_is_configured:
                 trust_flags = 'CT,C,C'
@@ -907,7 +916,7 @@ def __certmap_conf(self):
         shutil.copyfile(ipautil.SHARE_DIR + "certmap.conf.template",
                         config_dirname(self.serverid) + "certmap.conf")
         installutils.update_file(config_dirname(self.serverid) + "certmap.conf",
-                                 '$SUBJECT_BASE', str(self.subject_base))
+                                 '$ISSUER_DN', str(self.subject))
         sysupgrade.set_upgrade_state(
             'certmap.conf',
             'subject_base',
@@ -1049,7 +1058,9 @@ def add_ca_cert(self, cacert_fname, cacert_name=''):
         self.stop()
 
         dirname = config_dirname(installutils.realm_to_serverid(self.realm))
-        certdb = certs.CertDB(self.realm, nssdir=dirname, subject_base=self.subject_base)
+        certdb = certs.CertDB(
+                self.realm, nssdir=dirname,
+                subject_base=self.subject_base, ca_subject=self.subject)
         if not cacert_name or len(cacert_name) == 0:
             cacert_name = "Imported CA"
         # we can't pass in the nickname, so we set the instance variable
@@ -1148,8 +1159,7 @@ def find_subject_base(self):
         Try to find the current value of certificate subject base.
         1) Look in sysupgrade first
         2) If no value is found there, look in DS (start DS if necessary)
-        3) Last resort, look in the certmap.conf itself
-        4) If all fails, log loudly and return None
+        3) If all fails, log loudly and return None
 
         Note that this method can only be executed AFTER the ipa server
         is configured, the api is initialized elsewhere and
@@ -1191,27 +1201,6 @@ def find_subject_base(self):
                 root_logger.error('Cannot connect to DS to find certificate '
                                   'subject base: %s', e)
 
-        if not subject_base:
-            root_logger.debug('Unable to find certificate subject base in DS')
-            root_logger.debug('Trying to find certificate subject base in '
-                              'certmap.conf')
-
-            certmap_dir = config_dirname(
-                installutils.realm_to_serverid(api.env.realm)
-            )
-            try:
-                with open(os.path.join(certmap_dir, 'certmap.conf')) as f:
-                    for line in f:
-                        if line.startswith('certmap ipaca'):
-                            subject_base = line.strip().split(',')[-1]
-                            root_logger.debug(
-                                'Found certificate subject base in certmap.conf: '
-                                '%s', subject_base)
-
-            except IOError as e:
-                root_logger.error('Cannot open certmap.conf to find certificate '
-                                  'subject base: %s', e.strerror)
-
         if subject_base:
             return subject_base
 
@@ -1233,10 +1222,10 @@ def _request_service_keytab(self):
                                          replacevars=vardict)
 
     def __get_ds_cert(self):
-        subject = self.subject_base \
-                or installutils.default_subject_base(self.realm)
         nssdb_dir = config_dirname(self.serverid)
-        db = certs.CertDB(self.realm, nssdir=nssdb_dir, subject_base=subject)
+        db = certs.CertDB(
+            self.realm, nssdir=nssdb_dir,
+            subject_base=self.subject_base, ca_subject=self.subject)
         db.create_from_cacert(paths.IPA_CA_CRT)
         db.request_service_cert(self.nickname, self.principal, self.fqdn)
         db.create_pin_file()
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 9b63a46..7b1ecfb 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -1404,3 +1404,7 @@ def restart_dirsrv(instance_name="", capture_output=True):
 
 def default_subject_base(realm_name):
     return DN(('O', realm_name))
+
+
+def default_ca_subject_dn(subject_base):
+    return DN(('CN', 'Certificate Authority'), subject_base)
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index ac62e75..7964639 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -200,9 +200,7 @@ def renew_external_step_2(self, ca, old_cert_der):
         )
 
         cert_file, ca_file = installutils.load_external_cert(
-            options.external_cert_files,
-            DN(('CN', 'Certificate Authority'), x509.subject_base())
-        )
+            options.external_cert_files, DN(old_cert_obj.subject))
 
         with open(cert_file.name) as f:
             new_cert_data = f.read()
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index 879de68..da80df0 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -78,7 +78,8 @@ def __init__(self, realm):
 
     def configure_instance(self, realm_name, host_name, dm_password,
                            admin_password, pkcs12_info=None, master_host=None,
-                           subject_base=None, ra_only=False, promote=False):
+                           subject_base=None, subject=None,
+                           ra_only=False, promote=False):
         """Create a KRA instance.
 
            To create a clone, pass in pkcs12_info.
@@ -94,6 +95,8 @@ def configure_instance(self, realm_name, host_name, dm_password,
 
         self.subject_base = \
             subject_base or installutils.default_subject_base(realm_name)
+        self.subject = \
+            subject or installutils.default_ca_subject_dn(self.subject_base)
 
         self.realm = realm_name
         self.suffix = ipautil.realm_to_suffix(realm_name)
@@ -304,7 +307,7 @@ def __create_kra_agent(self):
             userCertificate=[cert_data],
             description=['2;%s;%s;%s' % (
                 cert.serial,
-                DN(('CN', 'Certificate Authority'), self.subject_base),
+                DN(self.subject),
                 DN(('CN', 'IPA RA'), self.subject_base))])
         conn.add_entry(entry)
 
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index b1157dc..1e3803a 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -251,7 +251,9 @@ def set_subject_base_in_config(realm_name, dm_password, subject_base):
                                  "on %s" % realm_name)
             raise e
         entry_attrs = conn.get_ipa_config()
-        if 'ipacertificatesubjectbase' not in entry_attrs:
+        cur_subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])
+        if not cur_subject_base[0] \
+                or DN(cur_subject_base[0]) != DN(subject_base):
             entry_attrs['ipacertificatesubjectbase'] = [str(subject_base)]
             conn.update_entry(entry_attrs)
         conn.disconnect()
@@ -484,6 +486,10 @@ def install_check(installer):
     if not options.subject_base:
         options.subject_base = installutils.default_subject_base(realm_name)
 
+    if not options.ca_subject:
+        options.ca_subject = \
+            installutils.default_ca_subject_dn(options.subject_base)
+
     if options.http_cert_files:
         if options.http_pin is None:
             options.http_pin = installutils.read_password(
@@ -736,6 +742,7 @@ def install(installer):
                                dm_password, dirsrv_pkcs12_info,
                                idstart=options.idstart, idmax=options.idmax,
                                subject_base=options.subject_base,
+                               subject=options.ca_subject,
                                hbac_allow=not options.no_hbac_allow)
         else:
             ds = dsinstance.DsInstance(fstore=fstore,
@@ -746,6 +753,7 @@ def install(installer):
                                dm_password,
                                idstart=options.idstart, idmax=options.idmax,
                                subject_base=options.subject_base,
+                               subject=options.ca_subject,
                                hbac_allow=not options.no_hbac_allow)
 
         ntpinstance.ntp_ldap_enable(host_name, ds.suffix, realm_name)
@@ -757,7 +765,7 @@ def install(installer):
         installer._ds = ds
         ds.init_info(
             realm_name, host_name, domain_name, dm_password,
-            options.subject_base, 1101, 1100, None)
+            options.subject_base, options.ca_subject, 1101, 1100, None)
 
     if setup_ca:
         if not options.external_cert_files and options.external_ca:
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index f0bb4ae..116350f 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -77,8 +77,7 @@ def install_http_certs(host_name, realm_name, subject_base):
     principal = 'HTTP/%s@%s' % (host_name, realm_name)
     # Obtain certificate for the HTTP service
     nssdir = certs.NSS_DIR
-    subject = subject_base or installutils.default_subject_base(realm_name)
-    db = certs.CertDB(realm_name, nssdir=nssdir, subject_base=subject)
+    db = certs.CertDB(realm_name, nssdir=nssdir, subject_base=subject_base)
     db.request_service_cert('Server-Cert', principal, host_name, True)
 
 
@@ -93,6 +92,11 @@ def install_replica_ds(config, options, ca_is_configured, remote_api,
         pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12",
                                        "dirsrv_pin.txt")
 
+    if ca_is_configured:
+        ca_subject = ca.lookup_ca_subject(config.realm_name)
+    else:
+        ca_subject = installutils.default_ca_subject_dn(config.subject_base)
+
     ds = dsinstance.DsInstance(
         config_ldif=options.dirsrv_config_file)
     ds.create_replica(
@@ -102,6 +106,7 @@ def install_replica_ds(config, options, ca_is_configured, remote_api,
         domain_name=config.domain_name,
         dm_password=config.dirman_password,
         subject_base=config.subject_base,
+        subject=ca_subject,
         pkcs12_info=pkcs12_info,
         ca_is_configured=ca_is_configured,
         ca_file=ca_file,
@@ -700,6 +705,10 @@ def install_check(installer):
         raise RuntimeError("CA cert file is not available. Please run "
                            "ipa-replica-prepare to create a new replica file.")
 
+    # look up CA subject name (needed for DS certmap.conf)
+    options.subject = unicode(
+        DN(x509.load_certificate_from_file(cafile).subject))
+
     for pkcs12_name, pin_name in (('dscert.p12', 'dirsrv_pin.txt'),
                                   ('httpcert.p12', 'http_pin.txt')):
         pkcs12_info = make_pkcs12_info(config.dir, pkcs12_name, pin_name)
@@ -791,7 +800,6 @@ def install_check(installer):
         if ca_enabled:
             options.realm_name = config.realm_name
             options.host_name = config.host_name
-            options.subject_base = config.subject_base
             ca.install_check(False, config, options)
 
         if kra_enabled:
@@ -1193,7 +1201,6 @@ def promote_check(installer):
         if ca_enabled:
             options.realm_name = config.realm_name
             options.host_name = config.host_name
-            options.subject_base = config.subject_base
             ca.install_check(False, config, options)
 
         if kra_enabled:

From e3419a424dcd794426db8b53a0c4a02abe073c73 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Thu, 17 Nov 2016 09:26:02 +1000
Subject: [PATCH 6/6] Indicate that ca subject / subject base uses LDAP RDN
 order

Update man pages and help output to indicate that --subject-base and
--ca-subject options interpret their arguments in LDAP order.

Fixes: https://fedorahosted.org/freeipa/ticket/6455
---
 install/tools/ipa-ca-install           | 6 ++++--
 install/tools/man/ipa-ca-install.1     | 4 ++--
 install/tools/man/ipa-server-install.1 | 4 ++--
 ipaserver/install/ca.py                | 8 ++++++--
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index 1ca162b..dd0a9b4 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -82,12 +82,14 @@ def parse_options():
                       default=None,
                       help=(
                           "The certificate subject base "
-                          "(default O=<realm-name>)"))
+                          "(default O=<realm-name>).  "
+                          "RDNs are in LDAP order (most specific RDN first)."))
     parser.add_option("--ca-subject", dest="ca_subject",
                       default=None,
                       help=(
                           "The CA certificate subject DN "
-                          "(default CN=Certificate Authority,O=<realm-name>)"))
+                          "(default CN=Certificate Authority,O=<realm-name>). "
+                          "RDNs are in LDAP order (most specific RDN first)."))
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
diff --git a/install/tools/man/ipa-ca-install.1 b/install/tools/man/ipa-ca-install.1
index 16e5431..76ce115 100644
--- a/install/tools/man/ipa-ca-install.1
+++ b/install/tools/man/ipa-ca-install.1
@@ -47,10 +47,10 @@ Type of the external CA. Possible values are "generic", "ms-cs". Default value i
 File containing the IPA CA certificate and the external CA certificate chain. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times.
 .TP
 \fB\-\-ca\-subject\fR=\fISUBJECT\fR
-The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME)
+The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME).  RDNs are in LDAP order (most specific RDN first).
 .TP
 \fB\-\-subject\-base\fR=\fISUBJECT\fR
-The subject base for certificates issued by IPA (default O=REALM.NAME)
+The subject base for certificates issued by IPA (default O=REALM.NAME).  RDNs are in LDAP order (most specific RDN first).
 .TP
 \fB\-\-ca\-signing\-algorithm\fR=\fIALGORITHM\fR
 Signing algorithm of the IPA CA certificate. Possible values are SHA1withRSA, SHA256withRSA, SHA512withRSA. Default value is SHA256withRSA. Use this option with --external-ca if the external CA does not support the default signing algorithm.
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 9bab143..b2db45b 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -130,10 +130,10 @@ Name of the Kerberos KDC SSL certificate to install
 File containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC certificates. The file is accepted in PEM and DER certificate and PKCS#7 certificate chain formats. This option may be used multiple times. Use this option if the CA certificate is not present in the certificate files.
 .TP
 \fB\-\-ca\-subject\fR=\fISUBJECT\fR
-The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME)
+The CA certificate subject DN (default CN=Certificate Authority,O=REALM.NAME).  RDNs are in LDAP order (most specific RDN first).
 .TP
 \fB\-\-subject\-base\fR=\fISUBJECT\fR
-The subject base for certificates issued by IPA (default O=REALM.NAME)
+The subject base for certificates issued by IPA (default O=REALM.NAME).  RDNs are in LDAP order (most specific RDN first).
 .TP
 \fB\-\-ca\-signing\-algorithm\fR=\fIALGORITHM\fR
 Signing algorithm of the IPA CA certificate. Possible values are SHA1withRSA, SHA256withRSA, SHA512withRSA. Default value is SHA256withRSA. Use this option with --external-ca if the external CA does not support the default signing algorithm.
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 00bdd66..af829e8 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -405,7 +405,10 @@ def external_cert_files(self, value):
 
     subject_base = knob(
         str, None,
-        description="The certificate subject base (default O=<realm-name>)",
+        description=(
+            "The certificate subject base (default O=<realm-name>). "
+            "RDNs are in LDAP order (most specific RDN first)."
+        ),
         cli_deprecated_names=['--subject'],
     )
     subject_base = master_install_only(subject_base)
@@ -418,7 +421,8 @@ def subject_base(self, value):
         str, None,
         description=(
             "The CA certificate subject DN "
-            "(default CN=Certificate Authority,O=<realm-name>)"
+            "(default CN=Certificate Authority,O=<realm-name>). "
+            "RDNs are in LDAP order (most specific RDN first)."
         ),
     )
     ca_subject = master_install_only(ca_subject)
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to