On 29/06/15 15:16, Martin Basti wrote:
On 25/06/15 13:46, Petr Spacek wrote:
On 17.6.2015 13:37, Martin Basti wrote:
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.
NACK.


0) ipa-dns-install --replace-dnssec-master always puts file into
/root/ipa-kasp.db.

It would be better to put it into local working directory or /var/lib/ipa (as
with replica files).


1) I installed DNSSEC key master role on the vm-134 but DNSSEC services were
not stopped by ipactl stop:

[root@vm-134 review]# ipactl stop
Stopping ipa-otpd Service
Stopping httpd Service
Stopping ipa_memcached Service
Stopping kadmin Service
Stopping krb5kdc Service
Stopping Directory Service
ipa: INFO: The ipactl command was successful

[root@vm-134 review]# ipactl start
Starting Directory Service
Starting krb5kdc Service
Starting kadmin Service
Starting named Service
Starting ipa_memcached Service
Starting httpd Service
Starting ipa-otpd Service
Starting ipa-ods-exporter Service
Starting ods-enforcerd Service
Starting ipa-dnskeysyncd Service

Subsequent ipactl stop worked fine, only the first one is affected.


2a) vm-134 was the original master. I ran this:

[root@vm-134 review]# ipa-dns-install
--replace-dnssec-master=vm-090.abc.idm.lab.eng.brq.redhat.com

... and then attempted to install master to vm-059:
[root@vm-059 review]# ipa-dns-install --dnssec-master

This command was accepted despite of missing --kasp-db option and wrong
replica name.

It should error out and tell the user to run the command with --kasp-db option.

Even better, we could get rid of explicit replica name specification in
--replace-dnssec-master option and allow to run installation with --kasp-db on
any replica as long as the kasp.db file is provided.



2b) Attempt to move DNSSEC key master from vm-134 to vm-090 *without*
specifying --kasp-db option was accepted.

[root@vm-090 review]# ipa-dns-install --dnssec-master

As in case (2a), it should print what user is supposed to do.

I propose following text:

Current DNSSEC key master <vm-134.abc.idm.lab.eng.brq.redhat.com> is being
moved to different server.

You need to copy kasp.db file from <vm-134.abc.idm.lab.eng.brq.redhat.com> and
run following command to complete the transition:

# ipa-dns-install --dnssec-master --kasp-db=/path/to/the/copied/kasp.db



3) [root@vm-134 review]# ipa-dns-install
--replace-dnssec-master=vm-090.abc.idm.lab.eng.brq.redhat.com
does not remove ISMASTER option from file /etc/sysconfig/ipa-dnskeysyncd .


4) [root@vm-134 review]# ipa-dns-install
--replace-dnssec-master=vm-090.abc.idm.lab.eng.brq.redhat.com

it is possible to run

[root@vm-134 review]# ipa-dns-install --dnssec-master

again without --kasp-db and it is accepted.

Moreover, in this case ipaConfigString "NEW_DNSSEC_MASTER" is not properly
removed from
cn=DNSKeySync,cn=vm-090.abc.idm.lab.eng.brq.redhat.com,cn=masters,cn=ipa,cn=etc,dc=ipa,dc=example.



5) Sequence of commands
[root@vm-134 review]# ipa-dns-install
--replace-dnssec-master=vm-090.abc.idm.lab.eng.brq.redhat.com

[root@vm-090 review]# ipa-replica-manage del vm-134.abc.idm.lab.eng.brq.redhat.com

allows me to run
[root@vm-090 review]# ipa-dns-install --dnssec-master

without --kasp-db option, it does not throw an error, and the information that
some other master existed somewhere is lost.

It would be probably better to replace this and to use some global attribute
in cn=dns so similar problems do not happen.



6) The migration itself seems to work, KASP DB seems to work properly, however it is necessary to run 'ods-ksmutil zonelist' command *before* all the daemons on the new master are (re)started. This needs do be done to re-generate file
/etc/opendnssec/zonelist.xml from the new (copied) DB.

Here please be careful about file permissions.

The command should be ran under 'ods' user to avoid permission clobbering.


Thank you for your hard work on this!

New patches attached.

Major part of the code was changed.

Please apply patch 268 first.





Updated patches attached.

I just changed the error log to debug log
                 ipautil.run(cmd, runas=ods_enforcerd.get_user_name())
-            except CalledProcessError as e:
-                root_logger.error("%s", e)
+            except CalledProcessError:
+ root_logger.debug("OpenDNSSEC database has not been updated")

As this is not error during uninstall.

--
Martin Basti

From 522b55101c18ebf74632514c9c85cf5bd3c7e745 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
--dnssec-master --kasp-db=FILE  This configure new DNSSEC master server,  kasp.db from old server is required for sucessful replacement
--force Skip checks

https://fedorahosted.org/freeipa/ticket/4657
---
 install/tools/ipa-dns-install              |  12 +++
 ipaplatform/base/paths.py                  |   1 +
 ipaserver/install/dns.py                   | 153 +++++++++++++++++++++++++++--
 ipaserver/install/odsexporterinstance.py   |  12 ++-
 ipaserver/install/opendnssecinstance.py    |  91 +++++++++++++++--
 ipaserver/install/server/install.py        |  23 ++++-
 ipaserver/install/server/replicainstall.py |  30 +++++-
 7 files changed, 300 insertions(+), 22 deletions(-)

diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index fd9311657e813988310db2be604ca68d26936af5..d827992010685936d64b7f03f0980ef39e94e0b0 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -61,6 +61,15 @@ 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("--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)")
+    parser.add_option("--force", dest="force", action="store_true",
+                      help="Force install")
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -74,6 +83,9 @@ def parse_options():
         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 e847f93b543d187f9ec72788e17f67e7afe90c07..200a79075076b232d6711cf4719baf13b7a16c09 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -276,6 +276,7 @@ class BasePathNamespace(object):
     SYSRESTORE_INDEX = "/var/lib/ipa-client/sysrestore/sysrestore.index"
     IPA_BACKUP_DIR = "/var/lib/ipa/backup"
     IPA_DNSSEC_DIR = "/var/lib/ipa/dnssec"
+    IPA_KASP_DB_BACKUP = "/var/lib/ipa/ipa-kasp.db.backup"
     DNSSEC_TOKENS_DIR = "/var/lib/ipa/dnssec/tokens"
     DNSSEC_SOFTHSM_PIN = "/var/lib/ipa/dnssec/softhsm_pin"
     IPA_CA_CSR = "/var/lib/ipa/ca.csr"
diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
index 8d9570d68c5c84116839371f3ce8e73d71da2db5..d0804c0531f95ce533c18778f6eb42b228e25c54 100644
--- a/ipaserver/install/dns.py
+++ b/ipaserver/install/dns.py
@@ -4,10 +4,15 @@
 
 import sys
 
+from subprocess import CalledProcessError
+
 from ipalib import api
+from ipalib import errors
 from ipaplatform.paths import paths
+from ipaplatform import services
 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 +28,67 @@ 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
+
+    # unconfigure services first
+    ods.uninstall()  # needs keytab to flush the latest ods database
+    ods_exporter.uninstall()
+
+    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.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 install_check(standalone, replica, options, hostname):
     global ip_addresses
@@ -41,14 +107,22 @@ 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."
+            print ""
+            print ("Please copy file from %s after uninstallation. This file is needed "
+                   "on new DNSSEC key " % paths.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 +133,78 @@ 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")
 
     # 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:
+        _is_master()
+
+    dnssec_zones = _find_dnssec_enabled_zones(api.Backend.ldap2)
+
+    if options.disable_dnssec_master:
+        if dnssec_zones and not options.force:
+            raise RuntimeError(
+                "Cannot disable DNSSEC key master, DNSSEC signing is still "
+                "enabled for following zone(s): %s\n"
+                "Use --force option to skip this check." %
+                ", ".join([str(zone) for zone in dnssec_zones]))
+    elif options.dnssec_master:
         # check opendnssec packages are installed
         if not opendnssecinstance.check_inst():
             sys.exit("Aborting installation")
+        if options.kasp_db_file:
+            dnskeysyncd = services.service('ipa-dnskeysyncd')
+
+            if not dnskeysyncd.is_installed():
+                raise RuntimeError("ipa-dnskeysyncd is not configured on this "
+                                   "server, you cannot reuse OpenDNSSEC "
+                                   "database (kasp.db file)")
+
+            # check if replica can be the DNSSEC master
+            named = services.knownservices.named
+            ods_enforcerd = services.knownservices.ods_enforcerd
+            cmd = [paths.IPA_DNSKEYSYNCD_REPLICA]
+            environment = {
+                "SOFTHSM2_CONF": paths.DNSSEC_SOFTHSM2_CONF,
+            }
+
+            # stop dnskeysyncd before test
+            dnskeysyncd_running = dnskeysyncd.is_running()
+            dnskeysyncd.stop()
+            try:
+                ipautil.run(cmd, env=environment,
+                            runas=ods_enforcerd.get_user_name(),
+                            suplementary_groups=[named.get_group_name()])
+            except CalledProcessError as e:
+                root_logger.debug("%s", e)
+                raise RuntimeError("IPA server cannot be the new DNSSEC master "
+                                   "(some keys are missing)")
+            finally:
+                if dnskeysyncd_running:
+                    dnskeysyncd.start()
+        elif dnssec_zones and not options.force:
+            # some zones have --dnssec=true, make sure a user really want to
+            # install new database
+            raise RuntimeError(
+                "DNSSEC is enabled for following zone(s): %s\n"
+                "Please use option --kasp-db to keep current OpenDNSSEC "
+                "database or use --force option to skip this check." %
+                ", ".join([str(zone) for zone in dnssec_zones]))
+
 
     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 +256,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 +284,15 @@ 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)
+    elif options.disable_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..dea6c654abd8017c2c5218ba48b74cde0768dea1 100644
--- a/ipaserver/install/opendnssecinstance.py
+++ b/ipaserver/install/opendnssecinstance.py
@@ -9,6 +9,9 @@ import os
 import pwd
 import grp
 import stat
+import shutil
+
+from subprocess import CalledProcessError
 
 import _ipap11helper
 
@@ -31,7 +34,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 +65,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 +97,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 +157,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 +270,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 +279,26 @@ 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)
 
-        ods_enforcerd = services.knownservices.ods_enforcerd
-        ipautil.run(command, stdin="y", runas=ods_enforcerd.get_user_name())
+            # regenerate zonelist.xml
+            ods_enforcerd = services.knownservices.ods_enforcerd
+            cmd = ['ods-ksmutil', 'zonelist']
+            ipautil.run(cmd, runas=ods_enforcerd.get_user_name())
+        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())
 
     def __setup_dnskeysyncd(self):
         # set up dnskeysyncd this is DNSSEC master
@@ -286,6 +319,44 @@ 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
+
+        # remove directive from ipa-dnskeysyncd, this server is not DNSSEC
+        # master anymore
+        installutils.set_directive(paths.SYSCONFIG_IPA_DNSKEYSYNCD,
+                                   'ISMASTER', None,
+                                   quotes=False, separator='=')
+
+        if ipautil.file_exists(paths.OPENDNSSEC_KASP_DB):
+
+            # force to export data
+            ods_enforcerd = services.knownservices.ods_enforcerd
+            cmd = [paths.IPA_ODS_EXPORTER, 'ipa-full-update']
+            try:
+                ipautil.run(cmd, runas=ods_enforcerd.get_user_name())
+            except CalledProcessError:
+                root_logger.debug("OpenDNSSEC database has not been updated")
+
+            try:
+                shutil.copy(paths.OPENDNSSEC_KASP_DB,
+                            paths.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.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..ad7243b8418c3eaaa6d977e5a056eb748a1bc2d6 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -1330,6 +1330,25 @@ 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",
+    )
+
+    kasp_db_file = Knob(
+        str, None,
+        initializable=False,
+        description="Copy OpenDNSSEC metadata from the specified file (will "
+                    "not create a new kasp.db file)",
+    )
+
+    force = Knob(
+        bool, False,
+        initializable=False,
+        description="Force install",
+    )
+
     zonemgr = Knob(
         str, None,
         description=("DNS zone manager e-mail address. Defaults to "
@@ -1618,7 +1637,6 @@ class Server(common.Installable, common.Interactive, core.Composite):
         self.ca_cert_files = self.ca.ca_cert_files
         self.subject = self.ca.subject
         self.ca_signing_algorithm = self.ca.ca_signing_algorithm
-
         self.setup_dns = self.dns.setup_dns
         self.forwarders = self.dns.forwarders
         self.no_forwarders = self.dns.no_forwarders
@@ -1626,6 +1644,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.kasp_db_file = self.dns.kasp_db_file
+        self.force = self.dns.force
         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 54d1ede1340afddeca6e1de453aadd166fb19444..4fe7e1f78388901b3cc4e9dc0c2046a193dcfa94 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -699,6 +699,31 @@ 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",
+    )
+
+    force = Knob(
+        bool, False,
+        initializable=False,
+        description="Force install",
+    )
+
+    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",
@@ -847,7 +872,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.kasp_db_file = self.dns.kasp_db_file
+        self.force = self.dns.force
         self.zonemgr = None
         self.no_host_dns = self.dns.no_host_dns
         self.no_dns_sshfp = self.dns.no_dns_sshfp
-- 
2.4.3

From 2d6b76faaa00ddff0ebd98252c93c27e99b03669 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.4.3

From 2f5c1ed3cc54f1954000d96322a1c35217b2d260 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 26 Jun 2015 12:44:20 +0200
Subject: [PATCH] Allow to run subprocess with suplementary groups

Param suplementary_groups allows to specify list of group names to be
used for subprocess.
suplementary_groups param requires runas param to be specified.

Required for ticket: https://fedorahosted.org/freeipa/ticket/4657
---
 ipapython/ipautil.py | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index f0c9c6b5ab1ac3f2a66b3467e833dcc21bc98a49..88e89706b8e2aa6dea80809510d88bceaa836e85 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -36,6 +36,7 @@ import netaddr
 import time
 import krbV
 import pwd
+import grp
 from dns import resolver, rdatatype
 from dns.exception import DNSException
 from contextlib import contextmanager
@@ -250,7 +251,7 @@ def shell_quote(string):
 
 def run(args, stdin=None, raiseonerr=True,
         nolog=(), env=None, capture_output=True, skip_output=False, cwd=None,
-        runas=None, timeout=None):
+        runas=None, timeout=None, suplementary_groups=[]):
     """
     Execute a command and return stdin, stdout and the process return code.
 
@@ -276,11 +277,15 @@ def run(args, stdin=None, raiseonerr=True,
     :param capture_output: Capture stderr and stdout
     :param skip_output: Redirect the output to /dev/null and do not capture it
     :param cwd: Current working directory
-    :param runas: Name of a user that the command shold be run as. The spawned
+    :param runas: Name of a user that the command should be run as. The spawned
         process will have both real and effective UID and GID set.
     :param timeout: Timeout if the command hasn't returned within the specified
         number of seconds.
+    :param suplementary_groups: List of group names that will be used as
+        suplementary groups for subporcess.
+        The option runas must be specified together with this option.
     """
+    assert isinstance(suplementary_groups, list)
     p_in = None
     p_out = None
     p_err = None
@@ -317,11 +322,22 @@ def run(args, stdin=None, raiseonerr=True,
     preexec_fn = None
     if runas is not None:
         pent = pwd.getpwnam(runas)
+
+        suplementary_gids = [
+            grp.getgrnam(group).gr_gid for group in suplementary_groups
+        ]
+
         root_logger.debug('runas=%s (UID %d, GID %s)', runas,
             pent.pw_uid, pent.pw_gid)
+        if suplementary_groups:
+            for group, gid in zip(suplementary_groups, suplementary_gids):
+                root_logger.debug('suplementary_group=%s (GID %d)', group, gid)
 
-        preexec_fn = lambda: (os.setregid(pent.pw_gid, pent.pw_gid),
-                              os.setreuid(pent.pw_uid, pent.pw_uid))
+        preexec_fn = lambda: (
+            os.setgroups(suplementary_gids),
+            os.setregid(pent.pw_gid, pent.pw_gid),
+            os.setreuid(pent.pw_uid, pent.pw_uid),
+        )
 
     try:
         p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err,
-- 
2.4.3

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