On 17/06/15 13:26, Petr Spacek wrote:
On 16.6.2015 15:40, Martin Basti wrote:
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.
Sorry, NACK, I'm not able to apply this patch set to current master
(69607250b9762a6c9b657dd31653b03d54a7b411).

Rebased patches attached.

--
Martin Basti

From 5f19362a7aa18e0a19f68712888849e4a40d6052 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 85246f0fd5725c10d446af026094352bc383caec Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 17 Jun 2015 13:35:18 +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 57e30bc54ae030a4620660d1fa7539626721ebbd..b178c2f0b1b08c4a27175d4d8347c48ea3f12f23 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -623,6 +623,7 @@ def ensure_last_services(conn, hostname, masters, options):
         dnssec_masters = opendnssecinstance.get_dnssec_key_masters(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")
 
     ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
-- 
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