mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-25 23:56:30 -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',
|
||||
log_file_name=paths.IPAREPLICA_INSTALL_LOG,
|
||||
debug_option=True,
|
||||
use_private_ccache=False,
|
||||
)
|
||||
|
||||
|
||||
|
@ -360,5 +360,6 @@ class BasePathNamespace(object):
|
||||
IPA_CUSTODIA_CONF = '/etc/ipa/custodia/custodia.conf'
|
||||
IPA_CUSTODIA_SOCKET = '/run/httpd/ipa-custodia.sock'
|
||||
IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log'
|
||||
IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab'
|
||||
|
||||
path_namespace = BasePathNamespace
|
||||
|
@ -25,6 +25,7 @@ if six.PY3:
|
||||
|
||||
def install_tool(configurable_class, command_name, log_file_name,
|
||||
positional_arguments=None, usage=None, debug_option=False,
|
||||
use_private_ccache=True,
|
||||
uninstall_log_file_name=None,
|
||||
uninstall_positional_arguments=None, uninstall_usage=None):
|
||||
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,
|
||||
debug_option=debug_option,
|
||||
uninstall_kwargs=uninstall_kwargs,
|
||||
use_private_ccache=use_private_ccache,
|
||||
)
|
||||
)
|
||||
|
||||
@ -76,6 +78,7 @@ class ConfigureTool(admintool.AdminTool):
|
||||
configurable_class = None
|
||||
debug_option = False
|
||||
positional_arguments = None
|
||||
use_private_ccache = True
|
||||
|
||||
@staticmethod
|
||||
def _transform(configurable_class):
|
||||
@ -305,10 +308,12 @@ class ConfigureTool(admintool.AdminTool):
|
||||
|
||||
signal.signal(signal.SIGTERM, self.__signal_handler)
|
||||
|
||||
# Use private ccache
|
||||
with private_ccache():
|
||||
if self.use_private_ccache:
|
||||
with private_ccache():
|
||||
super(ConfigureTool, self).run()
|
||||
cfgr.run()
|
||||
else:
|
||||
super(ConfigureTool, self).run()
|
||||
|
||||
cfgr.run()
|
||||
|
||||
@staticmethod
|
||||
|
@ -260,6 +260,32 @@ def is_step_one_done():
|
||||
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():
|
||||
"""Check if CA is installed locally by checking for existence of CS.cfg
|
||||
:return:True/False
|
||||
|
@ -653,6 +653,18 @@ class CertDB(object):
|
||||
def export_pem_cert(self, 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):
|
||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2015 FreeIPa Project Contributors, see 'COPYING' for license.
|
||||
|
||||
from ipapython.secrets.kem import IPAKEMKeys
|
||||
from ipapython.secrets.client import CustodiaClient
|
||||
from ipaplatform.paths import paths
|
||||
from service import SimpleServiceInstance
|
||||
from ipapython import ipautil
|
||||
@ -9,11 +10,14 @@ import os
|
||||
|
||||
|
||||
class CustodiaInstance(SimpleServiceInstance):
|
||||
def __init__(self):
|
||||
def __init__(self, host_name=None, realm=None):
|
||||
super(CustodiaInstance, self).__init__("ipa-custodia")
|
||||
self.config_file = paths.IPA_CUSTODIA_CONF
|
||||
self.server_keys = os.path.join(paths.IPA_CUSTODIA_CONF_DIR,
|
||||
'server.keys')
|
||||
self.ldap_uri = None
|
||||
self.fqdn = host_name
|
||||
self.realm = realm
|
||||
|
||||
def __config_file(self):
|
||||
template_file = os.path.basename(self.config_file) + '.template'
|
||||
@ -28,22 +32,48 @@ class CustodiaInstance(SimpleServiceInstance):
|
||||
fd.flush()
|
||||
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 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):
|
||||
KeyStore = IPAKEMKeys({'server_keys': self.server_keys})
|
||||
KeyStore = IPAKEMKeys({'server_keys': self.server_keys,
|
||||
'ldap_uri': self.ldap_uri})
|
||||
KeyStore.generate_server_keys()
|
||||
|
||||
def upgrade_instance(self, realm):
|
||||
self.realm = realm
|
||||
def upgrade_instance(self):
|
||||
if not os.path.exists(self.config_file):
|
||||
self.__config_file()
|
||||
if not os.path.exists(self.server_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):
|
||||
super(CustodiaInstance, self).__start()
|
||||
|
||||
|
@ -255,8 +255,8 @@ class DsInstance(service.Service):
|
||||
self.step("configure autobind for root", self.__root_autobind)
|
||||
self.step("configure new location for managed entries", self.__repoint_managed_entries)
|
||||
self.step("configure dirsrv ccache", self.configure_dirsrv_ccache)
|
||||
self.step("enable SASL mapping fallback", self.__enable_sasl_mapping_fallback)
|
||||
self.step("restarting directory server", self.__restart_instance)
|
||||
self.step("enabling SASL mapping fallback",
|
||||
self.__enable_sasl_mapping_fallback)
|
||||
|
||||
def __common_post_setup(self):
|
||||
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)
|
||||
|
||||
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 default layout", self.__add_default_layout)
|
||||
@ -314,6 +315,8 @@ class DsInstance(service.Service):
|
||||
if hbac_allow:
|
||||
self.step("creating default HBAC rule allow_all", self.add_hbac)
|
||||
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.__common_post_setup()
|
||||
@ -331,7 +334,8 @@ class DsInstance(service.Service):
|
||||
|
||||
def create_replica(self, realm_name, master_fqdn, fqdn,
|
||||
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
|
||||
# depleted by the DNA plugin and the replica will go and get a
|
||||
# new range from the master.
|
||||
@ -353,8 +357,15 @@ class DsInstance(service.Service):
|
||||
self.master_fqdn = master_fqdn
|
||||
if ca_is_configured is not None:
|
||||
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("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
||||
@ -374,14 +385,25 @@ class DsInstance(service.Service):
|
||||
self.realm,
|
||||
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,
|
||||
self.fqdn,
|
||||
self.dm_password)
|
||||
repl.setup_replication(self.master_fqdn,
|
||||
r_binddn=DN(('cn', 'Directory Manager')),
|
||||
r_bindpw=self.dm_password)
|
||||
self.dm_password, conn=conn)
|
||||
if self.promote:
|
||||
repl.setup_promote_replication(self.master_fqdn)
|
||||
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()
|
||||
|
||||
# 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):
|
||||
# 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
|
||||
if self.domainlevel is not None:
|
||||
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,
|
||||
autoconfig=True, pkcs12_info=None,
|
||||
subject_base=None, auto_redirect=True, ca_file=None,
|
||||
ca_is_configured=None):
|
||||
ca_is_configured=None, promote=False):
|
||||
self.fqdn = fqdn
|
||||
self.realm = realm
|
||||
self.domain = domain_name
|
||||
@ -132,6 +132,7 @@ class HTTPInstance(service.Service):
|
||||
self.ca_file = ca_file
|
||||
if ca_is_configured is not None:
|
||||
self.ca_is_configured = ca_is_configured
|
||||
self.promote = promote
|
||||
|
||||
# get a connection to the DS
|
||||
self.ldap_connect()
|
||||
@ -147,12 +148,13 @@ class HTTPInstance(service.Service):
|
||||
if self.ca_is_configured:
|
||||
self.step("configure certmonger for renewals",
|
||||
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("importing CA certificates from LDAP", self.__import_ca_certs)
|
||||
if autoconfig:
|
||||
self.step("setting up browser autoconfig", self.__setup_autoconfig)
|
||||
self.step("publish CA cert", self.__publish_ca_cert)
|
||||
self.step("creating a keytab for httpd", self.__create_http_keytab)
|
||||
if not self.promote:
|
||||
self.step("publish CA cert", self.__publish_ca_cert)
|
||||
self.step("clean up any existing httpd ccache", self.remove_httpd_ccache)
|
||||
self.step("configuring SELinux for httpd", self.configure_selinux_for_httpd)
|
||||
if not self.is_kdcproxy_configured():
|
||||
@ -183,10 +185,10 @@ class HTTPInstance(service.Service):
|
||||
self.print_msg(e.format_service_warning('web interface'))
|
||||
|
||||
def __create_http_keytab(self):
|
||||
installutils.kadmin_addprinc(self.principal)
|
||||
installutils.create_keytab(paths.IPA_KEYTAB, self.principal)
|
||||
self.move_service(self.principal)
|
||||
self.add_cert_to_service()
|
||||
if not self.promote:
|
||||
installutils.kadmin_addprinc(self.principal)
|
||||
installutils.create_keytab(paths.IPA_KEYTAB, self.principal)
|
||||
self.move_service(self.principal)
|
||||
|
||||
pent = pwd.getpwnam("apache")
|
||||
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')
|
||||
|
||||
self.__set_mod_nss_nickname(nickname)
|
||||
else:
|
||||
self.add_cert_to_service()
|
||||
|
||||
elif not self.promote:
|
||||
db.create_password_conf()
|
||||
self.dercert = db.create_server_cert(self.cert_nickname, self.fqdn,
|
||||
ca_db)
|
||||
db.track_server_cert(self.cert_nickname, self.principal,
|
||||
db.passwd_fname, 'restart_httpd')
|
||||
db.create_signing_cert("Signing-Cert", "Object Signing Cert", ca_db)
|
||||
self.add_cert_to_service()
|
||||
|
||||
# Fix the database permissions
|
||||
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 ipalib.util import validate_hostname
|
||||
from ipapython import config
|
||||
from ipalib import errors, x509
|
||||
from ipalib import api, errors, x509
|
||||
from ipapython.dn import DN
|
||||
from ipaserver.install import certs, service, sysupgrade
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipapython import certmonger
|
||||
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@ -1115,3 +1117,28 @@ def enable_and_start_oddjobd(sstore):
|
||||
oddjobd.start()
|
||||
except Exception as 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,
|
||||
domain_name, admin_password,
|
||||
setup_pkinit=False, pkcs12_info=None,
|
||||
subject_base=None):
|
||||
subject_base=None, promote=False):
|
||||
self.pkcs12_info = pkcs12_info
|
||||
self.subject_base = subject_base
|
||||
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.step("configuring KDC", self.__configure_instance)
|
||||
self.step("creating a keytab for the directory", self.__create_ds_keytab)
|
||||
self.step("creating a keytab for the machine", self.__create_host_keytab)
|
||||
if not promote:
|
||||
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)
|
||||
if 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()
|
||||
|
||||
|
@ -1605,6 +1605,96 @@ class ReplicationManager(object):
|
||||
except errors.EmptyModlist:
|
||||
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):
|
||||
"""ReplicationManager specific to CA agreements
|
||||
|
||||
|
@ -814,10 +814,8 @@ def install(installer):
|
||||
otpd.create_instance('OTPD', host_name, dm_password,
|
||||
ipautil.realm_to_suffix(realm_name))
|
||||
|
||||
custodia = custodiainstance.CustodiaInstance()
|
||||
custodia.create_instance('KEYS', host_name, dm_password,
|
||||
ipautil.realm_to_suffix(realm_name),
|
||||
realm_name)
|
||||
custodia = custodiainstance.CustodiaInstance(host_name, realm_name)
|
||||
custodia.create_instance(dm_password)
|
||||
|
||||
# Create a HTTP instance
|
||||
http = httpinstance.HTTPInstance(fstore)
|
||||
|
@ -8,13 +8,15 @@ import dns.exception as dnsexception
|
||||
import dns.name as dnsname
|
||||
import dns.resolver as dnsresolver
|
||||
import dns.reversename as dnsreversename
|
||||
import getpass
|
||||
import gssapi
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from ipapython import dogtag, ipautil, sysrestore
|
||||
from ipapython import certmonger, dogtag, ipaldap, ipautil, sysrestore
|
||||
from ipapython.dn import DN
|
||||
from ipapython.install import common, core
|
||||
from ipapython.install.common import step
|
||||
@ -24,14 +26,19 @@ from ipaplatform import services
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipaplatform.paths import paths
|
||||
from ipalib import api, certstore, constants, create_api, errors, x509
|
||||
import ipaclient.ipachangeconf
|
||||
import ipaclient.ntpconf
|
||||
from ipaserver.install import (
|
||||
bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance,
|
||||
installutils, kra, krbinstance, memcacheinstance, ntpinstance,
|
||||
otpdinstance, custodiainstance, service)
|
||||
from ipaserver.install.installutils import create_replica_config
|
||||
from ipaserver.install.installutils import ReplicaConfig
|
||||
from ipaserver.install.replication import (
|
||||
ReplicationManager, replica_conn_check)
|
||||
import SSSDConfig
|
||||
from subprocess import CalledProcessError
|
||||
from binascii import hexlify
|
||||
|
||||
from .common import BaseServer
|
||||
|
||||
@ -60,7 +67,29 @@ def make_pkcs12_info(directory, cert_name, password_name):
|
||||
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()
|
||||
|
||||
# if we have a pkcs12 file, create the cert db from
|
||||
@ -79,12 +108,13 @@ def install_replica_ds(config):
|
||||
pkcs12_info=pkcs12_info,
|
||||
ca_is_configured=ipautil.file_exists(config.dir + "/cacert.p12"),
|
||||
ca_file=config.dir + "/ca.crt",
|
||||
promote=promote,
|
||||
)
|
||||
|
||||
return ds
|
||||
|
||||
|
||||
def install_krb(config, setup_pkinit=False):
|
||||
def install_krb(config, setup_pkinit=False, promote=False):
|
||||
krb = krbinstance.KrbInstance()
|
||||
|
||||
# pkinit files
|
||||
@ -94,7 +124,7 @@ def install_krb(config, setup_pkinit=False):
|
||||
krb.create_replica(config.realm_name,
|
||||
config.master_host_name, config.host_name,
|
||||
config.domain_name, config.dirman_password,
|
||||
setup_pkinit, pkcs12_info)
|
||||
setup_pkinit, pkcs12_info, promote=promote)
|
||||
|
||||
return krb
|
||||
|
||||
@ -115,7 +145,7 @@ def install_ca_cert(ldap, base_dn, realm, cafile):
|
||||
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
|
||||
# that. Otherwise the ds setup will create the CA
|
||||
# cert
|
||||
@ -131,7 +161,8 @@ def install_http(config, auto_redirect):
|
||||
config.realm_name, config.host_name, config.domain_name,
|
||||
config.dirman_password, False, pkcs12_info,
|
||||
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
|
||||
try:
|
||||
@ -153,9 +184,10 @@ def install_http(config, auto_redirect):
|
||||
def install_dns_records(config, options, remote_api):
|
||||
|
||||
if not bindinstance.dns_container_exists(
|
||||
config.master_host_name,
|
||||
config.host_name,
|
||||
ipautil.realm_to_suffix(config.realm_name),
|
||||
dm_password=config.dirman_password):
|
||||
realm=config.realm_name, ldapi=True,
|
||||
autobind=ipaldap.AUTOBIND_ENABLED):
|
||||
return
|
||||
|
||||
try:
|
||||
@ -283,6 +315,43 @@ def check_dns_resolution(host_name, dns_servers):
|
||||
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):
|
||||
# always try to remove decrypted replica file
|
||||
try:
|
||||
@ -311,6 +380,37 @@ def common_cleanup(func):
|
||||
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
|
||||
def install_check(installer):
|
||||
options = installer
|
||||
@ -433,6 +533,14 @@ def install_check(installer):
|
||||
# available
|
||||
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
|
||||
# for this IPA version
|
||||
under_lower_bound = current < constants.MIN_DOMAIN_LEVEL
|
||||
@ -596,12 +704,9 @@ def install(installer):
|
||||
CA.import_ra_cert(config.dir + "/ra.p12")
|
||||
CA.fix_ra_perms()
|
||||
|
||||
# FIXME: must be done earlier in replica to fetch keys for CA/ldap server
|
||||
# before they are configured
|
||||
custodia = custodiainstance.CustodiaInstance()
|
||||
custodia.create_instance('KEYS', config.host_name,
|
||||
config.dirman_password,
|
||||
ipautil.realm_to_suffix(config.realm_name))
|
||||
custodia = custodiainstance.CustodiaInstance(config.host_name,
|
||||
config.realm_name)
|
||||
custodia.create_instance(config.dirman_password)
|
||||
|
||||
# The DS instance is created before the keytab, add the SSL cert we
|
||||
# generated
|
||||
@ -662,6 +767,412 @@ def install(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):
|
||||
replica_file = Knob(
|
||||
str, None,
|
||||
@ -710,6 +1221,15 @@ class Replica(BaseServer):
|
||||
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
|
||||
external_ca = None
|
||||
external_ca_type = None
|
||||
@ -742,11 +1262,11 @@ class Replica(BaseServer):
|
||||
self._update_hosts_file = False
|
||||
|
||||
if self.replica_file is None:
|
||||
raise RuntimeError(
|
||||
"you must provide a file generated by ipa-replica-prepare")
|
||||
if not ipautil.file_exists(self.replica_file):
|
||||
raise RuntimeError(
|
||||
"Replica file %s does not exist" % self.replica_file)
|
||||
self.promote = True
|
||||
else:
|
||||
if not ipautil.file_exists(self.replica_file):
|
||||
raise RuntimeError("Replica file %s does not exist"
|
||||
% self.replica_file)
|
||||
|
||||
if self.setup_dns:
|
||||
#pylint: disable=no-member
|
||||
@ -759,6 +1279,12 @@ class Replica(BaseServer):
|
||||
|
||||
@step()
|
||||
def main(self):
|
||||
install_check(self)
|
||||
yield
|
||||
install(self)
|
||||
if self.promote:
|
||||
promote_check(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:
|
||||
root_logger.error("Failed to restart %s: %s", bind.service_name, e)
|
||||
|
||||
custodia = custodiainstance.CustodiaInstance()
|
||||
custodia.upgrade_instance(api.env.realm)
|
||||
custodia = custodiainstance.CustodiaInstance(api.env.host, api.env.realm)
|
||||
custodia.upgrade_instance()
|
||||
|
||||
ca_restart = any([
|
||||
ca_restart,
|
||||
|
Loading…
Reference in New Issue
Block a user