|
|
86baa9 |
From 04f68b7354cf1268019b08885eeb3a6915f314ef Mon Sep 17 00:00:00 2001
|
|
|
86baa9 |
From: Christian Heimes <cheimes@redhat.com>
|
|
|
86baa9 |
Date: Thu, 12 Jul 2018 14:37:18 +0200
|
|
|
86baa9 |
Subject: [PATCH] Unify and simplify LDAP service discovery
|
|
|
86baa9 |
|
|
|
86baa9 |
Move LDAP service discovery and service definitions from
|
|
|
86baa9 |
ipaserver.install to ipaserver. Simplify and unify different
|
|
|
86baa9 |
implementations in favor of a single implementation.
|
|
|
86baa9 |
|
|
|
86baa9 |
Signed-off-by: Christian Heimes <cheimes@redhat.com>
|
|
|
86baa9 |
Reviewed-By: Thomas Woerner <twoerner@redhat.com>
|
|
|
86baa9 |
---
|
|
|
86baa9 |
install/tools/ipa-ca-install | 9 +-
|
|
|
86baa9 |
install/tools/ipactl | 4 +-
|
|
|
86baa9 |
ipaserver/install/cainstance.py | 3 +-
|
|
|
86baa9 |
ipaserver/install/ipa_kra_install.py | 11 +-
|
|
|
86baa9 |
ipaserver/install/opendnssecinstance.py | 3 +-
|
|
|
86baa9 |
ipaserver/install/server/replicainstall.py | 18 +--
|
|
|
86baa9 |
ipaserver/install/service.py | 65 +----------
|
|
|
86baa9 |
ipaserver/masters.py | 123 ++++++++++++++++++++
|
|
|
86baa9 |
ipaserver/plugins/cert.py | 10 +-
|
|
|
86baa9 |
ipaserver/plugins/dogtag.py | 99 ++++------------
|
|
|
86baa9 |
ipaserver/servroles.py | 9 +-
|
|
|
86baa9 |
ipatests/test_ipaserver/test_serverroles.py | 3 +-
|
|
|
86baa9 |
12 files changed, 192 insertions(+), 165 deletions(-)
|
|
|
86baa9 |
create mode 100644 ipaserver/masters.py
|
|
|
86baa9 |
|
|
|
86baa9 |
diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
|
|
|
86baa9 |
index f78f43d94981d29939a247f3c492c5e7340298ea..dcdbe884f15b13b92ec68a11d9f00e3e28771b42 100755
|
|
|
86baa9 |
--- a/install/tools/ipa-ca-install
|
|
|
86baa9 |
+++ b/install/tools/ipa-ca-install
|
|
|
86baa9 |
@@ -34,6 +34,7 @@ from ipaserver.install.installutils import check_creds, ReplicaConfig
|
|
|
86baa9 |
from ipaserver.install import dsinstance, ca
|
|
|
86baa9 |
from ipaserver.install import cainstance, service
|
|
|
86baa9 |
from ipaserver.install import custodiainstance
|
|
|
86baa9 |
+from ipaserver.masters import find_providing_server
|
|
|
86baa9 |
from ipapython import version
|
|
|
86baa9 |
from ipalib import api
|
|
|
86baa9 |
from ipalib.constants import DOMAIN_LEVEL_0
|
|
|
86baa9 |
@@ -211,8 +212,9 @@ def install_replica(safe_options, options, filename):
|
|
|
86baa9 |
config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
|
|
|
86baa9 |
|
|
|
86baa9 |
if config.ca_host_name is None:
|
|
|
86baa9 |
- config.ca_host_name = \
|
|
|
86baa9 |
- service.find_providing_server('CA', api.Backend.ldap2, api.env.ca_host)
|
|
|
86baa9 |
+ config.ca_host_name = find_providing_server(
|
|
|
86baa9 |
+ 'CA', api.Backend.ldap2, [api.env.ca_host]
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
|
|
|
86baa9 |
options.realm_name = config.realm_name
|
|
|
86baa9 |
options.domain_name = config.domain_name
|
|
|
86baa9 |
@@ -299,7 +301,8 @@ def promote(safe_options, options, filename):
|
|
|
86baa9 |
paths.KRB5_KEYTAB,
|
|
|
86baa9 |
ccache)
|
|
|
86baa9 |
|
|
|
86baa9 |
- ca_host = service.find_providing_server('CA', api.Backend.ldap2)
|
|
|
86baa9 |
+ ca_host = find_providing_server('CA', api.Backend.ldap2)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
if ca_host is None:
|
|
|
86baa9 |
install_master(safe_options, options)
|
|
|
86baa9 |
else:
|
|
|
86baa9 |
diff --git a/install/tools/ipactl b/install/tools/ipactl
|
|
|
86baa9 |
index ade91f7f75dab4fdf3b7fa73d2624a7395bc2a53..2767a26d1b70337d37dbcd87c707919579fe7e29 100755
|
|
|
86baa9 |
--- a/install/tools/ipactl
|
|
|
86baa9 |
+++ b/install/tools/ipactl
|
|
|
86baa9 |
@@ -224,9 +224,9 @@ def get_config(dirsrv):
|
|
|
86baa9 |
svc_list.append([order, name])
|
|
|
86baa9 |
|
|
|
86baa9 |
ordered_list = []
|
|
|
86baa9 |
- for (order, svc) in sorted(svc_list):
|
|
|
86baa9 |
+ for order, svc in sorted(svc_list):
|
|
|
86baa9 |
if svc in service.SERVICE_LIST:
|
|
|
86baa9 |
- ordered_list.append(service.SERVICE_LIST[svc][0])
|
|
|
86baa9 |
+ ordered_list.append(service.SERVICE_LIST[svc].systemd_name)
|
|
|
86baa9 |
return ordered_list
|
|
|
86baa9 |
|
|
|
86baa9 |
def get_config_from_file():
|
|
|
86baa9 |
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
|
|
86baa9 |
index e101087ac2d738eb95cc643bfb12faaf5e65f0be..f424e7cd76d24a5a633a4f4babf3e112537be92c 100644
|
|
|
86baa9 |
--- a/ipaserver/install/cainstance.py
|
|
|
86baa9 |
+++ b/ipaserver/install/cainstance.py
|
|
|
86baa9 |
@@ -71,6 +71,7 @@ from ipaserver.install import replication
|
|
|
86baa9 |
from ipaserver.install import sysupgrade
|
|
|
86baa9 |
from ipaserver.install.dogtaginstance import DogtagInstance
|
|
|
86baa9 |
from ipaserver.plugins import ldap2
|
|
|
86baa9 |
+from ipaserver.masters import ENABLED_SERVICE
|
|
|
86baa9 |
|
|
|
86baa9 |
logger = logging.getLogger(__name__)
|
|
|
86baa9 |
|
|
|
86baa9 |
@@ -1304,7 +1305,7 @@ class CAInstance(DogtagInstance):
|
|
|
86baa9 |
config = ['caRenewalMaster']
|
|
|
86baa9 |
else:
|
|
|
86baa9 |
config = []
|
|
|
86baa9 |
- self._ldap_enable(u'enabledService', "CA", self.fqdn, basedn, config)
|
|
|
86baa9 |
+ self._ldap_enable(ENABLED_SERVICE, "CA", self.fqdn, basedn, config)
|
|
|
86baa9 |
|
|
|
86baa9 |
def setup_lightweight_ca_key_retrieval(self):
|
|
|
86baa9 |
if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
|
|
|
86baa9 |
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
|
|
|
86baa9 |
index b536685f5f1f3fccab07fd37aa001958e2d38420..19260ac7f23a7c6f3a6328d4f146510a186b706e 100644
|
|
|
86baa9 |
--- a/ipaserver/install/ipa_kra_install.py
|
|
|
86baa9 |
+++ b/ipaserver/install/ipa_kra_install.py
|
|
|
86baa9 |
@@ -41,6 +41,7 @@ from ipaserver.install.installutils import create_replica_config
|
|
|
86baa9 |
from ipaserver.install import dogtaginstance
|
|
|
86baa9 |
from ipaserver.install import kra
|
|
|
86baa9 |
from ipaserver.install.installutils import ReplicaConfig
|
|
|
86baa9 |
+from ipaserver.masters import find_providing_server
|
|
|
86baa9 |
|
|
|
86baa9 |
logger = logging.getLogger(__name__)
|
|
|
86baa9 |
|
|
|
86baa9 |
@@ -206,8 +207,14 @@ class KRAInstaller(KRAInstall):
|
|
|
86baa9 |
config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
|
|
|
86baa9 |
|
|
|
86baa9 |
if config.kra_host_name is None:
|
|
|
86baa9 |
- config.kra_host_name = service.find_providing_server(
|
|
|
86baa9 |
- 'KRA', api.Backend.ldap2, api.env.ca_host)
|
|
|
86baa9 |
+ config.kra_host_name = find_providing_server(
|
|
|
86baa9 |
+ 'KRA', api.Backend.ldap2, [api.env.ca_host]
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ if config.kra_host_name is None:
|
|
|
86baa9 |
+ # all CA/KRA servers are down or unreachable.
|
|
|
86baa9 |
+ raise admintool.ScriptError(
|
|
|
86baa9 |
+ "Failed to find an active KRA server!"
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
custodia = custodiainstance.get_custodia_instance(
|
|
|
86baa9 |
config, custodiainstance.CustodiaModes.KRA_PEER)
|
|
|
86baa9 |
else:
|
|
|
86baa9 |
diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
|
|
|
86baa9 |
index 0337bb22fea44f95ee9077423136353a991325db..d6725dff11599a8755a29b6707dc7b451258629f 100644
|
|
|
86baa9 |
--- a/ipaserver/install/opendnssecinstance.py
|
|
|
86baa9 |
+++ b/ipaserver/install/opendnssecinstance.py
|
|
|
86baa9 |
@@ -15,6 +15,7 @@ from subprocess import CalledProcessError
|
|
|
86baa9 |
from ipalib.install import sysrestore
|
|
|
86baa9 |
from ipaserver.install import service
|
|
|
86baa9 |
from ipaserver.install import installutils
|
|
|
86baa9 |
+from ipaserver.masters import ENABLED_SERVICE
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
from ipapython import ipautil
|
|
|
86baa9 |
from ipaplatform import services
|
|
|
86baa9 |
@@ -45,7 +46,7 @@ def get_dnssec_key_masters(conn):
|
|
|
86baa9 |
filter_attrs = {
|
|
|
86baa9 |
u'cn': u'DNSSEC',
|
|
|
86baa9 |
u'objectclass': u'ipaConfigObject',
|
|
|
86baa9 |
- u'ipaConfigString': [KEYMASTER, u'enabledService'],
|
|
|
86baa9 |
+ u'ipaConfigString': [KEYMASTER, ENABLED_SERVICE],
|
|
|
86baa9 |
}
|
|
|
86baa9 |
only_masters_f = conn.make_filter(filter_attrs, rules=conn.MATCH_ALL)
|
|
|
86baa9 |
|
|
|
86baa9 |
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
|
|
86baa9 |
index b221e1291f973e7255263a39cfd680af1321598d..37ecbe4146fa908c30fb708037fcaa47af1a258b 100644
|
|
|
86baa9 |
--- a/ipaserver/install/server/replicainstall.py
|
|
|
86baa9 |
+++ b/ipaserver/install/server/replicainstall.py
|
|
|
86baa9 |
@@ -46,6 +46,7 @@ from ipaserver.install.installutils import (
|
|
|
86baa9 |
validate_mask)
|
|
|
86baa9 |
from ipaserver.install.replication import (
|
|
|
86baa9 |
ReplicationManager, replica_conn_check)
|
|
|
86baa9 |
+from ipaserver.masters import find_providing_servers, find_providing_server
|
|
|
86baa9 |
import SSSDConfig
|
|
|
86baa9 |
from subprocess import CalledProcessError
|
|
|
86baa9 |
|
|
|
86baa9 |
@@ -1257,9 +1258,10 @@ def promote_check(installer):
|
|
|
86baa9 |
if subject_base is not None:
|
|
|
86baa9 |
config.subject_base = DN(subject_base)
|
|
|
86baa9 |
|
|
|
86baa9 |
- # Find if any server has a CA
|
|
|
86baa9 |
- ca_host = service.find_providing_server(
|
|
|
86baa9 |
- 'CA', conn, config.ca_host_name)
|
|
|
86baa9 |
+ # Find any server with a CA
|
|
|
86baa9 |
+ ca_host = find_providing_server(
|
|
|
86baa9 |
+ 'CA', conn, [config.ca_host_name]
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
if ca_host is not None:
|
|
|
86baa9 |
config.ca_host_name = ca_host
|
|
|
86baa9 |
ca_enabled = True
|
|
|
86baa9 |
@@ -1280,14 +1282,16 @@ def promote_check(installer):
|
|
|
86baa9 |
"custom certificates.")
|
|
|
86baa9 |
raise ScriptError(rval=3)
|
|
|
86baa9 |
|
|
|
86baa9 |
- kra_host = service.find_providing_server(
|
|
|
86baa9 |
- 'KRA', conn, config.kra_host_name)
|
|
|
86baa9 |
+ # Find any server with a KRA
|
|
|
86baa9 |
+ kra_host = find_providing_server(
|
|
|
86baa9 |
+ 'KRA', conn, [config.kra_host_name]
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
if kra_host is not None:
|
|
|
86baa9 |
config.kra_host_name = kra_host
|
|
|
86baa9 |
kra_enabled = True
|
|
|
86baa9 |
else:
|
|
|
86baa9 |
if options.setup_kra:
|
|
|
86baa9 |
- logger.error("There is no KRA server in the domain, "
|
|
|
86baa9 |
+ logger.error("There is no active KRA server in the domain, "
|
|
|
86baa9 |
"can't setup a KRA clone")
|
|
|
86baa9 |
raise ScriptError(rval=3)
|
|
|
86baa9 |
kra_enabled = False
|
|
|
86baa9 |
@@ -1577,7 +1581,7 @@ def install(installer):
|
|
|
86baa9 |
# Enable configured services and update DNS SRV records
|
|
|
86baa9 |
service.enable_services(config.host_name)
|
|
|
86baa9 |
api.Command.dns_update_system_records()
|
|
|
86baa9 |
- ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
|
|
|
86baa9 |
+ ca_servers = find_providing_servers('CA', api.Backend.ldap2, api=api)
|
|
|
86baa9 |
api.Backend.ldap2.disconnect()
|
|
|
86baa9 |
|
|
|
86baa9 |
# Everything installed properly, activate ipa service.
|
|
|
86baa9 |
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
|
|
|
86baa9 |
index baefc0076990e67e56dfa68da48b56f4cd55849f..a030801175491f65dc83aa9d42afdb1dfdb65b0f 100644
|
|
|
86baa9 |
--- a/ipaserver/install/service.py
|
|
|
86baa9 |
+++ b/ipaserver/install/service.py
|
|
|
86baa9 |
@@ -38,34 +38,15 @@ from ipapython import kerberos
|
|
|
86baa9 |
from ipalib import api, errors
|
|
|
86baa9 |
from ipaplatform import services
|
|
|
86baa9 |
from ipaplatform.paths import paths
|
|
|
86baa9 |
+from ipaserver.masters import (
|
|
|
86baa9 |
+ CONFIGURED_SERVICE, ENABLED_SERVICE, SERVICE_LIST
|
|
|
86baa9 |
+)
|
|
|
86baa9 |
|
|
|
86baa9 |
logger = logging.getLogger(__name__)
|
|
|
86baa9 |
|
|
|
86baa9 |
if six.PY3:
|
|
|
86baa9 |
unicode = str
|
|
|
86baa9 |
|
|
|
86baa9 |
-# The service name as stored in cn=masters,cn=ipa,cn=etc. In the tuple
|
|
|
86baa9 |
-# the first value is the *nix service name, the second the start order.
|
|
|
86baa9 |
-SERVICE_LIST = {
|
|
|
86baa9 |
- 'KDC': ('krb5kdc', 10),
|
|
|
86baa9 |
- 'KPASSWD': ('kadmin', 20),
|
|
|
86baa9 |
- 'DNS': ('named', 30),
|
|
|
86baa9 |
- 'HTTP': ('httpd', 40),
|
|
|
86baa9 |
- 'KEYS': ('ipa-custodia', 41),
|
|
|
86baa9 |
- 'NTP': ('ntpd', 45),
|
|
|
86baa9 |
- 'CA': ('pki-tomcatd', 50),
|
|
|
86baa9 |
- 'KRA': ('pki-tomcatd', 51),
|
|
|
86baa9 |
- 'ADTRUST': ('smb', 60),
|
|
|
86baa9 |
- 'EXTID': ('winbind', 70),
|
|
|
86baa9 |
- 'OTPD': ('ipa-otpd', 80),
|
|
|
86baa9 |
- 'DNSKeyExporter': ('ipa-ods-exporter', 90),
|
|
|
86baa9 |
- 'DNSSEC': ('ods-enforcerd', 100),
|
|
|
86baa9 |
- 'DNSKeySync': ('ipa-dnskeysyncd', 110),
|
|
|
86baa9 |
-}
|
|
|
86baa9 |
-
|
|
|
86baa9 |
-CONFIGURED_SERVICE = u'configuredService'
|
|
|
86baa9 |
-ENABLED_SERVICE = u'enabledService'
|
|
|
86baa9 |
-
|
|
|
86baa9 |
|
|
|
86baa9 |
def print_msg(message, output_fd=sys.stdout):
|
|
|
86baa9 |
logger.debug("%s", message)
|
|
|
86baa9 |
@@ -117,44 +98,6 @@ def add_principals_to_group(admin_conn, group, member_attr, principals):
|
|
|
86baa9 |
pass
|
|
|
86baa9 |
|
|
|
86baa9 |
|
|
|
86baa9 |
-def find_providing_servers(svcname, conn, api):
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- Find servers that provide the given service.
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- :param svcname: The service to find
|
|
|
86baa9 |
- :param conn: a connection to the LDAP server
|
|
|
86baa9 |
- :return: list of host names (possibly empty)
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
|
|
|
86baa9 |
- query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
|
|
|
86baa9 |
- 'ipaConfigString': ENABLED_SERVICE,
|
|
|
86baa9 |
- 'cn': svcname}, rules='&')
|
|
|
86baa9 |
- try:
|
|
|
86baa9 |
- entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn)
|
|
|
86baa9 |
- except errors.NotFound:
|
|
|
86baa9 |
- return []
|
|
|
86baa9 |
- else:
|
|
|
86baa9 |
- return [entry.dn[1].value for entry in entries]
|
|
|
86baa9 |
-
|
|
|
86baa9 |
-
|
|
|
86baa9 |
-def find_providing_server(svcname, conn, host_name=None, api=api):
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- Find a server that provides the given service.
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- :param svcname: The service to find
|
|
|
86baa9 |
- :param conn: a connection to the LDAP server
|
|
|
86baa9 |
- :param host_name: the preferred server
|
|
|
86baa9 |
- :return: the selected host name
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- servers = find_providing_servers(svcname, conn, api)
|
|
|
86baa9 |
- if len(servers) == 0:
|
|
|
86baa9 |
- return None
|
|
|
86baa9 |
- if host_name in servers:
|
|
|
86baa9 |
- return host_name
|
|
|
86baa9 |
- return servers[0]
|
|
|
86baa9 |
-
|
|
|
86baa9 |
|
|
|
86baa9 |
def case_insensitive_attr_has_value(attr, value):
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
@@ -618,7 +561,7 @@ class Service(object):
|
|
|
86baa9 |
|
|
|
86baa9 |
def _ldap_enable(self, value, name, fqdn, ldap_suffix, config):
|
|
|
86baa9 |
extra_config_opts = [
|
|
|
86baa9 |
- ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])])
|
|
|
86baa9 |
+ u'startOrder {}'.format(SERVICE_LIST[name].startorder),
|
|
|
86baa9 |
]
|
|
|
86baa9 |
extra_config_opts.extend(config)
|
|
|
86baa9 |
|
|
|
86baa9 |
diff --git a/ipaserver/masters.py b/ipaserver/masters.py
|
|
|
86baa9 |
new file mode 100644
|
|
|
86baa9 |
index 0000000000000000000000000000000000000000..171c3abe0d6eea5aa6bcc642815eceae3ae885e7
|
|
|
86baa9 |
--- /dev/null
|
|
|
86baa9 |
+++ b/ipaserver/masters.py
|
|
|
86baa9 |
@@ -0,0 +1,123 @@
|
|
|
86baa9 |
+#
|
|
|
86baa9 |
+# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
|
|
|
86baa9 |
+#
|
|
|
86baa9 |
+"""Helpers services in for cn=masters,cn=ipa,cn=etc
|
|
|
86baa9 |
+"""
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+from __future__ import absolute_import
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+import collections
|
|
|
86baa9 |
+import logging
|
|
|
86baa9 |
+import random
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+from ipapython.dn import DN
|
|
|
86baa9 |
+from ipalib import api
|
|
|
86baa9 |
+from ipalib import errors
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+logger = logging.getLogger(__name__)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+# constants for ipaConfigString
|
|
|
86baa9 |
+CONFIGURED_SERVICE = u'configuredService'
|
|
|
86baa9 |
+ENABLED_SERVICE = u'enabledService'
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+# The service name as stored in cn=masters,cn=ipa,cn=etc. The values are:
|
|
|
86baa9 |
+# 0: systemd service name
|
|
|
86baa9 |
+# 1: start order for system service
|
|
|
86baa9 |
+# 2: LDAP server entry CN, also used as SERVICE_LIST key
|
|
|
86baa9 |
+service_definition = collections.namedtuple(
|
|
|
86baa9 |
+ "service_definition",
|
|
|
86baa9 |
+ "systemd_name startorder service_entry"
|
|
|
86baa9 |
+)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+SERVICES = [
|
|
|
86baa9 |
+ service_definition('krb5kdc', 10, 'KDC'),
|
|
|
86baa9 |
+ service_definition('kadmin', 20, 'KPASSWD'),
|
|
|
86baa9 |
+ service_definition('named', 30, 'DNS'),
|
|
|
86baa9 |
+ service_definition('httpd', 40, 'HTTP'),
|
|
|
86baa9 |
+ service_definition('ipa-custodia', 41, 'KEYS'),
|
|
|
86baa9 |
+ service_definition('ntpd', 45, 'NTP'),
|
|
|
86baa9 |
+ service_definition('pki-tomcatd', 50, 'CA'),
|
|
|
86baa9 |
+ service_definition('pki-tomcatd', 51, 'KRA'),
|
|
|
86baa9 |
+ service_definition('smb', 60, 'ADTRUST'),
|
|
|
86baa9 |
+ service_definition('winbind', 70, 'EXTID'),
|
|
|
86baa9 |
+ service_definition('ipa-otpd', 80, 'OTPD'),
|
|
|
86baa9 |
+ service_definition('ipa-ods-exporter', 90, 'DNSKeyExporter'),
|
|
|
86baa9 |
+ service_definition('ods-enforcerd', 100, 'DNSSEC'),
|
|
|
86baa9 |
+ service_definition('ipa-dnskeysyncd', 110, 'DNSKeySync'),
|
|
|
86baa9 |
+]
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+SERVICE_LIST = {s.service_entry: s for s in SERVICES}
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+def find_providing_servers(svcname, conn=None, preferred_hosts=(), api=api):
|
|
|
86baa9 |
+ """Find servers that provide the given service.
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ :param svcname: The service to find
|
|
|
86baa9 |
+ :param preferred_hosts: preferred servers
|
|
|
86baa9 |
+ :param conn: a connection to the LDAP server
|
|
|
86baa9 |
+ :param api: ipalib.API instance
|
|
|
86baa9 |
+ :return: list of host names in randomized order (possibly empty)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ Preferred servers are moved to the front of the list if and only if they
|
|
|
86baa9 |
+ are found as providing servers.
|
|
|
86baa9 |
+ """
|
|
|
86baa9 |
+ assert isinstance(preferred_hosts, (tuple, list))
|
|
|
86baa9 |
+ if svcname not in SERVICE_LIST:
|
|
|
86baa9 |
+ raise ValueError("Unknown service '{}'.".format(svcname))
|
|
|
86baa9 |
+ if conn is None:
|
|
|
86baa9 |
+ conn = api.Backend.ldap2
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ dn = DN(api.env.container_masters, api.env.basedn)
|
|
|
86baa9 |
+ query_filter = conn.make_filter(
|
|
|
86baa9 |
+ {
|
|
|
86baa9 |
+ 'objectClass': 'ipaConfigObject',
|
|
|
86baa9 |
+ 'ipaConfigString': ENABLED_SERVICE,
|
|
|
86baa9 |
+ 'cn': svcname
|
|
|
86baa9 |
+ },
|
|
|
86baa9 |
+ rules='&'
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ entries, _trunc = conn.find_entries(
|
|
|
86baa9 |
+ filter=query_filter,
|
|
|
86baa9 |
+ attrs_list=[],
|
|
|
86baa9 |
+ base_dn=dn
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ except errors.NotFound:
|
|
|
86baa9 |
+ return []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # unique list of host names, DNS is case insensitive
|
|
|
86baa9 |
+ servers = list(set(entry.dn[1].value.lower() for entry in entries))
|
|
|
86baa9 |
+ # shuffle the list like DNS SRV would randomize it
|
|
|
86baa9 |
+ random.shuffle(servers)
|
|
|
86baa9 |
+ # Move preferred hosts to front
|
|
|
86baa9 |
+ for host_name in reversed(preferred_hosts):
|
|
|
86baa9 |
+ host_name = host_name.lower()
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ servers.remove(host_name)
|
|
|
86baa9 |
+ except ValueError:
|
|
|
86baa9 |
+ # preferred server not found, log and ignore
|
|
|
86baa9 |
+ logger.warning(
|
|
|
86baa9 |
+ "Lookup failed: Preferred host %s does not provide %s.",
|
|
|
86baa9 |
+ host_name, svcname
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ else:
|
|
|
86baa9 |
+ servers.insert(0, host_name)
|
|
|
86baa9 |
+ return servers
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+def find_providing_server(svcname, conn=None, preferred_hosts=(), api=api):
|
|
|
86baa9 |
+ """Find a server that provides the given service.
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ :param svcname: The service to find
|
|
|
86baa9 |
+ :param conn: a connection to the LDAP server
|
|
|
86baa9 |
+ :param host_name: the preferred server
|
|
|
86baa9 |
+ :param api: ipalib.API instance
|
|
|
86baa9 |
+ :return: the selected host name or None
|
|
|
86baa9 |
+ """
|
|
|
86baa9 |
+ servers = find_providing_servers(
|
|
|
86baa9 |
+ svcname, conn=conn, preferred_hosts=preferred_hosts, api=api
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ if not servers:
|
|
|
86baa9 |
+ return None
|
|
|
86baa9 |
+ else:
|
|
|
86baa9 |
+ return servers[0]
|
|
|
86baa9 |
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
|
|
|
86baa9 |
index da77f507cb2307eeec63acd0ab9d58c985ea8fe2..ff750e9d38ff98e0e1fa1c2eee5a3d0719da94bf 100644
|
|
|
86baa9 |
--- a/ipaserver/plugins/cert.py
|
|
|
86baa9 |
+++ b/ipaserver/plugins/cert.py
|
|
|
86baa9 |
@@ -55,6 +55,7 @@ from ipalib import output
|
|
|
86baa9 |
from ipapython import dnsutil, kerberos
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
from ipaserver.plugins.service import normalize_principal, validate_realm
|
|
|
86baa9 |
+from ipaserver.masters import ENABLED_SERVICE, CONFIGURED_SERVICE
|
|
|
86baa9 |
|
|
|
86baa9 |
try:
|
|
|
86baa9 |
import pyhbac
|
|
|
86baa9 |
@@ -297,19 +298,14 @@ def caacl_check(principal, ca, profile_id):
|
|
|
86baa9 |
def ca_kdc_check(api_instance, hostname):
|
|
|
86baa9 |
master_dn = api_instance.Object.server.get_dn(unicode(hostname))
|
|
|
86baa9 |
kdc_dn = DN(('cn', 'KDC'), master_dn)
|
|
|
86baa9 |
-
|
|
|
86baa9 |
+ wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE}
|
|
|
86baa9 |
try:
|
|
|
86baa9 |
kdc_entry = api_instance.Backend.ldap2.get_entry(
|
|
|
86baa9 |
kdc_dn, ['ipaConfigString'])
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- ipaconfigstring = {val.lower() for val in kdc_entry['ipaConfigString']}
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- if 'enabledservice' not in ipaconfigstring \
|
|
|
86baa9 |
- and 'configuredservice' not in ipaconfigstring:
|
|
|
86baa9 |
+ if not wanted.intersection(kdc_entry['ipaConfigString']):
|
|
|
86baa9 |
raise errors.NotFound(
|
|
|
86baa9 |
reason=_("enabledService/configuredService not in "
|
|
|
86baa9 |
"ipaConfigString kdc entry"))
|
|
|
86baa9 |
-
|
|
|
86baa9 |
except errors.NotFound:
|
|
|
86baa9 |
raise errors.ACIError(
|
|
|
86baa9 |
info=_("Host '%(hostname)s' is not an active KDC")
|
|
|
86baa9 |
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
|
|
|
86baa9 |
index 5a44f00c583cd4bae8211a511f907c4e179bb3f6..17e2225688c0c3878ef22e5979c65e30f971b5b3 100644
|
|
|
86baa9 |
--- a/ipaserver/plugins/dogtag.py
|
|
|
86baa9 |
+++ b/ipaserver/plugins/dogtag.py
|
|
|
86baa9 |
@@ -255,6 +255,7 @@ from ipalib import Backend, api
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
import ipapython.cookie
|
|
|
86baa9 |
from ipapython import dogtag, ipautil, certdb
|
|
|
86baa9 |
+from ipaserver.masters import find_providing_server
|
|
|
86baa9 |
|
|
|
86baa9 |
if api.env.in_server:
|
|
|
86baa9 |
import pki
|
|
|
86baa9 |
@@ -1208,56 +1209,6 @@ def parse_updateCRL_xml(doc):
|
|
|
86baa9 |
return response
|
|
|
86baa9 |
|
|
|
86baa9 |
|
|
|
86baa9 |
-def host_has_service(host, ldap2, service='CA'):
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- :param host: A host which might be a master for a service.
|
|
|
86baa9 |
- :param ldap2: connection to the local database
|
|
|
86baa9 |
- :param service: The service for which the host might be a master.
|
|
|
86baa9 |
- :return: (true, false)
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- Check if a specified host is a master for a specified service.
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- base_dn = DN(('cn', host), ('cn', 'masters'), ('cn', 'ipa'),
|
|
|
86baa9 |
- ('cn', 'etc'), api.env.basedn)
|
|
|
86baa9 |
- filter_attrs = {
|
|
|
86baa9 |
- 'objectClass': 'ipaConfigObject',
|
|
|
86baa9 |
- 'cn': service,
|
|
|
86baa9 |
- 'ipaConfigString': 'enabledService',
|
|
|
86baa9 |
- }
|
|
|
86baa9 |
- query_filter = ldap2.make_filter(filter_attrs, rules='&')
|
|
|
86baa9 |
- try:
|
|
|
86baa9 |
- ent, _trunc = ldap2.find_entries(filter=query_filter, base_dn=base_dn)
|
|
|
86baa9 |
- if len(ent):
|
|
|
86baa9 |
- return True
|
|
|
86baa9 |
- except Exception:
|
|
|
86baa9 |
- pass
|
|
|
86baa9 |
- return False
|
|
|
86baa9 |
-
|
|
|
86baa9 |
-
|
|
|
86baa9 |
-def select_any_master(ldap2, service='CA'):
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- :param ldap2: connection to the local database
|
|
|
86baa9 |
- :param service: The service for which we're looking for a master.
|
|
|
86baa9 |
- :return: host as str
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- Select any host which is a master for a specified service.
|
|
|
86baa9 |
- """
|
|
|
86baa9 |
- base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
|
|
|
86baa9 |
- api.env.basedn)
|
|
|
86baa9 |
- filter_attrs = {
|
|
|
86baa9 |
- 'objectClass': 'ipaConfigObject',
|
|
|
86baa9 |
- 'cn': service,
|
|
|
86baa9 |
- 'ipaConfigString': 'enabledService',}
|
|
|
86baa9 |
- query_filter = ldap2.make_filter(filter_attrs, rules='&')
|
|
|
86baa9 |
- try:
|
|
|
86baa9 |
- ent, _trunc = ldap2.find_entries(filter=query_filter, base_dn=base_dn)
|
|
|
86baa9 |
- if len(ent):
|
|
|
86baa9 |
- entry = random.choice(ent)
|
|
|
86baa9 |
- return entry.dn[1].value
|
|
|
86baa9 |
- except Exception:
|
|
|
86baa9 |
- pass
|
|
|
86baa9 |
- return None
|
|
|
86baa9 |
-
|
|
|
86baa9 |
#-------------------------------------------------------------------------------
|
|
|
86baa9 |
|
|
|
86baa9 |
from ipalib import Registry, errors, SkipPluginModule
|
|
|
86baa9 |
@@ -1265,7 +1216,6 @@ if api.env.ra_plugin != 'dogtag':
|
|
|
86baa9 |
# In this case, abort loading this plugin module...
|
|
|
86baa9 |
raise SkipPluginModule(reason='dogtag not selected as RA plugin')
|
|
|
86baa9 |
import os
|
|
|
86baa9 |
-import random
|
|
|
86baa9 |
from ipaserver.plugins import rabase
|
|
|
86baa9 |
from ipalib.constants import TYPE_ERROR
|
|
|
86baa9 |
from ipalib import _
|
|
|
86baa9 |
@@ -1330,17 +1280,19 @@ class RestClient(Backend):
|
|
|
86baa9 |
if self._ca_host is not None:
|
|
|
86baa9 |
return self._ca_host
|
|
|
86baa9 |
|
|
|
86baa9 |
- ldap2 = self.api.Backend.ldap2
|
|
|
86baa9 |
- if host_has_service(api.env.ca_host, ldap2, "CA"):
|
|
|
86baa9 |
- object.__setattr__(self, '_ca_host', api.env.ca_host)
|
|
|
86baa9 |
- elif api.env.host != api.env.ca_host:
|
|
|
86baa9 |
- if host_has_service(api.env.host, ldap2, "CA"):
|
|
|
86baa9 |
- object.__setattr__(self, '_ca_host', api.env.host)
|
|
|
86baa9 |
- else:
|
|
|
86baa9 |
- object.__setattr__(self, '_ca_host', select_any_master(ldap2))
|
|
|
86baa9 |
- if self._ca_host is None:
|
|
|
86baa9 |
- object.__setattr__(self, '_ca_host', api.env.ca_host)
|
|
|
86baa9 |
- return self._ca_host
|
|
|
86baa9 |
+ preferred = [api.env.ca_host]
|
|
|
86baa9 |
+ if api.env.host != api.env.ca_host:
|
|
|
86baa9 |
+ preferred.append(api.env.host)
|
|
|
86baa9 |
+ ca_host = find_providing_server(
|
|
|
86baa9 |
+ 'CA', conn=self.api.Backend.ldap2, preferred_hosts=preferred,
|
|
|
86baa9 |
+ api=self.api
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ if ca_host is None:
|
|
|
86baa9 |
+ # TODO: need during installation, CA is not yet set as enabled
|
|
|
86baa9 |
+ ca_host = api.env.ca_host
|
|
|
86baa9 |
+ # object is locked, need to use __setattr__()
|
|
|
86baa9 |
+ object.__setattr__(self, '_ca_host', ca_host)
|
|
|
86baa9 |
+ return ca_host
|
|
|
86baa9 |
|
|
|
86baa9 |
def __enter__(self):
|
|
|
86baa9 |
"""Log into the REST API"""
|
|
|
86baa9 |
@@ -2082,9 +2034,7 @@ class kra(Backend):
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
|
|
|
86baa9 |
def __init__(self, api, kra_port=443):
|
|
|
86baa9 |
-
|
|
|
86baa9 |
self.kra_port = kra_port
|
|
|
86baa9 |
-
|
|
|
86baa9 |
super(kra, self).__init__(api)
|
|
|
86baa9 |
|
|
|
86baa9 |
@property
|
|
|
86baa9 |
@@ -2095,17 +2045,18 @@ class kra(Backend):
|
|
|
86baa9 |
|
|
|
86baa9 |
Select our KRA host.
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
- ldap2 = self.api.Backend.ldap2
|
|
|
86baa9 |
- if host_has_service(api.env.ca_host, ldap2, "KRA"):
|
|
|
86baa9 |
- return api.env.ca_host
|
|
|
86baa9 |
+ preferred = [api.env.ca_host]
|
|
|
86baa9 |
if api.env.host != api.env.ca_host:
|
|
|
86baa9 |
- if host_has_service(api.env.host, ldap2, "KRA"):
|
|
|
86baa9 |
- return api.env.host
|
|
|
86baa9 |
- host = select_any_master(ldap2, "KRA")
|
|
|
86baa9 |
- if host:
|
|
|
86baa9 |
- return host
|
|
|
86baa9 |
- else:
|
|
|
86baa9 |
- return api.env.ca_host
|
|
|
86baa9 |
+ preferred.append(api.env.host)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ kra_host = find_providing_server(
|
|
|
86baa9 |
+ 'KRA', self.api.Backend.ldap2, preferred_hosts=preferred,
|
|
|
86baa9 |
+ api=self.api
|
|
|
86baa9 |
+ )
|
|
|
86baa9 |
+ if kra_host is None:
|
|
|
86baa9 |
+ # TODO: need during installation, KRA is not yet set as enabled
|
|
|
86baa9 |
+ kra_host = api.env.ca_host
|
|
|
86baa9 |
+ return kra_host
|
|
|
86baa9 |
|
|
|
86baa9 |
@contextlib.contextmanager
|
|
|
86baa9 |
def get_client(self):
|
|
|
86baa9 |
diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
|
|
|
86baa9 |
index 994a59e35bb5d3a007e3d63bb273ba9ea552407d..af4e63710136a15e1673210c3e2207658698fbb5 100644
|
|
|
86baa9 |
--- a/ipaserver/servroles.py
|
|
|
86baa9 |
+++ b/ipaserver/servroles.py
|
|
|
86baa9 |
@@ -79,7 +79,7 @@ import six
|
|
|
86baa9 |
|
|
|
86baa9 |
from ipalib import _, errors
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
-
|
|
|
86baa9 |
+from ipaserver.masters import ENABLED_SERVICE
|
|
|
86baa9 |
|
|
|
86baa9 |
if six.PY3:
|
|
|
86baa9 |
unicode = str
|
|
|
86baa9 |
@@ -483,11 +483,8 @@ class ServiceBasedRole(BaseServerRole):
|
|
|
86baa9 |
:param entry: LDAPEntry of the service
|
|
|
86baa9 |
:returns: True if the service entry is enabled, False otherwise
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
- enabled_value = 'enabledservice'
|
|
|
86baa9 |
- ipaconfigstring_values = set(
|
|
|
86baa9 |
- e.lower() for e in entry.get('ipaConfigString', []))
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- return enabled_value in ipaconfigstring_values
|
|
|
86baa9 |
+ ipaconfigstring_values = set(entry.get('ipaConfigString', []))
|
|
|
86baa9 |
+ return ENABLED_SERVICE in ipaconfigstring_values
|
|
|
86baa9 |
|
|
|
86baa9 |
def _get_services_by_masters(self, entries):
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py
|
|
|
86baa9 |
index 76f1378ed5a94069cdef918d577e3658b78ecc43..e1bf0254dddfe03ade4fe72419b609de8e95b79a 100644
|
|
|
86baa9 |
--- a/ipatests/test_ipaserver/test_serverroles.py
|
|
|
86baa9 |
+++ b/ipatests/test_ipaserver/test_serverroles.py
|
|
|
86baa9 |
@@ -16,6 +16,7 @@ import pytest
|
|
|
86baa9 |
from ipaplatform.paths import paths
|
|
|
86baa9 |
from ipalib import api, create_api, errors
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
+from ipaserver.masters import ENABLED_SERVICE
|
|
|
86baa9 |
|
|
|
86baa9 |
pytestmark = pytest.mark.needs_ipaapi
|
|
|
86baa9 |
|
|
|
86baa9 |
@@ -25,7 +26,7 @@ def _make_service_entry(ldap_backend, dn, enabled=True, other_config=None):
|
|
|
86baa9 |
'objectClass': ['top', 'nsContainer', 'ipaConfigObject'],
|
|
|
86baa9 |
}
|
|
|
86baa9 |
if enabled:
|
|
|
86baa9 |
- mods.update({'ipaConfigString': ['enabledService']})
|
|
|
86baa9 |
+ mods.update({'ipaConfigString': [ENABLED_SERVICE]})
|
|
|
86baa9 |
|
|
|
86baa9 |
if other_config is not None:
|
|
|
86baa9 |
mods.setdefault('ipaConfigString', [])
|
|
|
86baa9 |
--
|
|
|
86baa9 |
2.20.1
|
|
|
86baa9 |
|