On 05/06/15 12:54, Petr Spacek wrote:
On 20.5.2015 18:00, Martin Basti wrote:
This patch allows to disable DNSSEC key master on IPA server, or replace
current DNSSEC key master with another IPA server.

Only for master branch.

https://fedorahosted.org/freeipa/ticket/4657

Patches attached.
NACK. This happens on DNSSEC key master:
$ ipa-dns-install --disable-dnssec-master

Do you want to disable current DNSSEC key master? [no]: yes
Unexpected error - see /var/log/ipaserver-install.log for details:
TypeError: sequence item 0: expected string, DNSName found
        
2015-06-05T10:52:35Z DEBUG   File
"/usr/lib/python2.7/site-packages/ipaserver/install/installutils.py", line
733, in run_script
     return_value = main_function()

   File "/sbin/ipa-dns-install", line 128, in main
     dns_installer.disable_dnssec_master(options.unattended)

   File "/usr/lib/python2.7/site-packages/ipaserver/install/dns.py", line 112,
in disable_dnssec_master
     ", ".join(dnssec_zones))

2015-06-05T10:52:35Z DEBUG The ipa-dns-install command failed, exception:
TypeError: sequence item 0: expected string, DNSName found

Updated patches attached.

Due new installers, more changes were required.

--
Martin Basti

From 6a9488489786215500af1d0a706380f296999ea0 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 13 May 2015 14:45:32 +0200
Subject: [PATCH 1/2] DNSSEC: allow to disable/replace DNSSEC key master

This commit allows to replace or disable DNSSEC key master

Replacing DNSSEC master requires to copy kasp.db file manually by user

ipa-dns-install:
--disable-dnssec-master  DNSSEC master will be disabled
--replace-dnssec-master=IPA_SERVER  DNSSEC master will be replaced, by
IPA_SERVER (required to rerun ipa-dns-install wit appropriate options).
--dnssec-master --kasp-db=FILE  This configure new DNSSEC master server,  kasp.db from old server is required

https://fedorahosted.org/freeipa/ticket/4657
---
 install/tools/ipa-dns-install              |  18 +++
 ipaplatform/base/paths.py                  |   1 +
 ipaserver/install/dns.py                   | 240 ++++++++++++++++++++++++++++-
 ipaserver/install/odsexporterinstance.py   |  12 +-
 ipaserver/install/opendnssecinstance.py    |  69 +++++++--
 ipaserver/install/server/install.py        |  23 +++
 ipaserver/install/server/replicainstall.py |  31 +++-
 7 files changed, 373 insertions(+), 21 deletions(-)

diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index fd9311657e813988310db2be604ca68d26936af5..0f640c3e85b1a5eb717be5082c2fdf030ec4eec5 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -61,6 +61,17 @@ def parse_options():
                       help="DNS zone manager e-mail address. Defaults to hostmaster@DOMAIN")
     parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
                       default=False, help="unattended installation never prompts the user")
+    parser.add_option("--disable-dnssec-master", dest="disable_dnssec_master",
+                      action="store_true", default=False, help="Disable the "
+                      "DNSSEC master on this server")
+    parser.add_option("--replace-dnssec-master", dest="replace_dnssec_master",
+                      type="string", metavar="IPA_DNS_SERVER_HOSTNAME",
+                      action="store", help="Replace the current DNSSEC master "
+                      "with the specified IPA server")
+    parser.add_option("--kasp-db", dest="kasp_db_file", type="string",
+                      metavar="FILE", action="store", help="Copy OpenDNSSEC "
+                      "metadata from the specified file (will not create a new "
+                      "kasp.db file)")
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -70,10 +81,17 @@ def parse_options():
     elif options.reverse_zones and options.no_reverse:
         parser.error("You cannot specify a --reverse-zone option together with --no-reverse")
 
+    if options.disable_dnssec_master and options.replace_dnssec_master:
+        parser.error("You cannot specify a --disable-dnssec-master option "
+                     "together with --replace-dnssec-master")
+
     if options.unattended:
         if not options.forwarders and not options.no_forwarders:
             parser.error("You must specify at least one --forwarder option or --no-forwarders option")
 
+    if options.kasp_db_file and not ipautil.file_exists(options.kasp_db_file):
+        parser.error("File %s does not exist" % options.kasp_db_file)
+
     if options.dm_password:
         print ("WARNING: Option -p/--ds-password is deprecated "
                "and should not be used anymore.")
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index e6b19181929b54f6d83701a0fdbc3c4a54364082..b8c27f09d6812982a38d26ef596c0539cb1b8a8b 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -143,6 +143,7 @@ class BasePathNamespace(object):
     KRA_AGENT_PEM = "/etc/httpd/alias/kra-agent.pem"
     CACERT_P12 = "/root/cacert.p12"
     ROOT_IPA_CSR = "/root/ipa.csr"
+    ROOT_IPA_KASP_DB_BACKUP = "/root/ipa-kasp.db.backup"
     ROOT_TMP_CA_P12 = "/root/tmp-ca.p12"
     NAMED_PID = "/run/named/named.pid"
     IP = "/sbin/ip"
diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
index 8d9570d68c5c84116839371f3ce8e73d71da2db5..91b08a368cba7d50cb6fbb2adf1b788fd7ee7c00 100644
--- a/ipaserver/install/dns.py
+++ b/ipaserver/install/dns.py
@@ -5,9 +5,11 @@
 import sys
 
 from ipalib import api
+from ipalib import errors
 from ipaplatform.paths import paths
 from ipapython import ipautil
 from ipapython import sysrestore
+from ipapython.dn import DN
 from ipapython.ipa_log_manager import root_logger
 from ipapython.ipaldap import AUTOBIND_ENABLED
 from ipapython.ipautil import user_input
@@ -23,6 +25,124 @@ ip_addresses = []
 dns_forwarders = []
 reverse_zones = []
 
+NEW_MASTER_MARK = 'NEW_DNSSEC_MASTER'
+
+
+def _find_dnssec_enabled_zones(conn):
+    search_kw = {'idnssecinlinesigning': True}
+    dnssec_enabled_filter = conn.make_filter(search_kw)
+    dn = DN('cn=dns', api.env.basedn)
+    try:
+        entries, truncated = conn.find_entries(
+            base_dn=dn, filter=dnssec_enabled_filter, attrs_list=['idnsname'])
+    except errors.NotFound:
+        return []
+    else:
+        return [entry.single_value['idnsname'] for entry in entries
+                if 'idnsname' in entry]
+
+
+def _is_master():
+    # test if server is DNSSEC key master
+    masters = opendnssecinstance.get_dnssec_key_masters(api.Backend.ldap2)
+    if api.env.host not in masters:
+        raise RuntimeError("Current server is not DNSSEC key master")
+
+
+def _disable_dnssec():
+    fstore = sysrestore.FileStore(paths.SYSRESTORE)
+
+    ods = opendnssecinstance.OpenDNSSECInstance(
+            fstore, ldapi=True, autobind=AUTOBIND_ENABLED)
+    ods.realm = api.env.realm
+
+    ods_exporter = odsexporterinstance.ODSExporterInstance(fstore, ldapi=True)
+    ods_exporter.realm = api.env.realm
+
+    ods.ldap_connect()
+    ods.ldap_disable('DNSSEC', api.env.host, api.env.basedn)
+
+    ods_exporter.ldap_connect()
+    ods_exporter.ldap_disable('DNSKeyExporter', api.env.host, api.env.basedn)
+    ods_exporter.remove_service()
+
+    ods.uninstall()
+    ods_exporter.uninstall()
+
+    ods.ldap_disconnect()
+    ods_exporter.ldap_disconnect()
+
+    conn = api.Backend.ldap2
+    dn = DN(('cn', 'DNSSEC'), ('cn', api.env.host), ('cn', 'masters'),
+            ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+    try:
+        entry = conn.get_entry(dn)
+    except errors.NotFound:
+        pass
+    else:
+        ipa_config = entry.get('ipaConfigString', [])
+        if opendnssecinstance.KEYMASTER in ipa_config:
+            ipa_config.remove(opendnssecinstance.KEYMASTER)
+            conn.update_entry(entry)
+
+def replace_dnssec_master(new_master, unattended):
+    print "=============================================================================="
+    print "This program will disable DNSSEC key master on current IPA server "
+    print "and mark specified server to be new DNSSEC key master."
+    print ""
+    print "BE CAREFUL, replacing DNSSEC key masters is not recommended, "
+    print "it may break your DNSSEC signatures."
+    print ""
+    print "Replacing DNSSEC key master requires manual configuration, please "
+    print "read documentation."
+    print ("Please copy file from %s after uninstallation. This file is needed "
+           "on new DNSSEC key " % paths.ROOT_IPA_KASP_DB_BACKUP)
+    print "master server"
+    print ""
+    print "This includes:"
+    print ("  * Check the IPA server %s if can be new DNSSEC key master"
+           % new_master)
+    print "  * Unconfigure ipa-ods-exporter"
+    print "  * Unconfigure OpenDNSSEC"
+    print ""
+
+    if (not unattended and not ipautil.user_input(
+            "Do you want to replace current DNSSEC key master?",
+            False)):
+        raise RuntimeError("Cancelled by user.")
+
+    # test if server is DNSSEC key master
+    _is_master()
+
+    # detect if new DNSSEC key master has ipa-dnskeysyncd installed to be sure
+    # the DNSSEC keys are there and we will not lose them
+    dn = DN(('cn', new_master), ('cn', 'masters'), ('cn', 'ipa'),
+            ('cn', 'etc'), api.env.basedn)
+    dn_keysync = DN(('cn', 'DNSKeySync'), dn)
+
+    conn = api.Backend.ldap2
+
+    try:
+        conn.get_entry(dn)
+    except errors.NotFound:
+        raise RuntimeError("Could not find IPA server %s" % new_master)
+
+    try:
+        entry = conn.get_entry(dn_keysync)
+    except errors.NotFound:
+        raise RuntimeError("IPA server %s has not enabled or installed service"
+                           " ipa-dnskeysyncd installed." % new_master)
+    else:
+        ipa_config = entry.get('ipaConfigString', [])
+        if NEW_MASTER_MARK in ipa_config:
+            # new server was already marked to be DNSSEC master
+            pass
+        else:
+            ipa_config.append(NEW_MASTER_MARK)
+            conn.update_entry(entry)
+
+    _disable_dnssec()
+
 
 def install_check(standalone, replica, options, hostname):
     global ip_addresses
@@ -41,14 +161,32 @@ def install_check(standalone, replica, options, hostname):
             print "  * Configure ipa-ods-exporter (required by DNSSEC key master)"
             print "  * Configure OpenDNSSEC (required by DNSSEC key master)"
             print "  * Generate DNSSEC master key (required by DNSSEC key master)"
+        elif options.disable_dnssec_master:
+            print "  * Unconfigure ipa-ods-exporter"
+            print "  * Unconfigure OpenDNSSEC"
+            print ""
+            print "No new zones will be signed without DNSSEC key master IPA server."
+        elif options.replace_dnssec_master:
+            print ("  * Check the IPA server %s if can be new DNSSEC key master"
+                   % options.replace_dnssec_master)
+            print "  * Unconfigure ipa-ods-exporter"
+            print "  * Unconfigure OpenDNSSEC"
+            print ""
+            print "BE CAREFUL, replacing DNSSEC key masters is not recommended, "
+            print "it may break your DNSSEC signatures."
+            print ""
+            print "Replacing DNSSEC key master requires manual configuration, please "
+            print "read documentation."
+            print ("Please copy file from %s after uninstallation. This file is needed "
+                   "on new DNSSEC key " % paths.ROOT_IPA_KASP_DB_BACKUP)
+            print "master server"
         print ""
         print "NOTE: DNSSEC zone signing is not enabled by default"
         print ""
         if options.dnssec_master:
             print "DNSSEC support is experimental!"
             print ""
-            print "Plan carefully, current version doesn't allow you to move DNSSEC"
-            print "key master to different server and master cannot be uninstalled"
+            print "Plan carefully, replacing DNSSEC key master is not recommended"
             print ""
         print ""
         print "To accept the default shown in brackets, press the Enter key."
@@ -59,22 +197,91 @@ def install_check(standalone, replica, options, hostname):
             "Do you want to setup this IPA server as DNSSEC key master?",
             False)):
         sys.exit("Aborted")
+    elif (options.disable_dnssec_master and not options.unattended and not
+          ipautil.user_input(
+            "Do you want to disable current DNSSEC key master?",
+            False)):
+        sys.exit("Aborted")
+    elif (options.replace_dnssec_master and not options.unattended and not
+          ipautil.user_input(
+            "Do you want to replace current DNSSEC key master?",
+            False)):
+        sys.exit("Aborted")
 
     # Check bind packages are installed
     if not (bindinstance.check_inst(options.unattended) and
             dnskeysyncinstance.check_inst()):
         sys.exit("Aborting installation.")
 
-    if options.dnssec_master:
+    if options.disable_dnssec_master or options.replace_dnssec_master:
+        _is_master()
+
+    if options.disable_dnssec_master:
+        dnssec_zones = _find_dnssec_enabled_zones(api.Backend.ldap2)
+        if dnssec_zones:
+            raise RuntimeError(
+                "Cannot disable DNSSEC key master, DNSSEC signing is still "
+                "enabled for following zone(s): %s" %
+                ", ".join([str(zone) for zone in dnssec_zones]))
+
+    elif options.replace_dnssec_master:
+        # detect if new DNSSEC key master has ipa-dnskeysyncd installed to be
+        # sure the DNSSEC keys are there and we will not lose them
+        dn = DN(('cn', options.replace_dnssec_master),
+                api.env.container_masters, api.env.basedn)
+        dn_keysync = DN(('cn', 'DNSKeySync'), dn)
+
+        conn = api.Backend.ldap2
+
+        try:
+            conn.get_entry(dn)
+        except errors.NotFound:
+            raise RuntimeError("Could not find IPA server %s" %
+                               options.replace_dnssec_master)
+
+        try:
+            entry = conn.get_entry(dn_keysync)
+        except errors.NotFound:
+            raise RuntimeError("IPA server %s has not enabled or installed "
+                               "ipa-dnskeysyncd service." %
+                               options.replace_dnssec_master)
+        else:
+            ipa_config = entry.get('ipaConfigString', [])
+            if NEW_MASTER_MARK in ipa_config:
+                # new server was already marked to be DNSSEC master
+                pass
+            else:
+                ipa_config.append(NEW_MASTER_MARK)
+                conn.update_entry(entry)
+
+    elif options.dnssec_master:
         # check opendnssec packages are installed
         if not opendnssecinstance.check_inst():
             sys.exit("Aborting installation")
+        if options.kasp_db_file:
+            local_dnskeysyncd_dn = DN(('cn', 'DNSKeySync'), ('cn', hostname),
+                              ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+                              api.env.basedn)
+            conn = api.Backend.ldap2
+
+            try:
+                dnskeysync_entry = conn.get_entry(local_dnskeysyncd_dn)
+            except errors.NotFound:
+                raise RuntimeError(
+                    "IPA server has not enabled or installed service "
+                    "ipa-dnskeysyncd")
+            else:
+                ipa_config = dnskeysync_entry.get('ipaConfigString', [])
+                if not NEW_MASTER_MARK in ipa_config:
+                    # server is not marked as to be new DNSSEC key master
+                    sys.exit("Server is not marked to be new DNSSEC master")
+
 
     fstore = sysrestore.FileStore(paths.SYSRESTORE)
 
     if options.dnssec_master:
         ods = opendnssecinstance.OpenDNSSECInstance(
-            fstore, ldapi=True, autobind=AUTOBIND_ENABLED)
+            fstore, ldapi=True)
         ods.realm = api.env.realm
         dnssec_masters = ods.get_masters()
         # we can reinstall current server if it is dnssec master
@@ -126,6 +333,11 @@ def install(standalone, replica, options):
     global dns_forwarders
     global reverse_zones
 
+    local_dnskeysyncd_dn = DN(('cn', 'DNSKeySync'), ('cn', api.env.host),
+                              ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
+                              api.env.basedn)
+    conn = api.Backend.ldap2
+
     fstore = sysrestore.FileStore(paths.SYSRESTORE)
 
     conf_ntp = ntpinstance.NTPInstance(fstore).is_enabled()
@@ -149,13 +361,25 @@ def install(standalone, replica, options):
     dnskeysyncd = dnskeysyncinstance.DNSKeySyncInstance(fstore, ldapi=True)
     dnskeysyncd.create_instance(api.env.host, api.env.realm)
     if options.dnssec_master:
-        ods = opendnssecinstance.OpenDNSSECInstance(fstore, ldapi=True,
-                                                    autobind=AUTOBIND_ENABLED)
+        ods = opendnssecinstance.OpenDNSSECInstance(fstore, ldapi=True)
         ods_exporter = odsexporterinstance.ODSExporterInstance(
-            fstore, ldapi=True, autobind=AUTOBIND_ENABLED)
+            fstore, ldapi=True)
 
         ods_exporter.create_instance(api.env.host, api.env.realm)
-        ods.create_instance(api.env.host, api.env.realm)
+        ods.create_instance(api.env.host, api.env.realm,
+                            kasp_db_file=options.kasp_db_file)
+        if options.kasp_db_file:
+            # remove new DNSSEC master mark from dnskeysyncd service
+            # entry must exists
+            dnskeysync_entry = conn.get_entry(local_dnskeysyncd_dn)
+
+            ipa_config = dnskeysync_entry.get('ipaConfigString', [])
+            if NEW_MASTER_MARK in ipa_config:
+                ipa_config.remove(NEW_MASTER_MARK)
+                dnskeysync_entry['ipaConfigString'] = ipa_config
+                conn.update_entry(dnskeysync_entry)
+    elif options.disable_dnssec_master or options.replace_dnssec_master:
+        _disable_dnssec()
 
     dnskeysyncd.start_dnskeysyncd()
     bind.start_named()
diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py
index 5b6245bc48803b4c5545299e4386213319ae859a..c37095cfc3bba8c6724f45d23293bdf6f4a200ee 100644
--- a/ipaserver/install/odsexporterinstance.py
+++ b/ipaserver/install/odsexporterinstance.py
@@ -15,12 +15,12 @@ from ipapython.dn import DN
 from ipapython import sysrestore, ipautil, ipaldap
 from ipaplatform.paths import paths
 from ipaplatform import services
-from ipalib import errors
+from ipalib import errors, api
 
 
 class ODSExporterInstance(service.Service):
     def __init__(self, fstore=None, dm_password=None, ldapi=False,
-                 start_tls=False, autobind=ipaldap.AUTOBIND_DISABLED):
+                 start_tls=False, autobind=ipaldap.AUTOBIND_ENABLED):
         service.Service.__init__(
             self, "ipa-ods-exporter",
             service_desc="IPA OpenDNSSEC exporter daemon",
@@ -150,6 +150,14 @@ class ODSExporterInstance(service.Service):
     def __start(self):
         self.start()
 
+    def remove_service(self):
+        dns_exporter_principal = ("ipa-ods-exporter/%s@%s" % (self.fqdn,
+                                                              self.realm))
+        try:
+            api.Command.service_del(dns_exporter_principal)
+        except errors.NotFound:
+            pass
+
     def uninstall(self):
         if not self.is_configured():
             return
diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
index 5384759858463b25e92b20b65ac633b79519d5dd..f8f0d0f1385435e472182507cb01525624bae1d3 100644
--- a/ipaserver/install/opendnssecinstance.py
+++ b/ipaserver/install/opendnssecinstance.py
@@ -9,6 +9,7 @@ import os
 import pwd
 import grp
 import stat
+import shutil
 
 import _ipap11helper
 
@@ -31,7 +32,7 @@ def get_dnssec_key_masters(conn):
     """
     assert conn is not None
 
-    dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+    dn = DN(api.env.container_masters, api.env.basedn)
 
     filter_attrs = {
         u'cn': u'DNSSEC',
@@ -62,7 +63,7 @@ def check_inst():
 
 class OpenDNSSECInstance(service.Service):
     def __init__(self, fstore=None, dm_password=None, ldapi=False,
-                 start_tls=False, autobind=ipaldap.AUTOBIND_DISABLED):
+                 start_tls=False, autobind=ipaldap.AUTOBIND_ENABLED):
         service.Service.__init__(
             self, "ods-enforcerd",
             service_desc="OpenDNSSEC enforcer daemon",
@@ -94,12 +95,14 @@ class OpenDNSSECInstance(service.Service):
             self.ldap_connect()
         return get_dnssec_key_masters(self.admin_conn)
 
-    def create_instance(self, fqdn, realm_name, generate_master_key=True):
+    def create_instance(self, fqdn, realm_name, generate_master_key=True,
+                        kasp_db_file=None):
         self.backup_state("enabled", self.is_enabled())
         self.backup_state("running", self.is_running())
         self.fqdn = fqdn
         self.realm = realm_name
         self.suffix = ipautil.realm_to_suffix(self.realm)
+        self.kasp_db_file = kasp_db_file
 
         try:
             self.stop()
@@ -152,6 +155,21 @@ class OpenDNSSECInstance(service.Service):
         except errors.DuplicateEntry:
             root_logger.error("DNSSEC service already exists")
 
+        # add the KEYMASTER identifier into ipaConfigString
+        # this is needed for the re-enabled DNSSEC master
+        dn = DN(('cn', 'DNSSEC'), ('cn', self.fqdn), api.env.container_masters,
+                api.env.basedn)
+        try:
+            entry = self.admin_conn.get_entry(dn, ['ipaConfigString'])
+        except errors.NotFound as e:
+            root_logger.error(
+                "DNSSEC service entry not found in the LDAP (%s)", e)
+        else:
+            config = entry.setdefault('ipaConfigString', [])
+            if KEYMASTER not in config:
+                config.append(KEYMASTER)
+                self.admin_conn.update_entry(entry)
+
     def __setup_conf_files(self):
         if not self.fstore.has_file(paths.OPENDNSSEC_CONF_FILE):
             self.fstore.backup_file(paths.OPENDNSSEC_CONF_FILE)
@@ -250,7 +268,7 @@ class OpenDNSSECInstance(service.Service):
 
     def __setup_dnssec(self):
         # run once only
-        if self.get_state("KASP_DB_configured"):
+        if self.get_state("KASP_DB_configured") and not self.kasp_db_file:
             root_logger.debug("Already configured, skipping step")
             return
 
@@ -259,13 +277,21 @@ class OpenDNSSECInstance(service.Service):
         if not self.fstore.has_file(paths.OPENDNSSEC_KASP_DB):
             self.fstore.backup_file(paths.OPENDNSSEC_KASP_DB)
 
-        command = [
-            paths.ODS_KSMUTIL,
-            'setup'
-        ]
+        if self.kasp_db_file:
+            # copy user specified kasp.db to proper location and set proper
+            # privileges
+            shutil.copy(self.kasp_db_file, paths.OPENDNSSEC_KASP_DB)
+            os.chown(paths.OPENDNSSEC_KASP_DB, self.ods_uid, self.ods_gid)
+            os.chmod(paths.OPENDNSSEC_KASP_DB, 0660)
+        else:
+            # initialize new kasp.db
+            command = [
+                paths.ODS_KSMUTIL,
+                'setup'
+            ]
 
-        ods_enforcerd = services.knownservices.ods_enforcerd
-        ipautil.run(command, stdin="y", runas=ods_enforcerd.get_user_name())
+            ods_enforcerd = services.knownservices.ods_enforcerd
+            ipautil.run(command, stdin="y", runas=ods_enforcerd.get_user_name())
 
     def __setup_dnskeysyncd(self):
         # set up dnskeysyncd this is DNSSEC master
@@ -286,6 +312,29 @@ class OpenDNSSECInstance(service.Service):
         running = self.restore_state("running")
         enabled = self.restore_state("enabled")
 
+        # stop DNSSEC services before backing up kasp.db
+        try:
+            self.stop()
+        except Exception:
+            pass
+
+        ods_exporter = services.service('ipa-ods-exporter')
+        try:
+            ods_exporter.stop()
+        except Exception:
+            pass
+
+        if ipautil.file_exists(paths.OPENDNSSEC_KASP_DB):
+            try:
+                shutil.copy(paths.OPENDNSSEC_KASP_DB,
+                            paths.ROOT_IPA_KASP_DB_BACKUP)
+            except IOError as e:
+                root_logger.error(
+                    "Unable to backup OpenDNSSEC database: %s", e)
+            else:
+                root_logger.info("OpenDNSSEC database backed up in %s",
+                                 paths.ROOT_IPA_KASP_DB_BACKUP)
+
         for f in [paths.OPENDNSSEC_CONF_FILE, paths.OPENDNSSEC_KASP_FILE,
                   paths.OPENDNSSEC_KASP_DB, paths.SYSCONFIG_ODS]:
             try:
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index bde34851871f9b9f2bdf6dfecfd428755cc42e31..12a6a636b45a54577f1ca63f1ba99ca0f39590e7 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -1330,6 +1330,26 @@ class ServerDNS(common.Installable, core.Group, core.Composite):
         description="Setup server to be DNSSEC key master",
     )
 
+    disable_dnssec_master = Knob(
+        bool, False,
+        initializable=False,
+        description="Disable the DNSSEC master on this server",
+    )
+
+    replace_dnssec_master = Knob(
+        str, None,
+        initializable=False,
+        description="Replace the current DNSSEC master with the specified IPA "
+                    "server",
+    )
+
+    kasp_db_file = Knob(
+        str, None,
+        initializable=False,
+        description="Copy OpenDNSSEC metadata from the specified file (will "
+                    "not create a new kasp.db file)",
+    )
+
     zonemgr = Knob(
         str, None,
         description=("DNS zone manager e-mail address. Defaults to "
@@ -1626,6 +1646,9 @@ class Server(common.Installable, common.Interactive, core.Composite):
         self.no_reverse = self.dns.no_reverse
         self.no_dnssec_validation = self.dns.no_dnssec_validation
         self.dnssec_master = self.dns.dnssec_master
+        self.disable_dnssec_master = self.dns.disable_dnssec_master
+        self.replace_dnssec_master = self.dns.replace_dnssec_master
+        self.kasp_db_file = self.dns.kasp_db_file
         self.zonemgr = self.dns.zonemgr
         self.no_host_dns = self.dns.no_host_dns
         self.no_dns_sshfp = self.dns.no_dns_sshfp
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index 34580ce198b40f922ea984c1eea2dcd0c3aebb08..8bca410f4add3b45a747768bc90fbc24980a9ac7 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -688,6 +688,32 @@ class ReplicaDNS(common.Installable, core.Group, core.Composite):
         description="Disable DNSSEC validation",
     )
 
+    dnssec_master = Knob(
+        bool, False,
+        initializable=False,
+        description="Setup server to be DNSSEC key master",
+    )
+
+    disable_dnssec_master = Knob(
+        bool, False,
+        initializable=False,
+        description="Disable the DNSSEC master on this server",
+    )
+
+    replace_dnssec_master = Knob(
+        str, None,
+        initializable=False,
+        description="Replace the current DNSSEC master with the specified IPA "
+                    "server",
+    )
+
+    kasp_db_file = Knob(
+        str, None,
+        initializable=False,
+        description="Copy OpenDNSSEC metadata from the specified file (will "
+                    "not create a new kasp.db file)",
+    )
+
     no_host_dns = Knob(
         bool, False,
         description="Do not use DNS for hostname lookup during installation",
@@ -844,7 +870,10 @@ class Replica(common.Installable, common.Interactive, core.Composite):
         self.reverse_zones = self.dns.reverse_zones
         self.no_reverse = self.dns.no_reverse
         self.no_dnssec_validation = self.dns.no_dnssec_validation
-        self.dnssec_master = False
+        self.dnssec_master = self.dns.dnssec_master
+        self.disable_dnssec_master = self.dns.disable_dnssec_master
+        self.replace_dnssec_master = self.dns.replace_dnssec_master
+        self.kasp_db_file = self.dns.kasp_db_file
         self.zonemgr = None
         self.no_host_dns = self.dns.no_host_dns
         self.no_dns_sshfp = self.dns.no_dns_sshfp
-- 
2.1.0

From 3c4f7f48d6b5db4d69ab3c9b1ef8c508238e64ed Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 20 May 2015 17:49:08 +0200
Subject: [PATCH 2/2] DNSSEC: update message

https://fedorahosted.org/freeipa/ticket/4657
---
 install/tools/ipa-replica-manage | 1 +
 1 file changed, 1 insertion(+)

diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index 0d2688e6d73b1591c5e386656b7198c20d71558a..46a13b559dfead902d464d597b75ddf604e6bf19 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -701,6 +701,7 @@ def del_master(realm, hostname, options):
             dnssec_masters = opendnssecinstance.get_dnssec_key_masters(delrepl.conn)
             if hostname in dnssec_masters:
                 print "Replica is active DNSSEC key master. Uninstall could break your DNS system."
+                print "Please disable or replace DNSSEC key master first."
                 sys.exit("Deletion aborted")
 
         # Pick CA renewal master
-- 
2.1.0

-- 
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