Blob Blame History Raw
From ec58278fa7fc6d0b5ae8340c4054005a7864086f Mon Sep 17 00:00:00 2001
From: Christian Heimes <cheimes@redhat.com>
Date: Tue, 26 Mar 2019 13:10:23 +0100
Subject: [PATCH] Don't allow to hide last server for a role

DNSSec key master and CA renewal master can't be hidden. There must be
at least one enabled server available for each role, too.

Fixes: https://pagure.io/freeipa/issue/7892
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Thomas Woerner <twoerner@redhat.com>
Reviewed-By: Francois Cami <fcami@redhat.com>
---
 ipaserver/plugins/server.py                   | 30 ++++++++++++++++++
 .../test_replica_promotion.py                 | 31 +++++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
index 0d144d13bca66b65de64328139fd7126eea24c89..bfd1406ff826aea97195aa08ca35018e35cac18c 100644
--- a/ipaserver/plugins/server.py
+++ b/ipaserver/plugins/server.py
@@ -970,6 +970,35 @@ class server_state(crud.PKQuery):
 
     has_output = output.standard_boolean
 
+    def _check_hide_server(self, fqdn):
+        result = self.api.Command.config_show()['result']
+        err = []
+        # single value entries
+        if result.get("ca_renewal_master_server") == fqdn:
+            err.append(_("Cannot hide CA renewal master."))
+        if result.get("dnssec_key_master_server") == fqdn:
+            err.append(_("Cannot hide DNSSec key master."))
+        # multi value entries, only fail if we are the last one
+        checks = [
+            ("ca_server_server", "CA"),
+            ("dns_server_server", "DNS"),
+            ("ipa_master_server", "IPA"),
+            ("kra_server_server", "KRA"),
+        ]
+        for key, name in checks:
+            values = result.get(key, [])
+            if values == [fqdn]:  # fqdn is the only entry
+                err.append(
+                    _("Cannot hide last enabled %(name)s server.") % {
+                        'name': name
+                    }
+                )
+        if err:
+            raise errors.ValidationError(
+                name=fqdn,
+                error=' '.join(str(e) for e in err)
+            )
+
     def execute(self, *keys, **options):
         fqdn = keys[0]
         if options['state'] == u'enabled':
@@ -992,6 +1021,7 @@ class server_state(crud.PKQuery):
         if to_status == ENABLED:
             enable_services(fqdn)
         else:
+            self._check_hide_server(fqdn)
             hide_services(fqdn)
 
         # update system roles
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
index 9ce0074ea0df276b4800b306530d04c8274ece69..bf028bf7dc58abb6455ba1659f2d19bede69daa2 100644
--- a/ipatests/test_integration/test_replica_promotion.py
+++ b/ipatests/test_integration/test_replica_promotion.py
@@ -812,7 +812,15 @@ class TestHiddenReplicaPromotion(IntegrationTest):
 
     @classmethod
     def install(cls, mh):
+        # master with DNSSEC master
         tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
+        cls.master.run_command([
+            "ipa-dns-install",
+            "--dnssec-master",
+            "--forwarder", cls.master.config.dns_forwarder,
+            "-U",
+        ])
+        # hidden replica with CA and DNS
         tasks.install_replica(
             cls.master, cls.replicas[0],
             setup_dns=True, setup_kra=True,
@@ -879,6 +887,29 @@ class TestHiddenReplicaPromotion(IntegrationTest):
         self._check_dnsrecords([self.master], [self.replicas[0]])
         self._check_config([self.master], [self.replicas[0]])
 
+    def test_hide_master_fails(self):
+        # verify state
+        self._check_config([self.master], [self.replicas[0]])
+        # nothing to do
+        result = self.master.run_command([
+            'ipa', 'server-state',
+            self.master.hostname, '--state=enabled'
+        ], raiseonerr=False)
+        assert result.returncode == 1
+        assert "no modifications to be performed" in result.stderr_text
+        # hiding the last master fails
+        result = self.master.run_command([
+            'ipa', 'server-state',
+            self.master.hostname, '--state=hidden'
+        ], raiseonerr=False)
+        assert result.returncode == 1
+        keys = [
+            "CA renewal master", "DNSSec key master", "CA server",
+            "KRA server", "DNS server", "IPA server"
+        ]
+        for key in keys:
+            assert key in result.stderr_text
+
     def test_hidden_replica_promote(self):
         self.replicas[0].run_command([
             'ipa', 'server-state',
-- 
2.20.1