mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-26 16:16:31 -06:00
Implement replica promotion functionality
This patch implements a new flag --promote for the ipa-replica-install command that allows an administrative user to 'promote' an already joined client to become a full ipa server. The only credentials used are that of an administrator. This code relies on ipa-custodia being available on the peer master as well as a number of other patches to allow a computer account to request certificates for its services. Therefore this feature is marked to work only with domain level 1 and above servers. Ticket: https://fedorahosted.org/freeipa/ticket/2888 Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
parent
2cd0d20a2a
commit
d03619fff3
@ -30,6 +30,7 @@ ReplicaInstall = cli.install_tool(
|
|||||||
usage='%prog [options] REPLICA_FILE',
|
usage='%prog [options] REPLICA_FILE',
|
||||||
log_file_name=paths.IPAREPLICA_INSTALL_LOG,
|
log_file_name=paths.IPAREPLICA_INSTALL_LOG,
|
||||||
debug_option=True,
|
debug_option=True,
|
||||||
|
use_private_ccache=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -360,5 +360,6 @@ class BasePathNamespace(object):
|
|||||||
IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf'
|
IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf'
|
||||||
IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock'
|
IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock'
|
||||||
IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log'
|
IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log'
|
||||||
|
IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab'
|
||||||
|
|
||||||
path_namespace = BasePathNamespace
|
path_namespace = BasePathNamespace
|
||||||
|
@ -25,6 +25,7 @@ if six.PY3:
|
|||||||
|
|
||||||
def install_tool(configurable_class, command_name, log_file_name,
|
def install_tool(configurable_class, command_name, log_file_name,
|
||||||
positional_arguments=None, usage=None, debug_option=False,
|
positional_arguments=None, usage=None, debug_option=False,
|
||||||
|
use_private_ccache=True,
|
||||||
uninstall_log_file_name=None,
|
uninstall_log_file_name=None,
|
||||||
uninstall_positional_arguments=None, uninstall_usage=None):
|
uninstall_positional_arguments=None, uninstall_usage=None):
|
||||||
if (uninstall_log_file_name is not None or
|
if (uninstall_log_file_name is not None or
|
||||||
@ -52,6 +53,7 @@ def install_tool(configurable_class, command_name, log_file_name,
|
|||||||
usage=usage,
|
usage=usage,
|
||||||
debug_option=debug_option,
|
debug_option=debug_option,
|
||||||
uninstall_kwargs=uninstall_kwargs,
|
uninstall_kwargs=uninstall_kwargs,
|
||||||
|
use_private_ccache=use_private_ccache,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,6 +78,7 @@ class ConfigureTool(admintool.AdminTool):
|
|||||||
configurable_class = None
|
configurable_class = None
|
||||||
debug_option = False
|
debug_option = False
|
||||||
positional_arguments = None
|
positional_arguments = None
|
||||||
|
use_private_ccache = True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _transform(configurable_class):
|
def _transform(configurable_class):
|
||||||
@ -305,10 +308,12 @@ class ConfigureTool(admintool.AdminTool):
|
|||||||
|
|
||||||
signal.signal(signal.SIGTERM, self.__signal_handler)
|
signal.signal(signal.SIGTERM, self.__signal_handler)
|
||||||
|
|
||||||
# Use private ccache
|
if self.use_private_ccache:
|
||||||
with private_ccache():
|
with private_ccache():
|
||||||
|
super(ConfigureTool, self).run()
|
||||||
|
cfgr.run()
|
||||||
|
else:
|
||||||
super(ConfigureTool, self).run()
|
super(ConfigureTool, self).run()
|
||||||
|
|
||||||
cfgr.run()
|
cfgr.run()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -260,6 +260,32 @@ def is_step_one_done():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def find_ca_server(host_name, conn, api=api):
|
||||||
|
"""
|
||||||
|
:param host_name: the preferred server
|
||||||
|
:param conn: a connection to the LDAP server
|
||||||
|
:return: the selected host name
|
||||||
|
|
||||||
|
Find a server that is a CA.
|
||||||
|
"""
|
||||||
|
dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
|
||||||
|
query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
|
||||||
|
'ipaConfigString': 'enabledService',
|
||||||
|
'cn': 'CA'}, rules='&')
|
||||||
|
try:
|
||||||
|
entries, trunc = conn.find_entries(filter=query_filter, base_dn=dn)
|
||||||
|
except errors.NotFound:
|
||||||
|
return None
|
||||||
|
if len(entries):
|
||||||
|
if host_name is not None:
|
||||||
|
for entry in entries:
|
||||||
|
if entry.dn[1].value == host_name:
|
||||||
|
return host_name
|
||||||
|
# if the preferred is not found, return the first in the list
|
||||||
|
return entries[0].dn[1].value
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def is_ca_installed_locally():
|
def is_ca_installed_locally():
|
||||||
"""Check if CA is installed locally by checking for existence of CS.cfg
|
"""Check if CA is installed locally by checking for existence of CS.cfg
|
||||||
:return:True/False
|
:return:True/False
|
||||||
|
@ -653,6 +653,18 @@ class CertDB(object):
|
|||||||
def export_pem_cert(self, nickname, location):
|
def export_pem_cert(self, nickname, location):
|
||||||
return self.nssdb.export_pem_cert(nickname, location)
|
return self.nssdb.export_pem_cert(nickname, location)
|
||||||
|
|
||||||
|
def request_service_cert(self, nickname, principal, host, pwdconf=False):
|
||||||
|
self.create_from_cacert(paths.IPA_CA_CRT)
|
||||||
|
if pwdconf:
|
||||||
|
self.create_password_conf()
|
||||||
|
reqid = certmonger.request_cert(nssdb=self.secdir,
|
||||||
|
nickname=nickname,
|
||||||
|
principal=principal,
|
||||||
|
subject=host,
|
||||||
|
passwd_fname=self.passwd_fname)
|
||||||
|
# Now wait for the cert to appear. Check three times then abort
|
||||||
|
certmonger.wait_for_request(reqid, timeout=15)
|
||||||
|
|
||||||
|
|
||||||
class _CrossProcessLock(object):
|
class _CrossProcessLock(object):
|
||||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Copyright (C) 2015 FreeIPa Project Contributors, see 'COPYING' for license.
|
# Copyright (C) 2015 FreeIPa Project Contributors, see 'COPYING' for license.
|
||||||
|
|
||||||
from ipapython.secrets.kem import IPAKEMKeys
|
from ipapython.secrets.kem import IPAKEMKeys
|
||||||
|
from ipapython.secrets.client import CustodiaClient
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from service import SimpleServiceInstance
|
from service import SimpleServiceInstance
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
@ -9,11 +10,14 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
class CustodiaInstance(SimpleServiceInstance):
|
class CustodiaInstance(SimpleServiceInstance):
|
||||||
def __init__(self):
|
def __init__(self, host_name=None, realm=None):
|
||||||
super(CustodiaInstance, self).__init__("ipa-custodia")
|
super(CustodiaInstance, self).__init__("ipa-custodia")
|
||||||
self.config_file = paths.IPA_CUSTODIA_CONF
|
self.config_file = paths.IPA_CUSTODIA_CONF
|
||||||
self.server_keys = os.path.join(paths.IPA_CUSTODIA_CONF_DIR,
|
self.server_keys = os.path.join(paths.IPA_CUSTODIA_CONF_DIR,
|
||||||
'server.keys')
|
'server.keys')
|
||||||
|
self.ldap_uri = None
|
||||||
|
self.fqdn = host_name
|
||||||
|
self.realm = realm
|
||||||
|
|
||||||
def __config_file(self):
|
def __config_file(self):
|
||||||
template_file = os.path.basename(self.config_file) + '.template'
|
template_file = os.path.basename(self.config_file) + '.template'
|
||||||
@ -28,22 +32,48 @@ class CustodiaInstance(SimpleServiceInstance):
|
|||||||
fd.flush()
|
fd.flush()
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
def create_instance(self, *args, **kwargs):
|
def create_instance(self, dm_password=None):
|
||||||
|
suffix = ipautil.realm_to_suffix(self.realm)
|
||||||
self.step("Generating ipa-custodia config file", self.__config_file)
|
self.step("Generating ipa-custodia config file", self.__config_file)
|
||||||
self.step("Generating ipa-custodia keys", self.__gen_keys)
|
self.step("Generating ipa-custodia keys", self.__gen_keys)
|
||||||
super(CustodiaInstance, self).create_instance(*args, **kwargs)
|
super(CustodiaInstance, self).create_instance(gensvc_name='KEYS',
|
||||||
|
fqdn=self.fqdn,
|
||||||
|
dm_password=dm_password,
|
||||||
|
ldap_suffix=suffix,
|
||||||
|
realm=self.realm)
|
||||||
|
|
||||||
def __gen_keys(self):
|
def __gen_keys(self):
|
||||||
KeyStore = IPAKEMKeys({'server_keys': self.server_keys})
|
KeyStore = IPAKEMKeys({'server_keys': self.server_keys,
|
||||||
|
'ldap_uri': self.ldap_uri})
|
||||||
KeyStore.generate_server_keys()
|
KeyStore.generate_server_keys()
|
||||||
|
|
||||||
def upgrade_instance(self, realm):
|
def upgrade_instance(self):
|
||||||
self.realm = realm
|
|
||||||
if not os.path.exists(self.config_file):
|
if not os.path.exists(self.config_file):
|
||||||
self.__config_file()
|
self.__config_file()
|
||||||
if not os.path.exists(self.server_keys):
|
if not os.path.exists(self.server_keys):
|
||||||
self.__gen_keys()
|
self.__gen_keys()
|
||||||
|
|
||||||
|
def create_replica(self, master_host_name):
|
||||||
|
suffix = ipautil.realm_to_suffix(self.realm)
|
||||||
|
self.ldap_uri = 'ldap://%s' % master_host_name
|
||||||
|
self.master_host_name = master_host_name
|
||||||
|
|
||||||
|
self.step("Generating ipa-custodia config file", self.__config_file)
|
||||||
|
self.step("Generating ipa-custodia keys", self.__gen_keys)
|
||||||
|
self.step("Importing RA Key", self.__import_ra_key)
|
||||||
|
super(CustodiaInstance, self).create_instance(gensvc_name='KEYS',
|
||||||
|
fqdn=self.fqdn,
|
||||||
|
ldap_suffix=suffix,
|
||||||
|
realm=self.realm)
|
||||||
|
|
||||||
|
def __import_ra_key(self):
|
||||||
|
cli = CustodiaClient(self.fqdn, self.master_host_name, self.realm)
|
||||||
|
cli.fetch_key('ra/ipaCert')
|
||||||
|
|
||||||
|
def import_dm_password(self, master_host_name):
|
||||||
|
cli = CustodiaClient(self.fqdn, master_host_name, self.realm)
|
||||||
|
cli.fetch_key('dm/DMHash')
|
||||||
|
|
||||||
def __start(self):
|
def __start(self):
|
||||||
super(CustodiaInstance, self).__start()
|
super(CustodiaInstance, self).__start()
|
||||||
|
|
||||||
|
@ -255,8 +255,8 @@ class DsInstance(service.Service):
|
|||||||
self.step("configure autobind for root", self.__root_autobind)
|
self.step("configure autobind for root", self.__root_autobind)
|
||||||
self.step("configure new location for managed entries", self.__repoint_managed_entries)
|
self.step("configure new location for managed entries", self.__repoint_managed_entries)
|
||||||
self.step("configure dirsrv ccache", self.configure_dirsrv_ccache)
|
self.step("configure dirsrv ccache", self.configure_dirsrv_ccache)
|
||||||
self.step("enable SASL mapping fallback", self.__enable_sasl_mapping_fallback)
|
self.step("enabling SASL mapping fallback",
|
||||||
self.step("restarting directory server", self.__restart_instance)
|
self.__enable_sasl_mapping_fallback)
|
||||||
|
|
||||||
def __common_post_setup(self):
|
def __common_post_setup(self):
|
||||||
self.step("initializing group membership", self.init_memberof)
|
self.step("initializing group membership", self.init_memberof)
|
||||||
@ -301,6 +301,7 @@ class DsInstance(service.Service):
|
|||||||
subject_base, idstart, idmax, pkcs12_info, ca_file=ca_file)
|
subject_base, idstart, idmax, pkcs12_info, ca_file=ca_file)
|
||||||
|
|
||||||
self.__common_setup()
|
self.__common_setup()
|
||||||
|
self.step("restarting directory server", self.__restart_instance)
|
||||||
|
|
||||||
self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
||||||
self.step("adding default layout", self.__add_default_layout)
|
self.step("adding default layout", self.__add_default_layout)
|
||||||
@ -314,6 +315,8 @@ class DsInstance(service.Service):
|
|||||||
if hbac_allow:
|
if hbac_allow:
|
||||||
self.step("creating default HBAC rule allow_all", self.add_hbac)
|
self.step("creating default HBAC rule allow_all", self.add_hbac)
|
||||||
self.step("creating default CA ACL rule", self.add_caacl)
|
self.step("creating default CA ACL rule", self.add_caacl)
|
||||||
|
self.step("adding sasl mappings to the directory",
|
||||||
|
self.__configure_sasl_mappings)
|
||||||
self.step("adding entries for topology management", self.__add_topology_entries)
|
self.step("adding entries for topology management", self.__add_topology_entries)
|
||||||
|
|
||||||
self.__common_post_setup()
|
self.__common_post_setup()
|
||||||
@ -331,7 +334,8 @@ class DsInstance(service.Service):
|
|||||||
|
|
||||||
def create_replica(self, realm_name, master_fqdn, fqdn,
|
def create_replica(self, realm_name, master_fqdn, fqdn,
|
||||||
domain_name, dm_password, subject_base,
|
domain_name, dm_password, subject_base,
|
||||||
pkcs12_info=None, ca_file=None, ca_is_configured=None):
|
pkcs12_info=None, ca_file=None,
|
||||||
|
ca_is_configured=None, promote=False):
|
||||||
# idstart and idmax are configured so that the range is seen as
|
# idstart and idmax are configured so that the range is seen as
|
||||||
# depleted by the DNA plugin and the replica will go and get a
|
# depleted by the DNA plugin and the replica will go and get a
|
||||||
# new range from the master.
|
# new range from the master.
|
||||||
@ -353,8 +357,15 @@ class DsInstance(service.Service):
|
|||||||
self.master_fqdn = master_fqdn
|
self.master_fqdn = master_fqdn
|
||||||
if ca_is_configured is not None:
|
if ca_is_configured is not None:
|
||||||
self.ca_is_configured = ca_is_configured
|
self.ca_is_configured = ca_is_configured
|
||||||
|
self.promote = promote
|
||||||
|
|
||||||
self.__common_setup(True)
|
self.__common_setup(enable_ssl=(not self.promote))
|
||||||
|
self.step("restarting directory server", self.__restart_instance)
|
||||||
|
|
||||||
|
if self.promote:
|
||||||
|
self.step("creating DS keytab", self.__get_ds_keytab)
|
||||||
|
self.step("retriving DS Certificate", self.__get_ds_cert)
|
||||||
|
self.step("restarting directory server", self.__restart_instance)
|
||||||
|
|
||||||
self.step("setting up initial replication", self.__setup_replica)
|
self.step("setting up initial replication", self.__setup_replica)
|
||||||
self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
||||||
@ -374,14 +385,25 @@ class DsInstance(service.Service):
|
|||||||
self.realm,
|
self.realm,
|
||||||
self.dm_password)
|
self.dm_password)
|
||||||
|
|
||||||
|
# Always connect to self over ldapi
|
||||||
|
conn = ipaldap.IPAdmin(self.fqdn, ldapi=True, realm=self.realm)
|
||||||
|
conn.do_external_bind('root')
|
||||||
repl = replication.ReplicationManager(self.realm,
|
repl = replication.ReplicationManager(self.realm,
|
||||||
self.fqdn,
|
self.fqdn,
|
||||||
self.dm_password)
|
self.dm_password, conn=conn)
|
||||||
repl.setup_replication(self.master_fqdn,
|
if self.promote:
|
||||||
r_binddn=DN(('cn', 'Directory Manager')),
|
repl.setup_promote_replication(self.master_fqdn)
|
||||||
r_bindpw=self.dm_password)
|
else:
|
||||||
|
repl.setup_replication(self.master_fqdn,
|
||||||
|
r_binddn=DN(('cn', 'Directory Manager')),
|
||||||
|
r_bindpw=self.dm_password)
|
||||||
self.run_init_memberof = repl.needs_memberof_fixup()
|
self.run_init_memberof = repl.needs_memberof_fixup()
|
||||||
|
|
||||||
|
# Now that the server is up make sure all changes happen against
|
||||||
|
# the local server (as repica pomotion does not have the DM password.
|
||||||
|
if self.admin_conn:
|
||||||
|
self.ldap_disconnect()
|
||||||
|
self.ldapi = True
|
||||||
|
|
||||||
def __configure_sasl_mappings(self):
|
def __configure_sasl_mappings(self):
|
||||||
# we need to remove any existing SASL mappings in the directory as otherwise they
|
# we need to remove any existing SASL mappings in the directory as otherwise they
|
||||||
@ -1128,3 +1150,58 @@ class DsInstance(service.Service):
|
|||||||
# Create global domain level entry and set the domain level
|
# Create global domain level entry and set the domain level
|
||||||
if self.domainlevel is not None:
|
if self.domainlevel is not None:
|
||||||
self._ldap_mod("domainlevel.ldif", self.sub_dict)
|
self._ldap_mod("domainlevel.ldif", self.sub_dict)
|
||||||
|
|
||||||
|
def __get_ds_keytab(self):
|
||||||
|
|
||||||
|
self.fstore.backup_file(paths.DS_KEYTAB)
|
||||||
|
try:
|
||||||
|
os.unlink(paths.DS_KEYTAB)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
installutils.install_service_keytab(self.principal,
|
||||||
|
self.master_fqdn,
|
||||||
|
paths.DS_KEYTAB)
|
||||||
|
|
||||||
|
# Configure DS to use the keytab
|
||||||
|
vardict = {"KRB5_KTNAME": paths.DS_KEYTAB}
|
||||||
|
ipautil.config_replace_variables(paths.SYSCONFIG_DIRSRV,
|
||||||
|
replacevars=vardict)
|
||||||
|
|
||||||
|
# Keytab must be owned by DS itself
|
||||||
|
pent = pwd.getpwnam(DS_USER)
|
||||||
|
os.chown(paths.DS_KEYTAB, pent.pw_uid, pent.pw_gid)
|
||||||
|
|
||||||
|
def __get_ds_cert(self):
|
||||||
|
subject = DN(('O', self.realm))
|
||||||
|
nssdb_dir = config_dirname(self.serverid)
|
||||||
|
db = certs.CertDB(self.realm, nssdir=nssdb_dir, subject_base=subject)
|
||||||
|
db.request_service_cert(self.nickname, self.principal, self.fqdn)
|
||||||
|
db.create_pin_file()
|
||||||
|
|
||||||
|
# Connect to self over ldapi as Directory Manager and configure SSL
|
||||||
|
conn = ipaldap.IPAdmin(self.fqdn, ldapi=True, realm=self.realm)
|
||||||
|
conn.do_external_bind('root')
|
||||||
|
|
||||||
|
mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
|
||||||
|
(ldap.MOD_REPLACE, "nsSSL3Ciphers", "+all"),
|
||||||
|
(ldap.MOD_REPLACE, "allowWeakCipher", "off")]
|
||||||
|
conn.modify_s(DN(('cn', 'encryption'), ('cn', 'config')), mod)
|
||||||
|
|
||||||
|
mod = [(ldap.MOD_ADD, "nsslapd-security", "on")]
|
||||||
|
conn.modify_s(DN(('cn', 'config')), mod)
|
||||||
|
|
||||||
|
entry = conn.make_entry(
|
||||||
|
DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
|
||||||
|
objectclass=["top", "nsEncryptionModule"],
|
||||||
|
cn=["RSA"],
|
||||||
|
nsSSLPersonalitySSL=[self.nickname],
|
||||||
|
nsSSLToken=["internal (software)"],
|
||||||
|
nsSSLActivation=["on"],
|
||||||
|
)
|
||||||
|
conn.add_entry(entry)
|
||||||
|
|
||||||
|
conn.unbind()
|
||||||
|
|
||||||
|
# check for open secure port 636 from now on
|
||||||
|
self.open_ports.append(636)
|
||||||
|
@ -112,7 +112,7 @@ class HTTPInstance(service.Service):
|
|||||||
def create_instance(self, realm, fqdn, domain_name, dm_password=None,
|
def create_instance(self, realm, fqdn, domain_name, dm_password=None,
|
||||||
autoconfig=True, pkcs12_info=None,
|
autoconfig=True, pkcs12_info=None,
|
||||||
subject_base=None, auto_redirect=True, ca_file=None,
|
subject_base=None, auto_redirect=True, ca_file=None,
|
||||||
ca_is_configured=None):
|
ca_is_configured=None, promote=False):
|
||||||
self.fqdn = fqdn
|
self.fqdn = fqdn
|
||||||
self.realm = realm
|
self.realm = realm
|
||||||
self.domain = domain_name
|
self.domain = domain_name
|
||||||
@ -132,6 +132,7 @@ class HTTPInstance(service.Service):
|
|||||||
self.ca_file = ca_file
|
self.ca_file = ca_file
|
||||||
if ca_is_configured is not None:
|
if ca_is_configured is not None:
|
||||||
self.ca_is_configured = ca_is_configured
|
self.ca_is_configured = ca_is_configured
|
||||||
|
self.promote = promote
|
||||||
|
|
||||||
# get a connection to the DS
|
# get a connection to the DS
|
||||||
self.ldap_connect()
|
self.ldap_connect()
|
||||||
@ -147,12 +148,13 @@ class HTTPInstance(service.Service):
|
|||||||
if self.ca_is_configured:
|
if self.ca_is_configured:
|
||||||
self.step("configure certmonger for renewals",
|
self.step("configure certmonger for renewals",
|
||||||
self.configure_certmonger_renewal_guard)
|
self.configure_certmonger_renewal_guard)
|
||||||
|
self.step("setting up httpd keytab", self.__create_http_keytab)
|
||||||
self.step("setting up ssl", self.__setup_ssl)
|
self.step("setting up ssl", self.__setup_ssl)
|
||||||
self.step("importing CA certificates from LDAP", self.__import_ca_certs)
|
self.step("importing CA certificates from LDAP", self.__import_ca_certs)
|
||||||
if autoconfig:
|
if autoconfig:
|
||||||
self.step("setting up browser autoconfig", self.__setup_autoconfig)
|
self.step("setting up browser autoconfig", self.__setup_autoconfig)
|
||||||
self.step("publish CA cert", self.__publish_ca_cert)
|
if not self.promote:
|
||||||
self.step("creating a keytab for httpd", self.__create_http_keytab)
|
self.step("publish CA cert", self.__publish_ca_cert)
|
||||||
self.step("clean up any existing httpd ccache", self.remove_httpd_ccache)
|
self.step("clean up any existing httpd ccache", self.remove_httpd_ccache)
|
||||||
self.step("configuring SELinux for httpd", self.configure_selinux_for_httpd)
|
self.step("configuring SELinux for httpd", self.configure_selinux_for_httpd)
|
||||||
if not self.is_kdcproxy_configured():
|
if not self.is_kdcproxy_configured():
|
||||||
@ -183,10 +185,10 @@ class HTTPInstance(service.Service):
|
|||||||
self.print_msg(e.format_service_warning('web interface'))
|
self.print_msg(e.format_service_warning('web interface'))
|
||||||
|
|
||||||
def __create_http_keytab(self):
|
def __create_http_keytab(self):
|
||||||
installutils.kadmin_addprinc(self.principal)
|
if not self.promote:
|
||||||
installutils.create_keytab(paths.IPA_KEYTAB, self.principal)
|
installutils.kadmin_addprinc(self.principal)
|
||||||
self.move_service(self.principal)
|
installutils.create_keytab(paths.IPA_KEYTAB, self.principal)
|
||||||
self.add_cert_to_service()
|
self.move_service(self.principal)
|
||||||
|
|
||||||
pent = pwd.getpwnam("apache")
|
pent = pwd.getpwnam("apache")
|
||||||
os.chown(paths.IPA_KEYTAB, pent.pw_uid, pent.pw_gid)
|
os.chown(paths.IPA_KEYTAB, pent.pw_uid, pent.pw_gid)
|
||||||
@ -309,14 +311,16 @@ class HTTPInstance(service.Service):
|
|||||||
db.track_server_cert(nickname, self.principal, db.passwd_fname, 'restart_httpd')
|
db.track_server_cert(nickname, self.principal, db.passwd_fname, 'restart_httpd')
|
||||||
|
|
||||||
self.__set_mod_nss_nickname(nickname)
|
self.__set_mod_nss_nickname(nickname)
|
||||||
else:
|
self.add_cert_to_service()
|
||||||
|
|
||||||
|
elif not self.promote:
|
||||||
db.create_password_conf()
|
db.create_password_conf()
|
||||||
self.dercert = db.create_server_cert(self.cert_nickname, self.fqdn,
|
self.dercert = db.create_server_cert(self.cert_nickname, self.fqdn,
|
||||||
ca_db)
|
ca_db)
|
||||||
db.track_server_cert(self.cert_nickname, self.principal,
|
db.track_server_cert(self.cert_nickname, self.principal,
|
||||||
db.passwd_fname, 'restart_httpd')
|
db.passwd_fname, 'restart_httpd')
|
||||||
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
|
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
|
||||||
|
self.add_cert_to_service()
|
||||||
|
|
||||||
# Fix the database permissions
|
# Fix the database permissions
|
||||||
os.chmod(certs.NSS_DIR + "/cert8.db", 0o660)
|
os.chmod(certs.NSS_DIR + "/cert8.db", 0o660)
|
||||||
|
@ -47,12 +47,14 @@ from ipapython.admintool import ScriptError
|
|||||||
from ipapython.ipa_log_manager import root_logger, log_mgr
|
from ipapython.ipa_log_manager import root_logger, log_mgr
|
||||||
from ipalib.util import validate_hostname
|
from ipalib.util import validate_hostname
|
||||||
from ipapython import config
|
from ipapython import config
|
||||||
from ipalib import errors, x509
|
from ipalib import api, errors, x509
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipaserver.install import certs, service, sysupgrade
|
from ipaserver.install import certs, service, sysupgrade
|
||||||
from ipaplatform import services
|
from ipaplatform import services
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipaplatform.tasks import tasks
|
from ipaplatform.tasks import tasks
|
||||||
|
from ipapython import certmonger
|
||||||
|
|
||||||
|
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
unicode = str
|
unicode = str
|
||||||
@ -1115,3 +1117,28 @@ def enable_and_start_oddjobd(sstore):
|
|||||||
oddjobd.start()
|
oddjobd.start()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
root_logger.critical("Unable to start oddjobd: {0}".format(str(e)))
|
root_logger.critical("Unable to start oddjobd: {0}".format(str(e)))
|
||||||
|
|
||||||
|
|
||||||
|
def install_service_keytab(principal, server, path):
|
||||||
|
|
||||||
|
try:
|
||||||
|
api.Backend.rpcclient.connect()
|
||||||
|
|
||||||
|
# Create services if none exists (we use the .forward method
|
||||||
|
# here so that we can control the client version number and avoid
|
||||||
|
# errors. This is a workaround until the API becomes version
|
||||||
|
# independent: FIXME
|
||||||
|
|
||||||
|
api.Backend.rpcclient.forward(
|
||||||
|
'service_add',
|
||||||
|
krbprincipalname=principal,
|
||||||
|
version=u'2.112' # All the way back to 3.0 servers
|
||||||
|
)
|
||||||
|
except errors.DuplicateEntry:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
if api.Backend.rpcclient.isconnected():
|
||||||
|
api.Backend.rpcclient.disconnect()
|
||||||
|
|
||||||
|
args = [paths.IPA_GETKEYTAB, '-k', path, '-p', principal, '-s', server]
|
||||||
|
ipautil.run(args)
|
||||||
|
@ -173,7 +173,7 @@ class KrbInstance(service.Service):
|
|||||||
master_fqdn, host_name,
|
master_fqdn, host_name,
|
||||||
domain_name, admin_password,
|
domain_name, admin_password,
|
||||||
setup_pkinit=False, pkcs12_info=None,
|
setup_pkinit=False, pkcs12_info=None,
|
||||||
subject_base=None):
|
subject_base=None, promote=False):
|
||||||
self.pkcs12_info = pkcs12_info
|
self.pkcs12_info = pkcs12_info
|
||||||
self.subject_base = subject_base
|
self.subject_base = subject_base
|
||||||
self.master_fqdn = master_fqdn
|
self.master_fqdn = master_fqdn
|
||||||
@ -181,12 +181,17 @@ class KrbInstance(service.Service):
|
|||||||
self.__common_setup(realm_name, host_name, domain_name, admin_password)
|
self.__common_setup(realm_name, host_name, domain_name, admin_password)
|
||||||
|
|
||||||
self.step("configuring KDC", self.__configure_instance)
|
self.step("configuring KDC", self.__configure_instance)
|
||||||
self.step("creating a keytab for the directory", self.__create_ds_keytab)
|
if not promote:
|
||||||
self.step("creating a keytab for the machine", self.__create_host_keytab)
|
self.step("creating a keytab for the directory",
|
||||||
|
self.__create_ds_keytab)
|
||||||
|
self.step("creating a keytab for the machine",
|
||||||
|
self.__create_host_keytab)
|
||||||
self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
|
self.step("adding the password extension to the directory", self.__add_pwd_extop_module)
|
||||||
if setup_pkinit:
|
if setup_pkinit:
|
||||||
self.step("installing X509 Certificate for PKINIT", self.__setup_pkinit)
|
self.step("installing X509 Certificate for PKINIT", self.__setup_pkinit)
|
||||||
self.step("enable GSSAPI for replication", self.__convert_to_gssapi_replication)
|
if not promote:
|
||||||
|
self.step("enable GSSAPI for replication",
|
||||||
|
self.__convert_to_gssapi_replication)
|
||||||
|
|
||||||
self.__common_post_setup()
|
self.__common_post_setup()
|
||||||
|
|
||||||
|
@ -1605,6 +1605,96 @@ class ReplicationManager(object):
|
|||||||
except errors.EmptyModlist:
|
except errors.EmptyModlist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def join_replication_managers(self, conn):
|
||||||
|
"""
|
||||||
|
Create a pseudo user to use for replication.
|
||||||
|
"""
|
||||||
|
dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
|
||||||
|
('cn', 'etc'), self.suffix)
|
||||||
|
mydn = DN(('krbprincipalname', 'ldap/%s@%s' % (self.hostname,
|
||||||
|
self.realm)),
|
||||||
|
('cn', 'services'), ('cn', 'accounts'), self.suffix)
|
||||||
|
|
||||||
|
entry = conn.get_entry(dn)
|
||||||
|
if mydn not in entry['member']:
|
||||||
|
entry['member'].append(mydn)
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn.update_entry(entry)
|
||||||
|
except errors.EmptyModlist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_temp_sasl_mapping(self, conn, r_hostname):
|
||||||
|
"""
|
||||||
|
Create a special user to let SASL Mapping find a valid user
|
||||||
|
on first replication.
|
||||||
|
"""
|
||||||
|
name = 'ldap/%s@%s' % (r_hostname, self.realm)
|
||||||
|
replica_binddn = DN(('cn', name), ('cn', 'config'))
|
||||||
|
entry = conn.make_entry(
|
||||||
|
replica_binddn,
|
||||||
|
objectclass=["top", "person"],
|
||||||
|
cn=[name],
|
||||||
|
sn=["replication manager pseudo user"]
|
||||||
|
)
|
||||||
|
conn.add_entry(entry)
|
||||||
|
|
||||||
|
entry = conn.get_entry(self.replica_dn())
|
||||||
|
entry['nsDS5ReplicaBindDN'].append(replica_binddn)
|
||||||
|
conn.update_entry(entry)
|
||||||
|
|
||||||
|
entry = conn.make_entry(
|
||||||
|
DN(('cn', 'Peer Master'), ('cn', 'mapping'), ('cn', 'sasl'),
|
||||||
|
('cn', 'config')),
|
||||||
|
objectclass=["top", "nsSaslMapping"],
|
||||||
|
cn=["Peer Master"],
|
||||||
|
nsSaslMapRegexString=['^[^:@]+$'],
|
||||||
|
nsSaslMapBaseDNTemplate=[DN(('cn', 'config'))],
|
||||||
|
nsSaslMapFilterTemplate=['(cn=&@%s)' % self.realm],
|
||||||
|
nsSaslMapPriority=['1'],
|
||||||
|
)
|
||||||
|
conn.add_entry(entry)
|
||||||
|
|
||||||
|
def remove_temp_replication_user(self, conn, r_hostname):
|
||||||
|
"""
|
||||||
|
Remove the special SASL Mapping user created in a previous step.
|
||||||
|
"""
|
||||||
|
name = 'ldap/%s@%s' % (r_hostname, self.realm)
|
||||||
|
replica_binddn = DN(('cn', name), ('cn', 'config'))
|
||||||
|
conn.delete_entry(replica_binddn)
|
||||||
|
|
||||||
|
entry = conn.get_entry(self.replica_dn())
|
||||||
|
while replica_binddn in entry['nsDS5ReplicaBindDN']:
|
||||||
|
entry['nsDS5ReplicaBindDN'].remove(replica_binddn)
|
||||||
|
conn.update_entry(entry)
|
||||||
|
|
||||||
|
def setup_promote_replication(self, r_hostname):
|
||||||
|
# note - there appears to be a bug in python-ldap - it does not
|
||||||
|
# allow connections using two different CA certs
|
||||||
|
r_conn = ipaldap.IPAdmin(r_hostname, port=389, protocol='ldap')
|
||||||
|
r_conn.do_sasl_gssapi_bind()
|
||||||
|
|
||||||
|
# Setup the first half
|
||||||
|
l_id = self._get_replica_id(self.conn, r_conn)
|
||||||
|
self.basic_replication_setup(self.conn, l_id, self.repl_man_dn, None)
|
||||||
|
self.add_temp_sasl_mapping(self.conn, r_hostname)
|
||||||
|
|
||||||
|
# Now setup the other half
|
||||||
|
r_id = self._get_replica_id(r_conn, r_conn)
|
||||||
|
self.basic_replication_setup(r_conn, r_id, self.repl_man_dn, None)
|
||||||
|
self.join_replication_managers(r_conn)
|
||||||
|
|
||||||
|
self.setup_agreement(r_conn, self.conn.host, isgssapi=True)
|
||||||
|
self.setup_agreement(self.conn, r_hostname, isgssapi=True)
|
||||||
|
|
||||||
|
# Finally start replication
|
||||||
|
ret = self.start_replication(r_conn, master=False)
|
||||||
|
if ret != 0:
|
||||||
|
raise RuntimeError("Failed to start replication")
|
||||||
|
|
||||||
|
self.remove_temp_replication_user(self.conn, r_hostname)
|
||||||
|
|
||||||
|
|
||||||
class CSReplicationManager(ReplicationManager):
|
class CSReplicationManager(ReplicationManager):
|
||||||
"""ReplicationManager specific to CA agreements
|
"""ReplicationManager specific to CA agreements
|
||||||
|
|
||||||
|
@ -814,10 +814,8 @@ def install(installer):
|
|||||||
otpd.create_instance('OTPD', host_name, dm_password,
|
otpd.create_instance('OTPD', host_name, dm_password,
|
||||||
ipautil.realm_to_suffix(realm_name))
|
ipautil.realm_to_suffix(realm_name))
|
||||||
|
|
||||||
custodia = custodiainstance.CustodiaInstance()
|
custodia = custodiainstance.CustodiaInstance(host_name, realm_name)
|
||||||
custodia.create_instance('KEYS', host_name, dm_password,
|
custodia.create_instance(dm_password)
|
||||||
ipautil.realm_to_suffix(realm_name),
|
|
||||||
realm_name)
|
|
||||||
|
|
||||||
# Create a HTTP instance
|
# Create a HTTP instance
|
||||||
http = httpinstance.HTTPInstance(fstore)
|
http = httpinstance.HTTPInstance(fstore)
|
||||||
|
@ -8,13 +8,15 @@ import dns.exception as dnsexception
|
|||||||
import dns.name as dnsname
|
import dns.name as dnsname
|
||||||
import dns.resolver as dnsresolver
|
import dns.resolver as dnsresolver
|
||||||
import dns.reversename as dnsreversename
|
import dns.reversename as dnsreversename
|
||||||
|
import getpass
|
||||||
|
import gssapi
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from ipapython import dogtag, ipautil, sysrestore
|
from ipapython import certmonger, dogtag, ipaldap, ipautil, sysrestore
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipapython.install import common, core
|
from ipapython.install import common, core
|
||||||
from ipapython.install.common import step
|
from ipapython.install.common import step
|
||||||
@ -24,14 +26,19 @@ from ipaplatform import services
|
|||||||
from ipaplatform.tasks import tasks
|
from ipaplatform.tasks import tasks
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipalib import api, certstore, constants, create_api, errors, x509
|
from ipalib import api, certstore, constants, create_api, errors, x509
|
||||||
|
import ipaclient.ipachangeconf
|
||||||
import ipaclient.ntpconf
|
import ipaclient.ntpconf
|
||||||
from ipaserver.install import (
|
from ipaserver.install import (
|
||||||
bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance,
|
bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance,
|
||||||
installutils, kra, krbinstance, memcacheinstance, ntpinstance,
|
installutils, kra, krbinstance, memcacheinstance, ntpinstance,
|
||||||
otpdinstance, custodiainstance, service)
|
otpdinstance, custodiainstance, service)
|
||||||
from ipaserver.install.installutils import create_replica_config
|
from ipaserver.install.installutils import create_replica_config
|
||||||
|
from ipaserver.install.installutils import ReplicaConfig
|
||||||
from ipaserver.install.replication import (
|
from ipaserver.install.replication import (
|
||||||
ReplicationManager, replica_conn_check)
|
ReplicationManager, replica_conn_check)
|
||||||
|
import SSSDConfig
|
||||||
|
from subprocess import CalledProcessError
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
from .common import BaseServer
|
from .common import BaseServer
|
||||||
|
|
||||||
@ -60,7 +67,29 @@ def make_pkcs12_info(directory, cert_name, password_name):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def install_replica_ds(config):
|
def install_http_certs(config, fstore):
|
||||||
|
|
||||||
|
# Obtain keytab for the HTTP service
|
||||||
|
fstore.backup_file(paths.IPA_KEYTAB)
|
||||||
|
try:
|
||||||
|
os.unlink(paths.IPA_KEYTAB)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
principal = 'HTTP/%s@%s' % (config.host_name, config.realm_name)
|
||||||
|
installutils.install_service_keytab(principal,
|
||||||
|
config.master_host_name,
|
||||||
|
paths.IPA_KEYTAB)
|
||||||
|
|
||||||
|
# Obtain certificate for the HTTP service
|
||||||
|
nssdir = certs.NSS_DIR
|
||||||
|
subject = DN(('O', config.realm_name))
|
||||||
|
db = certs.CertDB(config.realm_name, nssdir=nssdir, subject_base=subject)
|
||||||
|
db.request_service_cert('Server-Cert', principal, config.host_name, True)
|
||||||
|
# FIXME: need Signing-Cert too ?
|
||||||
|
|
||||||
|
|
||||||
|
def install_replica_ds(config, promote=False):
|
||||||
dsinstance.check_ports()
|
dsinstance.check_ports()
|
||||||
|
|
||||||
# if we have a pkcs12 file, create the cert db from
|
# if we have a pkcs12 file, create the cert db from
|
||||||
@ -79,12 +108,13 @@ def install_replica_ds(config):
|
|||||||
pkcs12_info=pkcs12_info,
|
pkcs12_info=pkcs12_info,
|
||||||
ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"),
|
ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"),
|
||||||
ca_file=config.dir + "/ca.crt",
|
ca_file=config.dir + "/ca.crt",
|
||||||
|
promote=promote,
|
||||||
)
|
)
|
||||||
|
|
||||||
return ds
|
return ds
|
||||||
|
|
||||||
|
|
||||||
def install_krb(config, setup_pkinit=False):
|
def install_krb(config, setup_pkinit=False, promote=False):
|
||||||
krb = krbinstance.KrbInstance()
|
krb = krbinstance.KrbInstance()
|
||||||
|
|
||||||
# pkinit files
|
# pkinit files
|
||||||
@ -94,7 +124,7 @@ def install_krb(config, setup_pkinit=False):
|
|||||||
krb.create_replica(config.realm_name,
|
krb.create_replica(config.realm_name,
|
||||||
config.master_host_name, config.host_name,
|
config.master_host_name, config.host_name,
|
||||||
config.domain_name, config.dirman_password,
|
config.domain_name, config.dirman_password,
|
||||||
setup_pkinit, pkcs12_info)
|
setup_pkinit, pkcs12_info, promote=promote)
|
||||||
|
|
||||||
return krb
|
return krb
|
||||||
|
|
||||||
@ -115,7 +145,7 @@ def install_ca_cert(ldap, base_dn, realm, cafile):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def install_http(config, auto_redirect):
|
def install_http(config, auto_redirect, promote=False):
|
||||||
# if we have a pkcs12 file, create the cert db from
|
# if we have a pkcs12 file, create the cert db from
|
||||||
# that. Otherwise the ds setup will create the CA
|
# that. Otherwise the ds setup will create the CA
|
||||||
# cert
|
# cert
|
||||||
@ -131,7 +161,8 @@ def install_http(config, auto_redirect):
|
|||||||
config.realm_name, config.host_name, config.domain_name,
|
config.realm_name, config.host_name, config.domain_name,
|
||||||
config.dirman_password, False, pkcs12_info,
|
config.dirman_password, False, pkcs12_info,
|
||||||
auto_redirect=auto_redirect, ca_file=config.dir + "/ca.crt",
|
auto_redirect=auto_redirect, ca_file=config.dir + "/ca.crt",
|
||||||
ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"))
|
ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"),
|
||||||
|
promote=promote)
|
||||||
|
|
||||||
# Now copy the autoconfiguration files
|
# Now copy the autoconfiguration files
|
||||||
try:
|
try:
|
||||||
@ -153,9 +184,10 @@ def install_http(config, auto_redirect):
|
|||||||
def install_dns_records(config, options, remote_api):
|
def install_dns_records(config, options, remote_api):
|
||||||
|
|
||||||
if not bindinstance.dns_container_exists(
|
if not bindinstance.dns_container_exists(
|
||||||
config.master_host_name,
|
config.host_name,
|
||||||
ipautil.realm_to_suffix(config.realm_name),
|
ipautil.realm_to_suffix(config.realm_name),
|
||||||
dm_password=config.dirman_password):
|
realm=config.realm_name, ldapi=True,
|
||||||
|
autobind=ipaldap.AUTOBIND_ENABLED):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -283,6 +315,43 @@ def check_dns_resolution(host_name, dns_servers):
|
|||||||
return no_errors
|
return no_errors
|
||||||
|
|
||||||
|
|
||||||
|
def check_ca_enabled(api):
|
||||||
|
try:
|
||||||
|
api.Backend.rpcclient.connect()
|
||||||
|
result = api.Backend.rpcclient.forward(
|
||||||
|
'ca_is_enabled',
|
||||||
|
version=u'2.112' # All the way back to 3.0 servers
|
||||||
|
)
|
||||||
|
return result['result']
|
||||||
|
finally:
|
||||||
|
if api.Backend.rpcclient.isconnected():
|
||||||
|
api.Backend.rpcclient.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
def configure_certmonger():
|
||||||
|
messagebus = services.knownservices.messagebus
|
||||||
|
try:
|
||||||
|
messagebus.start()
|
||||||
|
except Exception, e:
|
||||||
|
print("Messagebus service unavailable: %s" % str(e))
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
# Ensure that certmonger has been started at least once to generate the
|
||||||
|
# cas files in /var/lib/certmonger/cas.
|
||||||
|
cmonger = services.knownservices.certmonger
|
||||||
|
try:
|
||||||
|
cmonger.restart()
|
||||||
|
except Exception, e:
|
||||||
|
print("Certmonger service unavailable: %s" % str(e))
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmonger.enable()
|
||||||
|
except Exception, e:
|
||||||
|
print("Failed to enable Certmonger: %s" % str(e))
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
|
||||||
def remove_replica_info_dir(installer):
|
def remove_replica_info_dir(installer):
|
||||||
# always try to remove decrypted replica file
|
# always try to remove decrypted replica file
|
||||||
try:
|
try:
|
||||||
@ -311,6 +380,37 @@ def common_cleanup(func):
|
|||||||
return decorated
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
|
def promote_sssd(host_name):
|
||||||
|
sssdconfig = SSSDConfig.SSSDConfig()
|
||||||
|
sssdconfig.import_config()
|
||||||
|
domains = sssdconfig.list_active_domains()
|
||||||
|
|
||||||
|
ipa_domain = None
|
||||||
|
|
||||||
|
for name in domains:
|
||||||
|
domain = sssdconfig.get_domain(name)
|
||||||
|
try:
|
||||||
|
hostname = domain.get_option('ipa_hostname')
|
||||||
|
if hostname == host_name:
|
||||||
|
ipa_domain = domain
|
||||||
|
except SSSDConfig.NoOptionError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ipa_domain is None:
|
||||||
|
raise RuntimeError("Couldn't find IPA domain in sssd.conf")
|
||||||
|
else:
|
||||||
|
domain.set_option('ipa_server', host_name)
|
||||||
|
domain.set_option('ipa_server_mode', True)
|
||||||
|
sssdconfig.save_domain(domain)
|
||||||
|
sssdconfig.write()
|
||||||
|
|
||||||
|
sssd = services.service('sssd')
|
||||||
|
try:
|
||||||
|
sssd.restart()
|
||||||
|
except CalledProcessError:
|
||||||
|
root_logger.warning("SSSD service restart was unsuccessful.")
|
||||||
|
|
||||||
|
|
||||||
@common_cleanup
|
@common_cleanup
|
||||||
def install_check(installer):
|
def install_check(installer):
|
||||||
options = installer
|
options = installer
|
||||||
@ -433,6 +533,14 @@ def install_check(installer):
|
|||||||
# available
|
# available
|
||||||
current = 0
|
current = 0
|
||||||
|
|
||||||
|
if current != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
"You cannot use a replica file to join a replica when the "
|
||||||
|
"domain is above level 0. Please join the system to the "
|
||||||
|
"domain by running ipa-client-install first, the try again "
|
||||||
|
"without a replica file."
|
||||||
|
)
|
||||||
|
|
||||||
# Detect if current level is out of supported range
|
# Detect if current level is out of supported range
|
||||||
# for this IPA version
|
# for this IPA version
|
||||||
under_lower_bound = current < constants.MIN_DOMAIN_LEVEL
|
under_lower_bound = current < constants.MIN_DOMAIN_LEVEL
|
||||||
@ -596,12 +704,9 @@ def install(installer):
|
|||||||
CA.import_ra_cert(config.dir + "/ra.p12")
|
CA.import_ra_cert(config.dir + "/ra.p12")
|
||||||
CA.fix_ra_perms()
|
CA.fix_ra_perms()
|
||||||
|
|
||||||
# FIXME: must be done earlier in replica to fetch keys for CA/ldap server
|
custodia = custodiainstance.CustodiaInstance(config.host_name,
|
||||||
# before they are configured
|
config.realm_name)
|
||||||
custodia = custodiainstance.CustodiaInstance()
|
custodia.create_instance(config.dirman_password)
|
||||||
custodia.create_instance('KEYS', config.host_name,
|
|
||||||
config.dirman_password,
|
|
||||||
ipautil.realm_to_suffix(config.realm_name))
|
|
||||||
|
|
||||||
# The DS instance is created before the keytab, add the SSL cert we
|
# The DS instance is created before the keytab, add the SSL cert we
|
||||||
# generated
|
# generated
|
||||||
@ -662,6 +767,412 @@ def install(installer):
|
|||||||
remove_replica_info_dir(installer)
|
remove_replica_info_dir(installer)
|
||||||
|
|
||||||
|
|
||||||
|
@common_cleanup
|
||||||
|
def promote_check(installer):
|
||||||
|
options = installer
|
||||||
|
|
||||||
|
# FIXME: to implement yet
|
||||||
|
if options.setup_ca:
|
||||||
|
raise NotImplementedError
|
||||||
|
if options.setup_kra:
|
||||||
|
raise NotImplementedError
|
||||||
|
if options.setup_dns:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
tasks.check_selinux_status()
|
||||||
|
|
||||||
|
client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||||
|
if not client_fstore.has_files():
|
||||||
|
sys.exit("IPA client is not configured on this system.\n"
|
||||||
|
"You must use a replica file or join the system "
|
||||||
|
"using 'ipa-client-install'.")
|
||||||
|
|
||||||
|
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||||
|
|
||||||
|
fstore = sysrestore.FileStore(paths.SYSRESTORE)
|
||||||
|
|
||||||
|
# Check to see if httpd is already configured to listen on 443
|
||||||
|
if httpinstance.httpd_443_configured():
|
||||||
|
sys.exit("Aborting installation")
|
||||||
|
|
||||||
|
check_dirsrv()
|
||||||
|
|
||||||
|
if not options.no_ntp:
|
||||||
|
try:
|
||||||
|
ipaclient.ntpconf.check_timedate_services()
|
||||||
|
except ipaclient.ntpconf.NTPConflictingService, e:
|
||||||
|
print("WARNING: conflicting time&date synchronization service '%s'"
|
||||||
|
" will" % e.conflicting_service)
|
||||||
|
print("be disabled in favor of ntpd")
|
||||||
|
print("")
|
||||||
|
except ipaclient.ntpconf.NTPConfigurationError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
api.bootstrap(context='installer')
|
||||||
|
api.finalize()
|
||||||
|
|
||||||
|
config = ReplicaConfig()
|
||||||
|
config.realm_name = api.env.realm
|
||||||
|
config.host_name = api.env.host
|
||||||
|
config.domain_name = api.env.domain
|
||||||
|
config.master_host_name = api.env.server
|
||||||
|
config.setup_ca = options.setup_ca
|
||||||
|
config.setup_kra = options.setup_kra
|
||||||
|
|
||||||
|
installutils.verify_fqdn(config.host_name, options.no_host_dns)
|
||||||
|
installutils.verify_fqdn(config.master_host_name, options.no_host_dns)
|
||||||
|
|
||||||
|
# Check if ccache is available
|
||||||
|
try:
|
||||||
|
root_logger.debug('KRB5CCNAME set to %s' %
|
||||||
|
os.environ.get('KRB5CCNAME', None))
|
||||||
|
# get default creds, will raise if none found
|
||||||
|
default_cred = gssapi.creds.Credentials()
|
||||||
|
principal = str(default_cred.name)
|
||||||
|
except gssapi.raw.misc.GSSError as e:
|
||||||
|
root_logger.debug('Failed to find default ccache: %s' % e)
|
||||||
|
principal = None
|
||||||
|
|
||||||
|
# Check if the principal matches the requested one (if any)
|
||||||
|
if principal is not None and options.principal is not None:
|
||||||
|
op = options.principal
|
||||||
|
if op.find('@') == -1:
|
||||||
|
op = '%s@%s' % (op, config.realm_name)
|
||||||
|
if principal != op:
|
||||||
|
root_logger.debug('Specified principal %s does not match '
|
||||||
|
'available credentials (%s)' %
|
||||||
|
(options.principal, principal))
|
||||||
|
principal = None
|
||||||
|
|
||||||
|
if principal is None:
|
||||||
|
(ccache_fd, ccache_name) = tempfile.mkstemp()
|
||||||
|
os.close(ccache_fd)
|
||||||
|
|
||||||
|
if options.principal is not None:
|
||||||
|
principal = options.principal
|
||||||
|
else:
|
||||||
|
principal = 'admin'
|
||||||
|
stdin = None
|
||||||
|
if principal.find('@') == -1:
|
||||||
|
principal = '%s@%s' % (principal, config.realm_name)
|
||||||
|
if options.password is not None:
|
||||||
|
stdin = options.password
|
||||||
|
else:
|
||||||
|
if not options.unattended:
|
||||||
|
try:
|
||||||
|
stdin = getpass.getpass("Password for %s: " % principal)
|
||||||
|
except EOFError:
|
||||||
|
stdin = None
|
||||||
|
if not stdin:
|
||||||
|
raise RuntimeError("Password must be provided for %s."
|
||||||
|
% principal)
|
||||||
|
else:
|
||||||
|
if sys.stdin.isatty():
|
||||||
|
root_logger.info("Password must be provided in " +
|
||||||
|
"non-interactive mode. " +
|
||||||
|
"This can be done via " +
|
||||||
|
"echo password | ipa-client-install " +
|
||||||
|
"... or with the -w option.")
|
||||||
|
raise RuntimeError("Password must be provided in " +
|
||||||
|
"non-interactive mode.")
|
||||||
|
else:
|
||||||
|
stdin = sys.stdin.readline()
|
||||||
|
|
||||||
|
try:
|
||||||
|
ipautil.kinit_password(principal, stdin, ccache_name)
|
||||||
|
except RuntimeError as e:
|
||||||
|
raise RuntimeError("Kerberos authentication failed: %s" % e)
|
||||||
|
|
||||||
|
os.environ['KRB5CCNAME'] = ccache_name
|
||||||
|
|
||||||
|
cafile = paths.IPA_CA_CRT
|
||||||
|
if not ipautil.file_exists(cafile):
|
||||||
|
raise RuntimeError("CA cert file is not available! Please reinstall"
|
||||||
|
"the client and try again.")
|
||||||
|
|
||||||
|
ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
|
||||||
|
remote_api = create_api(mode=None)
|
||||||
|
remote_api.bootstrap(in_server=True, context='installer',
|
||||||
|
ldap_uri=ldapuri)
|
||||||
|
remote_api.finalize()
|
||||||
|
conn = remote_api.Backend.ldap2
|
||||||
|
replman = None
|
||||||
|
try:
|
||||||
|
# Try out authentication
|
||||||
|
conn.connect(ccache=os.environ.get('KRB5CCNAME'))
|
||||||
|
replman = ReplicationManager(config.realm_name,
|
||||||
|
config.master_host_name, None)
|
||||||
|
|
||||||
|
# Check that we don't already have a replication agreement
|
||||||
|
try:
|
||||||
|
(acn, adn) = replman.agreement_dn(config.host_name)
|
||||||
|
entry = conn.get_entry(adn, ['*'])
|
||||||
|
except errors.NotFound:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
root_logger.info('Error: A replication agreement for this '
|
||||||
|
'host already exists.')
|
||||||
|
print('A replication agreement for this host already exists. '
|
||||||
|
'It needs to be removed.')
|
||||||
|
print("Run this command:")
|
||||||
|
print(" %% ipa-replica-manage del %s --force" %
|
||||||
|
config.host_name)
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
# Detect the current domain level
|
||||||
|
try:
|
||||||
|
current = remote_api.Command['domainlevel_get']()['result']
|
||||||
|
except errors.NotFound:
|
||||||
|
# If we're joining an older master, domain entry is not
|
||||||
|
# available
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
if current == 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
"You must provide a file generated by ipa-replica-prepare to "
|
||||||
|
"create a replica when the domain is at level 0."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Detect if current level is out of supported range
|
||||||
|
# for this IPA version
|
||||||
|
under_lower_bound = current < constants.MIN_DOMAIN_LEVEL
|
||||||
|
above_upper_bound = current > constants.MAX_DOMAIN_LEVEL
|
||||||
|
|
||||||
|
if under_lower_bound or above_upper_bound:
|
||||||
|
message = ("This version of FreeIPA does not support "
|
||||||
|
"the Domain Level which is currently set for "
|
||||||
|
"this domain. The Domain Level needs to be "
|
||||||
|
"raised before installing a replica with "
|
||||||
|
"this version is allowed to be installed "
|
||||||
|
"within this domain.")
|
||||||
|
root_logger.error(message)
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
# Detect if the other master can handle replication managers
|
||||||
|
# cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX
|
||||||
|
dn = DN(('cn', 'replication managers'), ('cn', 'sysaccounts'),
|
||||||
|
('cn', 'etc'), ipautil.realm_to_suffix(config.realm_name))
|
||||||
|
try:
|
||||||
|
entry = conn.get_entry(dn)
|
||||||
|
except errors.NotFound:
|
||||||
|
msg = ("The Replication Managers group is not available in "
|
||||||
|
"the domain. Replica promotion requires the use of "
|
||||||
|
"Replication Managers to be able to replicate data. "
|
||||||
|
"Upgrade the peer master or use the ipa-replica-prepare "
|
||||||
|
"command on the master and use a prep file to install "
|
||||||
|
"this replica.")
|
||||||
|
root_logger.error(msg)
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
dns_masters = remote_api.Object['dnsrecord'].get_dns_masters()
|
||||||
|
if dns_masters:
|
||||||
|
if not options.no_host_dns:
|
||||||
|
root_logger.debug('Check forward/reverse DNS resolution')
|
||||||
|
resolution_ok = (
|
||||||
|
check_dns_resolution(config.master_host_name,
|
||||||
|
dns_masters) and
|
||||||
|
check_dns_resolution(config.host_name, dns_masters))
|
||||||
|
if not resolution_ok and installer.interactive:
|
||||||
|
if not ipautil.user_input("Continue?", False):
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
root_logger.debug('No IPA DNS servers, '
|
||||||
|
'skipping forward/reverse resolution check')
|
||||||
|
|
||||||
|
entry_attrs = conn.get_ipa_config()
|
||||||
|
subject_base = entry_attrs.get('ipacertificatesubjectbase', [None])[0]
|
||||||
|
if subject_base is not None:
|
||||||
|
config.subject_base = DN(subject_base)
|
||||||
|
|
||||||
|
# Find if any server has a CA
|
||||||
|
ca_host = cainstance.find_ca_server(api.env.server, conn)
|
||||||
|
if ca_host is not None:
|
||||||
|
config.ca_host_name = ca_host
|
||||||
|
ca_enabled = True
|
||||||
|
else:
|
||||||
|
# FIXME: add way to pass in certificates
|
||||||
|
root_logger.error("The remote master does not have a CA "
|
||||||
|
"installed, can't proceed without certs")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
if options.setup_ca:
|
||||||
|
if not ca_enabled:
|
||||||
|
root_logger.error("The remote master does not have a CA "
|
||||||
|
"installed, can't set up CA")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
options.realm_name = config.realm_name
|
||||||
|
options.host_name = config.host_name
|
||||||
|
options.subject = config.subject_base
|
||||||
|
ca.install_check(False, None, options)
|
||||||
|
|
||||||
|
if config.setup_kra:
|
||||||
|
try:
|
||||||
|
kra.install_check(remote_api, config, options)
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(str(e))
|
||||||
|
sys.exit(1)
|
||||||
|
except errors.ACIError:
|
||||||
|
sys.exit("\nInsufficiently privileges to promote the server.")
|
||||||
|
except errors.LDAPError:
|
||||||
|
sys.exit("\nUnable to connect to LDAP server %s" %
|
||||||
|
config.master_host_name)
|
||||||
|
finally:
|
||||||
|
if replman and replman.conn:
|
||||||
|
replman.conn.unbind()
|
||||||
|
if conn.isconnected():
|
||||||
|
conn.disconnect()
|
||||||
|
|
||||||
|
if options.setup_dns:
|
||||||
|
dns.install_check(False, True, options, config.host_name)
|
||||||
|
else:
|
||||||
|
config.ips = installutils.get_server_ip_address(
|
||||||
|
config.host_name, not installer.interactive,
|
||||||
|
False, options.ip_addresses)
|
||||||
|
|
||||||
|
# check connection
|
||||||
|
if not options.skip_conncheck:
|
||||||
|
replica_conn_check(
|
||||||
|
config.master_host_name, config.host_name, config.realm_name,
|
||||||
|
options.setup_ca, dogtag.Dogtag10Constants.DS_PORT)
|
||||||
|
|
||||||
|
if not ipautil.file_exists(cafile):
|
||||||
|
raise RuntimeError("CA cert file is not available.")
|
||||||
|
|
||||||
|
installer._ca_enabled = ca_enabled
|
||||||
|
installer._remote_api = remote_api
|
||||||
|
installer._fstore = fstore
|
||||||
|
installer._sstore = sstore
|
||||||
|
installer._config = config
|
||||||
|
|
||||||
|
|
||||||
|
@common_cleanup
|
||||||
|
def promote(installer):
|
||||||
|
options = installer
|
||||||
|
fstore = installer._fstore
|
||||||
|
sstore = installer._sstore
|
||||||
|
config = installer._config
|
||||||
|
|
||||||
|
# Save client file and merge in server directives
|
||||||
|
target_fname = paths.IPA_DEFAULT_CONF
|
||||||
|
fstore.backup_file(target_fname)
|
||||||
|
ipaconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Replica Promote")
|
||||||
|
ipaconf.setOptionAssignment(" = ")
|
||||||
|
ipaconf.setSectionNameDelimiters(("[", "]"))
|
||||||
|
|
||||||
|
config.promote = installer.promote
|
||||||
|
config.dirman_password = hexlify(ipautil.ipa_generate_password())
|
||||||
|
|
||||||
|
dogtag_constants = dogtag.install_constants
|
||||||
|
|
||||||
|
# FIXME: allow to use passed in certs instead
|
||||||
|
if installer._ca_enabled:
|
||||||
|
configure_certmonger()
|
||||||
|
|
||||||
|
# Create DS user/group if it doesn't exist yet
|
||||||
|
dsinstance.create_ds_user()
|
||||||
|
|
||||||
|
# Configure ntpd
|
||||||
|
if not options.no_ntp:
|
||||||
|
ipaclient.ntpconf.force_ntpd(sstore)
|
||||||
|
ntp = ntpinstance.NTPInstance()
|
||||||
|
ntp.create_instance()
|
||||||
|
|
||||||
|
# Configure dirsrv
|
||||||
|
ds = install_replica_ds(config, promote=True)
|
||||||
|
|
||||||
|
# Always try to install DNS records
|
||||||
|
install_dns_records(config, options, api)
|
||||||
|
|
||||||
|
# Must install http certs before changing ipa configuration file
|
||||||
|
# or certmonger will fail to contact the peer master
|
||||||
|
install_http_certs(config, fstore)
|
||||||
|
|
||||||
|
# Create the management framework config file
|
||||||
|
gopts = [
|
||||||
|
ipaconf.setOption('host', config.host_name),
|
||||||
|
ipaconf.rmOption('server'),
|
||||||
|
ipaconf.setOption('xmlrpc_uri',
|
||||||
|
'https://%s/ipa/xml' %
|
||||||
|
ipautil.format_netloc(config.host_name)),
|
||||||
|
ipaconf.setOption('ldap_uri',
|
||||||
|
installutils.realm_to_ldapi_uri(config.realm_name)),
|
||||||
|
ipaconf.setOption('mode', 'production'),
|
||||||
|
ipaconf.setOption('enable_ra', 'True'),
|
||||||
|
ipaconf.setOption('ra_plugin', 'dogtag'),
|
||||||
|
ipaconf.setOption('dogtag_version',
|
||||||
|
dogtag.install_constants.DOGTAG_VERSION)]
|
||||||
|
opts = [ipaconf.setSection('global', gopts)]
|
||||||
|
|
||||||
|
ipaconf.changeConf(target_fname, opts)
|
||||||
|
os.chmod(target_fname, 0o644) # must be readable for httpd
|
||||||
|
|
||||||
|
custodia = custodiainstance.CustodiaInstance(config.host_name,
|
||||||
|
config.realm_name)
|
||||||
|
custodia.create_replica(config.master_host_name)
|
||||||
|
|
||||||
|
if config.setup_ca:
|
||||||
|
options.realm_name = config.realm_name
|
||||||
|
options.domain_name = config.domain_name
|
||||||
|
options.host_name = config.host_name
|
||||||
|
options.dm_password = config.dirman_password
|
||||||
|
|
||||||
|
ca.install(False, config, options)
|
||||||
|
|
||||||
|
krb = install_krb(config,
|
||||||
|
setup_pkinit=not options.no_pkinit,
|
||||||
|
promote=True)
|
||||||
|
|
||||||
|
http = install_http(config,
|
||||||
|
auto_redirect=not options.no_ui_redirect,
|
||||||
|
promote=True)
|
||||||
|
|
||||||
|
otpd = otpdinstance.OtpdInstance()
|
||||||
|
otpd.create_instance('OTPD', config.host_name, config.dirman_password,
|
||||||
|
ipautil.realm_to_suffix(config.realm_name))
|
||||||
|
|
||||||
|
CA = cainstance.CAInstance(
|
||||||
|
config.realm_name, certs.NSS_DIR,
|
||||||
|
dogtag_constants=dogtag_constants)
|
||||||
|
CA.dm_password = config.dirman_password
|
||||||
|
CA.configure_certmonger_renewal()
|
||||||
|
CA.fix_ra_perms()
|
||||||
|
|
||||||
|
# Apply any LDAP updates. Needs to be done after the replica is synced-up
|
||||||
|
service.print_msg("Applying LDAP updates")
|
||||||
|
ds.apply_updates()
|
||||||
|
|
||||||
|
if options.setup_kra:
|
||||||
|
kra.install(api, config, options)
|
||||||
|
else:
|
||||||
|
service.print_msg("Restarting the directory server")
|
||||||
|
ds.restart()
|
||||||
|
|
||||||
|
service.print_msg("Restarting the KDC")
|
||||||
|
krb.restart()
|
||||||
|
|
||||||
|
if config.setup_ca:
|
||||||
|
dogtag_service = services.knownservices[dogtag_constants.SERVICE_NAME]
|
||||||
|
dogtag_service.restart(dogtag_constants.PKI_INSTANCE_NAME)
|
||||||
|
|
||||||
|
if options.setup_dns:
|
||||||
|
api.Backend.ldap2.connect(autobind=True)
|
||||||
|
dns.install(False, True, options)
|
||||||
|
|
||||||
|
# Restart httpd to pick up the new IPA configuration
|
||||||
|
service.print_msg("Restarting the web server")
|
||||||
|
http.restart()
|
||||||
|
|
||||||
|
ds.replica_populate()
|
||||||
|
|
||||||
|
custodia.import_dm_password(config.master_host_name)
|
||||||
|
|
||||||
|
promote_sssd(config.host_name)
|
||||||
|
|
||||||
|
# Everything installed properly, activate ipa service.
|
||||||
|
services.knownservices.ipa.enable()
|
||||||
|
|
||||||
|
|
||||||
class Replica(BaseServer):
|
class Replica(BaseServer):
|
||||||
replica_file = Knob(
|
replica_file = Knob(
|
||||||
str, None,
|
str, None,
|
||||||
@ -710,6 +1221,15 @@ class Replica(BaseServer):
|
|||||||
description="skip connection check to remote master",
|
description="skip connection check to remote master",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
principal = Knob(
|
||||||
|
str, None,
|
||||||
|
sensitive=True,
|
||||||
|
description="User Principal allowed to promote replicas",
|
||||||
|
cli_short_name='P',
|
||||||
|
)
|
||||||
|
|
||||||
|
promote = False
|
||||||
|
|
||||||
# ca
|
# ca
|
||||||
external_ca = None
|
external_ca = None
|
||||||
external_ca_type = None
|
external_ca_type = None
|
||||||
@ -742,11 +1262,11 @@ class Replica(BaseServer):
|
|||||||
self._update_hosts_file = False
|
self._update_hosts_file = False
|
||||||
|
|
||||||
if self.replica_file is None:
|
if self.replica_file is None:
|
||||||
raise RuntimeError(
|
self.promote = True
|
||||||
"you must provide a file generated by ipa-replica-prepare")
|
else:
|
||||||
if not ipautil.file_exists(self.replica_file):
|
if not ipautil.file_exists(self.replica_file):
|
||||||
raise RuntimeError(
|
raise RuntimeError("Replica file %s does not exist"
|
||||||
"Replica file %s does not exist" % self.replica_file)
|
% self.replica_file)
|
||||||
|
|
||||||
if self.setup_dns:
|
if self.setup_dns:
|
||||||
#pylint: disable=no-member
|
#pylint: disable=no-member
|
||||||
@ -759,6 +1279,12 @@ class Replica(BaseServer):
|
|||||||
|
|
||||||
@step()
|
@step()
|
||||||
def main(self):
|
def main(self):
|
||||||
install_check(self)
|
if self.promote:
|
||||||
yield
|
promote_check(self)
|
||||||
install(self)
|
yield
|
||||||
|
promote(self)
|
||||||
|
else:
|
||||||
|
with ipautil.private_ccache():
|
||||||
|
install_check(self)
|
||||||
|
yield
|
||||||
|
install(self)
|
||||||
|
@ -1540,8 +1540,8 @@ def upgrade_configuration():
|
|||||||
except ipautil.CalledProcessError as e:
|
except ipautil.CalledProcessError as e:
|
||||||
root_logger.error("Failed to restart %s: %s", bind.service_name, e)
|
root_logger.error("Failed to restart %s: %s", bind.service_name, e)
|
||||||
|
|
||||||
custodia = custodiainstance.CustodiaInstance()
|
custodia = custodiainstance.CustodiaInstance(api.env.host, api.env.realm)
|
||||||
custodia.upgrade_instance(api.env.realm)
|
custodia.upgrade_instance()
|
||||||
|
|
||||||
ca_restart = any([
|
ca_restart = any([
|
||||||
ca_restart,
|
ca_restart,
|
||||||
|
Loading…
Reference in New Issue
Block a user