Import Upstream version 4.12.4
This commit is contained in:
@@ -60,7 +60,6 @@ class ServerCertificateInstallInterface(service.ServiceInstallInterface):
|
||||
description = "SSL certificate"
|
||||
|
||||
dirsrv_cert_files = knob(
|
||||
# pylint: disable=invalid-sequence-index
|
||||
typing.List[str], None,
|
||||
description=("File containing the Directory Server SSL certificate "
|
||||
"and private key"),
|
||||
@@ -71,7 +70,6 @@ class ServerCertificateInstallInterface(service.ServiceInstallInterface):
|
||||
dirsrv_cert_files = prepare_only(dirsrv_cert_files)
|
||||
|
||||
http_cert_files = knob(
|
||||
# pylint: disable=invalid-sequence-index
|
||||
typing.List[str], None,
|
||||
description=("File containing the Apache Server SSL certificate and "
|
||||
"private key"),
|
||||
@@ -82,7 +80,6 @@ class ServerCertificateInstallInterface(service.ServiceInstallInterface):
|
||||
http_cert_files = prepare_only(http_cert_files)
|
||||
|
||||
pkinit_cert_files = knob(
|
||||
# pylint: disable=invalid-sequence-index
|
||||
typing.List[str], None,
|
||||
description=("File containing the Kerberos KDC SSL certificate and "
|
||||
"private key"),
|
||||
@@ -141,8 +138,50 @@ class ServerCertificateInstallInterface(service.ServiceInstallInterface):
|
||||
pkinit_cert_name = prepare_only(pkinit_cert_name)
|
||||
|
||||
|
||||
@group
|
||||
class ServerHSMInstallInterface(service.ServiceInstallInterface):
|
||||
description = "HSM"
|
||||
|
||||
token_name = knob(
|
||||
str, None,
|
||||
description=(
|
||||
"The PKCS#11 token name if using an HSM to store and generate "
|
||||
"private keys."
|
||||
),
|
||||
cli_metavar='NAME',
|
||||
)
|
||||
token_name = master_install_only(token_name)
|
||||
|
||||
token_library_path = knob(
|
||||
str, None,
|
||||
description=(
|
||||
"The full path to the PKCS#11 shared library needed to"
|
||||
"access an HSM device."
|
||||
),
|
||||
cli_metavar='NAME',
|
||||
)
|
||||
token_library_path = prepare_only(token_library_path)
|
||||
|
||||
token_password = knob(
|
||||
str, None,
|
||||
sensitive=True,
|
||||
description=("The PKCS#11 token password for the HSM."),
|
||||
cli_metavar='NAME',
|
||||
)
|
||||
token_password = prepare_only(token_password)
|
||||
|
||||
token_password_file = knob(
|
||||
str, None,
|
||||
description=("The full path to a file containing the password to "
|
||||
"the PKCS#11 token password."),
|
||||
cli_metavar='NAME',
|
||||
)
|
||||
token_password_file = prepare_only(token_password_file)
|
||||
|
||||
|
||||
@group
|
||||
class ServerInstallInterface(ServerCertificateInstallInterface,
|
||||
ServerHSMInstallInterface,
|
||||
client.ClientInstallInterface,
|
||||
ca.CAInstallInterface,
|
||||
kra.KRAInstallInterface,
|
||||
@@ -171,7 +210,6 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
|
||||
domain_name = client.ClientInstallInterface.domain_name
|
||||
domain_name = extend_knob(
|
||||
domain_name,
|
||||
# pylint: disable=no-member
|
||||
cli_names=list(domain_name.cli_names) + ['-n'],
|
||||
)
|
||||
|
||||
@@ -432,11 +470,6 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
|
||||
"You cannot specify an --enable-compat option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
if self.netbios_name:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --netbios-name option without the "
|
||||
"--setup-adtrust option")
|
||||
|
||||
if self.no_msdcs:
|
||||
raise RuntimeError(
|
||||
"You cannot specify a --no-msdcs option without the "
|
||||
|
||||
@@ -31,7 +31,7 @@ from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib.constants import DOMAIN_LEVEL_0
|
||||
from ipalib.constants import DOMAIN_LEVEL_0, FQDN
|
||||
from ipalib.facts import is_ipa_configured, is_ipa_client_configured
|
||||
from ipalib.util import (
|
||||
validate_domain_name,
|
||||
@@ -42,9 +42,9 @@ from ipaserver.install import (
|
||||
adtrust, adtrustinstance, bindinstance, ca, dns, dsinstance,
|
||||
httpinstance, installutils, kra, krbinstance,
|
||||
otpdinstance, custodiainstance, replication, service,
|
||||
sysupgrade)
|
||||
sysupgrade, cainstance)
|
||||
from ipaserver.install.installutils import (
|
||||
BadHostError, get_fqdn, get_server_ip_address,
|
||||
BadHostError, get_server_ip_address,
|
||||
load_pkcs12, read_password, verify_fqdn, update_hosts_file,
|
||||
validate_mask)
|
||||
|
||||
@@ -76,7 +76,7 @@ def validate_dm_password(password):
|
||||
# Actual behavior of setup-ds.pl is that it does not accept white
|
||||
# space characters in password when called interactively but does when
|
||||
# provided such password in INF file. But it ignores leading and trailing
|
||||
# white spaces in INF file.
|
||||
# whitespaces in INF file.
|
||||
|
||||
# Disallow leading/trailing whaitespaces
|
||||
if password.strip() != password:
|
||||
@@ -132,7 +132,7 @@ def read_cache(dm_password):
|
||||
fname,
|
||||
dm_password,
|
||||
top_dir)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
shutil.rmtree(top_dir)
|
||||
raise Exception("Decryption of answer cache in %s failed, please "
|
||||
"check your password." % paths.ROOT_IPA_CACHE)
|
||||
@@ -178,18 +178,24 @@ def write_cache(options):
|
||||
shutil.rmtree(top_dir)
|
||||
|
||||
|
||||
def read_host_name(host_default, no_host_dns=False):
|
||||
def read_host_name(host_default):
|
||||
"""
|
||||
Prompt user to input FQDN. Does not verify it.
|
||||
|
||||
"""
|
||||
print("Enter the fully qualified domain name of the computer")
|
||||
print("on which you're setting up server software. Using the form")
|
||||
print("<hostname>.<domainname>")
|
||||
print("Example: master.example.com.")
|
||||
print("Example: master.example.com")
|
||||
print("")
|
||||
print("")
|
||||
if host_default == "":
|
||||
host_default = "master.example.com"
|
||||
host_name = user_input("Server host name", host_default, allow_empty=False)
|
||||
print("")
|
||||
verify_fqdn(host_name, no_host_dns)
|
||||
|
||||
if host_name.endswith('.'):
|
||||
host_name = host_name[:-1]
|
||||
|
||||
return host_name
|
||||
|
||||
@@ -315,6 +321,19 @@ def remove_master_from_managed_topology(api_instance, options):
|
||||
logger.warning("Failed to delete master: %s", e)
|
||||
|
||||
|
||||
def cleanup_dogtag_server_specific_data():
|
||||
"""
|
||||
There are data in Dogtag database related to specific servers.
|
||||
Some of these data should be left alone, e.g. range assignments.
|
||||
Some of these data should be cleaned up; that's what this
|
||||
subroutine does.
|
||||
|
||||
"""
|
||||
# remove ACME user
|
||||
acme_uid = cainstance.CAInstance.acme_uid(api.env.host)
|
||||
cainstance.CAInstance.delete_user(acme_uid)
|
||||
|
||||
|
||||
@common_cleanup
|
||||
def install_check(installer):
|
||||
options = installer
|
||||
@@ -408,10 +427,39 @@ def install_check(installer):
|
||||
if not setup_ca and options.setup_kra:
|
||||
raise ScriptError(
|
||||
"--setup-kra cannot be used with CA-less installation")
|
||||
if setup_ca:
|
||||
if any(
|
||||
(
|
||||
options.token_name is not None,
|
||||
options.token_library_path is not None,
|
||||
options.token_password is not None,
|
||||
options.token_password_file is not None,
|
||||
)
|
||||
):
|
||||
if any(
|
||||
(
|
||||
options.token_name is None,
|
||||
options.token_library_path is None)
|
||||
):
|
||||
raise ScriptError(
|
||||
"Both token name and library path are required."
|
||||
)
|
||||
else:
|
||||
if any(
|
||||
(
|
||||
options.token_name is not None,
|
||||
options.token_library_path is not None,
|
||||
options.token_password is not None,
|
||||
options.token_password_file is not None,
|
||||
)
|
||||
):
|
||||
raise ScriptError(
|
||||
"HSM token options are not valid with CA-less installs."
|
||||
)
|
||||
|
||||
print("======================================="
|
||||
"=======================================")
|
||||
print("This program will set up the FreeIPA Server.")
|
||||
print("This program will set up the IPA Server.")
|
||||
print("Version {}".format(version.VERSION))
|
||||
print("")
|
||||
print("This includes:")
|
||||
@@ -427,6 +475,7 @@ def install_check(installer):
|
||||
print(" * Configure KRA (dogtag) for secret management")
|
||||
if options.setup_dns:
|
||||
print(" * Configure DNS (bind)")
|
||||
print(" * Configure SID generation")
|
||||
if options.setup_adtrust:
|
||||
print(" * Configure Samba (smb) and winbind for managing AD trusts")
|
||||
if not options.no_pkinit:
|
||||
@@ -477,14 +526,15 @@ def install_check(installer):
|
||||
if options.host_name:
|
||||
host_default = options.host_name
|
||||
else:
|
||||
host_default = get_fqdn()
|
||||
host_default = FQDN
|
||||
|
||||
if installer.interactive and not options.host_name:
|
||||
host_name = read_host_name(host_default)
|
||||
else:
|
||||
host_name = host_default
|
||||
|
||||
try:
|
||||
if not installer.interactive or options.host_name:
|
||||
verify_fqdn(host_default, options.no_host_dns)
|
||||
host_name = host_default
|
||||
else:
|
||||
host_name = read_host_name(host_default, options.no_host_dns)
|
||||
verify_fqdn(host_name, options.no_host_dns)
|
||||
except BadHostError as e:
|
||||
raise ScriptError(e)
|
||||
|
||||
@@ -504,6 +554,9 @@ def install_check(installer):
|
||||
|
||||
domain_name = domain_name.lower()
|
||||
|
||||
if host_name.lower() == domain_name:
|
||||
raise ScriptError("hostname cannot be the same as the domain name")
|
||||
|
||||
if not options.realm_name:
|
||||
realm_name = read_realm_name(domain_name, not installer.interactive)
|
||||
logger.debug("read realm_name: %s\n", realm_name)
|
||||
@@ -603,6 +656,22 @@ def install_check(installer):
|
||||
else:
|
||||
admin_password = options.admin_password
|
||||
|
||||
if all(
|
||||
(
|
||||
options.token_password is None,
|
||||
options.token_password_file is None,
|
||||
options.token_name is not None
|
||||
)
|
||||
):
|
||||
if options.unattended:
|
||||
raise ScriptError("HSM token password required")
|
||||
token_password = read_password(
|
||||
f"HSM token '{options.token_name}'" , confirm=False)
|
||||
if token_password is None:
|
||||
raise ScriptError("HSM token password required")
|
||||
else:
|
||||
options.token_password = token_password
|
||||
|
||||
# Configuration for ipalib, we will bootstrap and finalize later, after
|
||||
# we are sure we have the configuration file ready.
|
||||
cfg = dict(
|
||||
@@ -686,8 +755,9 @@ def install_check(installer):
|
||||
logger.debug('Starting Directory Server')
|
||||
services.knownservices.dirsrv.start(instance_name)
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install_check(False, options, api)
|
||||
# Always call adtrust.install_check
|
||||
# if --setup-adtrust is not specified, only the SID part is executed
|
||||
adtrust.install_check(False, options, api)
|
||||
|
||||
# installer needs to update hosts file when DNS subsystem will be
|
||||
# installed or custom addresses are used
|
||||
@@ -949,8 +1019,9 @@ def install(installer):
|
||||
if options.setup_dns:
|
||||
dns.install(False, False, options)
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install(False, options, fstore, api)
|
||||
# Always call adtrust installer to configure SID generation
|
||||
# if --setup-adtrust is not specified, only the SID part is executed
|
||||
adtrust.install(False, options, fstore, api)
|
||||
|
||||
# Set the admin user kerberos password
|
||||
ds.change_admin_password(admin_password)
|
||||
@@ -971,6 +1042,8 @@ def install(installer):
|
||||
args.append("--no-sshd")
|
||||
if options.mkhomedir:
|
||||
args.append("--mkhomedir")
|
||||
if options.subid:
|
||||
args.append("--subid")
|
||||
start = time.time()
|
||||
run(args, redirect_output=True)
|
||||
dur = time.time() - start
|
||||
@@ -1032,7 +1105,7 @@ def install(installer):
|
||||
"enabling chronyd.")
|
||||
|
||||
print("")
|
||||
if setup_ca:
|
||||
if setup_ca and not options.token_name:
|
||||
print(("Be sure to back up the CA certificates stored in " +
|
||||
paths.CACERT_P12))
|
||||
print("These files are required to create replicas. The password for "
|
||||
@@ -1081,6 +1154,9 @@ def uninstall_check(installer):
|
||||
"uninstall procedure?", False):
|
||||
raise ScriptError("Aborting uninstall operation.")
|
||||
|
||||
kra.uninstall_check(options)
|
||||
ca.uninstall_check(options)
|
||||
|
||||
try:
|
||||
api.Backend.ldap2.connect(autobind=True)
|
||||
|
||||
@@ -1102,7 +1178,9 @@ def uninstall_check(installer):
|
||||
else:
|
||||
dns.uninstall_check(options)
|
||||
|
||||
ca.uninstall_check(options)
|
||||
ca.uninstall_crl_check(options)
|
||||
|
||||
cleanup_dogtag_server_specific_data()
|
||||
|
||||
if domain_level == DOMAIN_LEVEL_0:
|
||||
rm = replication.ReplicationManager(
|
||||
@@ -1146,6 +1224,13 @@ def uninstall(installer):
|
||||
|
||||
rv = 0
|
||||
|
||||
# Uninstall the KRA prior to shutting the services down so it
|
||||
# can un-register with the CA.
|
||||
kra.uninstall()
|
||||
# Uninstall the CA priori to shutting the services down so it
|
||||
# can unregister from the security domain
|
||||
ca.uninstall()
|
||||
|
||||
print("Shutting down all IPA services")
|
||||
try:
|
||||
services.knownservices.ipa.stop()
|
||||
@@ -1158,10 +1243,6 @@ def uninstall(installer):
|
||||
|
||||
restore_time_sync(sstore, fstore)
|
||||
|
||||
kra.uninstall()
|
||||
|
||||
ca.uninstall()
|
||||
|
||||
dns.uninstall()
|
||||
|
||||
httpinstance.HTTPInstance(fstore).uninstall()
|
||||
@@ -1247,6 +1328,9 @@ def uninstall(installer):
|
||||
logger.warning("Failed to remove file %s: %s",
|
||||
paths.IPA_RENEWAL_LOCK, e)
|
||||
|
||||
ipautil.remove_file(paths.SVC_LIST_FILE)
|
||||
ipautil.rmtree('/root/.cache/ipa')
|
||||
|
||||
print("Removing IPA client configuration")
|
||||
try:
|
||||
result = run([paths.IPA_CLIENT_INSTALL, "--on-master",
|
||||
|
||||
@@ -9,6 +9,7 @@ import logging
|
||||
|
||||
import dns.exception as dnsexception
|
||||
import dns.name as dnsname
|
||||
import itertools
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
@@ -22,8 +23,8 @@ import six
|
||||
|
||||
from ipaclient.install.client import check_ldap_conf, sssd_enable_ifp
|
||||
import ipaclient.install.timeconf
|
||||
from ipalib.install import certstore, sysrestore
|
||||
from ipalib.install.kinit import kinit_keytab
|
||||
from ipalib.install import sysrestore
|
||||
from ipalib.kinit import kinit_keytab
|
||||
from ipapython import ipaldap, ipautil
|
||||
from ipapython.dn import DN
|
||||
from ipapython.dnsutil import DNSResolver
|
||||
@@ -32,14 +33,17 @@ from ipapython.ipachangeconf import IPAChangeConf
|
||||
from ipaplatform import services
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipaplatform.paths import paths
|
||||
from ipalib import api, constants, create_api, errors, rpc, x509
|
||||
from ipalib import api, constants, create_api, errors, rpc
|
||||
from ipalib.config import Env
|
||||
from ipalib.facts import is_ipa_configured, is_ipa_client_configured
|
||||
from ipalib.util import no_matching_interface_for_ip_address_warning
|
||||
from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
|
||||
from ipaserver.install.dogtaginstance import INTERNAL_TOKEN
|
||||
from ipaserver.install import (
|
||||
adtrust, bindinstance, ca, dns, dsinstance, httpinstance,
|
||||
installutils, kra, krbinstance, otpdinstance, custodiainstance, service)
|
||||
adtrust, bindinstance, ca, cainstance, dns, dsinstance, httpinstance,
|
||||
installutils, kra, krainstance, krbinstance, otpdinstance,
|
||||
custodiainstance, service,)
|
||||
from ipaserver.install import certs
|
||||
from ipaserver.install.installutils import (
|
||||
ReplicaConfig, load_pkcs12, validate_mask)
|
||||
from ipaserver.install.replication import (
|
||||
@@ -132,24 +136,6 @@ def install_krb(config, setup_pkinit=False, pkcs12_info=None, fstore=None):
|
||||
return krb
|
||||
|
||||
|
||||
def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT):
|
||||
try:
|
||||
try:
|
||||
certs = certstore.get_ca_certs(ldap, base_dn, realm, False)
|
||||
except errors.NotFound:
|
||||
try:
|
||||
shutil.copy(cafile, destfile)
|
||||
except shutil.Error:
|
||||
# cafile == IPA_CA_CRT
|
||||
pass
|
||||
else:
|
||||
certs = [c[0] for c in certs if c[2] is not False]
|
||||
x509.write_certificate_list(certs, destfile, mode=0o644)
|
||||
except Exception as e:
|
||||
raise ScriptError("error copying files: " + str(e))
|
||||
return destfile
|
||||
|
||||
|
||||
def install_http(config, auto_redirect, ca_is_configured, ca_file,
|
||||
pkcs12_info=None, fstore=None):
|
||||
# if we have a pkcs12 file, create the cert db from
|
||||
@@ -623,7 +609,7 @@ def check_domain_level_is_supported(current):
|
||||
above_upper_bound = current > constants.MAX_DOMAIN_LEVEL
|
||||
|
||||
if under_lower_bound or above_upper_bound:
|
||||
message = ("This version of FreeIPA does not support "
|
||||
message = ("This version of IPA 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 "
|
||||
@@ -720,6 +706,8 @@ def ensure_enrolled(installer):
|
||||
args.append("--no-sshd")
|
||||
if installer.mkhomedir:
|
||||
args.append("--mkhomedir")
|
||||
if installer.subid:
|
||||
args.append("--subid")
|
||||
if installer.force_join:
|
||||
args.append("--force-join")
|
||||
if installer.no_ntp:
|
||||
@@ -770,6 +758,101 @@ def promotion_check_ipa_domain(master_ldap_conn, basedn):
|
||||
))
|
||||
|
||||
|
||||
def promotion_check_host_principal_auth_ind(conn, hostdn):
|
||||
entry = conn.get_entry(hostdn, ['krbprincipalauthind'])
|
||||
if 'krbprincipalauthind' in entry:
|
||||
raise RuntimeError(
|
||||
"Client cannot be promoted to a replica if the host principal "
|
||||
"has an authentication indicator set."
|
||||
)
|
||||
|
||||
|
||||
def clean_up_hsm_nicknames(api):
|
||||
"""Ensure that all of the nicknames on the token are visible on
|
||||
the NSS softoken.
|
||||
"""
|
||||
# Hardcode the token names. NSS tooling does not provide a
|
||||
# public way to determine it other than scraping modutil
|
||||
# output.
|
||||
if tasks.is_fips_enabled():
|
||||
dbname = 'NSS FIPS 140-2 Certificate DB'
|
||||
else:
|
||||
dbname = 'NSS Certificate DB'
|
||||
|
||||
api.Backend.ldap2.connect()
|
||||
(token_name, _unused) = ca.lookup_hsm_configuration(api)
|
||||
api.Backend.ldap2.disconnect()
|
||||
if not token_name:
|
||||
return
|
||||
|
||||
cai = cainstance.CAInstance(api.env.realm, host_name=api.env.host)
|
||||
dogtag_reqs = cai.tracking_reqs.items()
|
||||
kra = krainstance.KRAInstance(api.env.realm)
|
||||
if kra.is_installed():
|
||||
dogtag_reqs = itertools.chain(dogtag_reqs,
|
||||
kra.tracking_reqs.items())
|
||||
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp(prefix="tmp-")
|
||||
pwd_file = os.path.join(tmpdir, "pwd_file")
|
||||
with open(pwd_file, "w") as pwd:
|
||||
with open(paths.PKI_TOMCAT_PASSWORD_CONF, 'r') as fd:
|
||||
for line in fd:
|
||||
(token, pin) = line.split('=', 1)
|
||||
if token.startswith('hardware-'):
|
||||
token = token.replace('hardware-', '')
|
||||
pwd.write(f'{token}:{pin}')
|
||||
elif token == INTERNAL_TOKEN:
|
||||
pwd.write(f'{dbname}:{pin}')
|
||||
pwd.flush()
|
||||
db = certs.CertDB(api.env.realm,
|
||||
nssdir=paths.PKI_TOMCAT_ALIAS_DIR,
|
||||
pwd_file=pwd_file)
|
||||
for (nickname, _unused) in dogtag_reqs:
|
||||
try:
|
||||
if nickname in (
|
||||
'caSigningCert cert-pki-ca',
|
||||
'Server-Cert cert-pki-ca'
|
||||
):
|
||||
continue
|
||||
if nickname in (
|
||||
'auditSigningCert cert-pki-ca',
|
||||
'auditSigningCert cert-pki-kra',
|
||||
):
|
||||
trust = ',,P'
|
||||
else:
|
||||
trust = ',,'
|
||||
db.run_certutil(['-M',
|
||||
'-n', f"{token_name}:{nickname}",
|
||||
'-t', trust])
|
||||
except CalledProcessError as e:
|
||||
logger.debug("Modifying trust on %s failed: %s",
|
||||
nickname, e)
|
||||
|
||||
if db.has_nickname('Directory Server CA certificate'):
|
||||
db.run_certutil(['--rename',
|
||||
'-n', 'Directory Server CA certificate',
|
||||
'--new-n', 'caSigningCert cert-pki-ca'],
|
||||
raiseonerr=False)
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def remote_connection(config):
|
||||
logger.debug("Creating LDAP connection to %s", config.master_host_name)
|
||||
ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
|
||||
xmlrpc_uri = 'https://{}/ipa/xml'.format(
|
||||
ipautil.format_netloc(config.master_host_name))
|
||||
remote_api = create_api(mode=None)
|
||||
remote_api.bootstrap(in_server=True,
|
||||
context='installer',
|
||||
confdir=paths.ETC_IPA,
|
||||
ldap_uri=ldapuri,
|
||||
xmlrpc_uri=xmlrpc_uri)
|
||||
remote_api.finalize()
|
||||
return remote_api
|
||||
|
||||
|
||||
@common_cleanup
|
||||
@preserve_enrollment_state
|
||||
def promote_check(installer):
|
||||
@@ -786,7 +869,8 @@ def promote_check(installer):
|
||||
raise ScriptError("--setup-ca and --*-cert-file options are "
|
||||
"mutually exclusive")
|
||||
|
||||
if not is_ipa_client_configured(on_master=True):
|
||||
ipa_client_installed = is_ipa_client_configured(on_master=True)
|
||||
if not ipa_client_installed:
|
||||
# One-step replica installation
|
||||
if options.password and options.admin_password:
|
||||
raise ScriptError("--password and --admin-password options are "
|
||||
@@ -814,14 +898,12 @@ def promote_check(installer):
|
||||
env._bootstrap(context='installer', confdir=paths.ETC_IPA, log=None)
|
||||
env._finalize_core(**dict(constants.DEFAULT_CONFIG))
|
||||
|
||||
# pylint: disable=no-member
|
||||
xmlrpc_uri = 'https://{}/ipa/xml'.format(ipautil.format_netloc(env.host))
|
||||
api.bootstrap(in_server=True,
|
||||
context='installer',
|
||||
confdir=paths.ETC_IPA,
|
||||
ldap_uri=ipaldap.realm_to_ldapi_uri(env.realm),
|
||||
xmlrpc_uri=xmlrpc_uri)
|
||||
# pylint: enable=no-member
|
||||
api.finalize()
|
||||
|
||||
config = ReplicaConfig()
|
||||
@@ -921,26 +1003,23 @@ def promote_check(installer):
|
||||
installutils.verify_fqdn(config.master_host_name, options.no_host_dns,
|
||||
local_hostname=not container_environment)
|
||||
|
||||
if config.host_name.lower() == config.domain_name.lower():
|
||||
raise ScriptError("hostname cannot be the same as the domain name")
|
||||
|
||||
ccache = os.environ['KRB5CCNAME']
|
||||
kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
|
||||
paths.KRB5_KEYTAB,
|
||||
ccache)
|
||||
|
||||
cafile = paths.IPA_CA_CRT
|
||||
if not os.path.isfile(cafile):
|
||||
raise RuntimeError("CA cert file is not available! Please reinstall"
|
||||
"the client and try again.")
|
||||
if ipa_client_installed:
|
||||
# host was already an IPA client, refresh client cert stores to
|
||||
# ensure we have up to date CA certs.
|
||||
try:
|
||||
ipautil.run([paths.IPA_CERTUPDATE])
|
||||
except ipautil.CalledProcessError:
|
||||
raise RuntimeError("ipa-certupdate failed to refresh certs.")
|
||||
|
||||
ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
|
||||
xmlrpc_uri = 'https://{}/ipa/xml'.format(
|
||||
ipautil.format_netloc(config.master_host_name))
|
||||
remote_api = create_api(mode=None)
|
||||
remote_api.bootstrap(in_server=True,
|
||||
context='installer',
|
||||
confdir=paths.ETC_IPA,
|
||||
ldap_uri=ldapuri,
|
||||
xmlrpc_uri=xmlrpc_uri)
|
||||
remote_api.finalize()
|
||||
remote_api = remote_connection(config)
|
||||
installer._remote_api = remote_api
|
||||
|
||||
with rpc_client(remote_api) as client:
|
||||
@@ -956,6 +1035,10 @@ def promote_check(installer):
|
||||
config.master_host_name, None)
|
||||
|
||||
promotion_check_ipa_domain(conn, remote_api.env.basedn)
|
||||
hostdn = DN(('fqdn', api.env.host),
|
||||
api.env.container_host,
|
||||
api.env.basedn)
|
||||
promotion_check_host_principal_auth_ind(conn, hostdn)
|
||||
|
||||
# Make sure that domain fulfills minimal domain level
|
||||
# requirement
|
||||
@@ -981,7 +1064,7 @@ def promote_check(installer):
|
||||
raise errors.ACIError(info="Not authorized")
|
||||
|
||||
if installer._ccache is None:
|
||||
del os.environ['KRB5CCNAME']
|
||||
os.environ.pop('KRB5CCNAME', None)
|
||||
else:
|
||||
os.environ['KRB5CCNAME'] = installer._ccache
|
||||
|
||||
@@ -1012,8 +1095,8 @@ def promote_check(installer):
|
||||
if replman.get_replication_agreement(config.host_name):
|
||||
msg = ("A replication agreement for this host already exists. "
|
||||
"It needs to be removed.\n"
|
||||
"Run this command:\n"
|
||||
" %% ipa-replica-manage del {host} --force"
|
||||
"Run this command on any working server:\n"
|
||||
" %% ipa server-del {host} --force"
|
||||
.format(host=config.host_name))
|
||||
raise ScriptError(msg, rval=3)
|
||||
|
||||
@@ -1066,8 +1149,16 @@ def promote_check(installer):
|
||||
'CA', conn, preferred_cas
|
||||
)
|
||||
if ca_host is not None:
|
||||
if options.setup_ca and config.master_host_name != ca_host:
|
||||
conn.disconnect()
|
||||
del remote_api
|
||||
config.master_host_name = ca_host
|
||||
remote_api = remote_connection(config)
|
||||
installer._remote_api = remote_api
|
||||
conn = remote_api.Backend.ldap2
|
||||
conn.connect(ccache=installer._ccache)
|
||||
config.ca_host_name = ca_host
|
||||
ca_enabled = True
|
||||
ca_enabled = True # There is a CA somewhere in the topology
|
||||
if options.dirsrv_cert_files:
|
||||
logger.error("Certificates could not be provided when "
|
||||
"CA is present on some master.")
|
||||
@@ -1105,8 +1196,18 @@ def promote_check(installer):
|
||||
'KRA', conn, preferred_kras
|
||||
)
|
||||
if kra_host is not None:
|
||||
if options.setup_kra and config.master_host_name != kra_host:
|
||||
conn.disconnect()
|
||||
del remote_api
|
||||
config.master_host_name = kra_host
|
||||
remote_api = remote_connection(config)
|
||||
installer._remote_api = remote_api
|
||||
conn = remote_api.Backend.ldap2
|
||||
conn.connect(ccache=installer._ccache)
|
||||
config.kra_host_name = kra_host
|
||||
kra_enabled = True
|
||||
if options.setup_kra: # only reset ca_host if KRA is requested
|
||||
config.ca_host_name = kra_host
|
||||
kra_enabled = True # There is a KRA somewhere in the topology
|
||||
if options.setup_kra and options.server and \
|
||||
kra_host != options.server:
|
||||
# Installer was provided with a specific master
|
||||
@@ -1145,8 +1246,9 @@ def promote_check(installer):
|
||||
# check addresses here, dns module is doing own check
|
||||
no_matching_interface_for_ip_address_warning(config.ips)
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install_check(False, options, remote_api)
|
||||
# Always call adtrust.install_check
|
||||
# if --setup-adtrust is not specified, only the SID part is executed
|
||||
adtrust.install_check(False, options, remote_api)
|
||||
|
||||
except errors.ACIError:
|
||||
logger.debug("%s", traceback.format_exc())
|
||||
@@ -1170,7 +1272,7 @@ def promote_check(installer):
|
||||
if add_to_ipaservers:
|
||||
# use user's credentials when the server host is not ipaservers
|
||||
if installer._ccache is None:
|
||||
del os.environ['KRB5CCNAME']
|
||||
os.environ.pop('KRB5CCNAME', None)
|
||||
else:
|
||||
os.environ['KRB5CCNAME'] = installer._ccache
|
||||
|
||||
@@ -1179,14 +1281,14 @@ def promote_check(installer):
|
||||
config.master_host_name, config.host_name, config.realm_name,
|
||||
options.setup_ca, 389,
|
||||
options.admin_password, principal=options.principal,
|
||||
ca_cert_file=cafile)
|
||||
ca_cert_file=paths.IPA_CA_CRT)
|
||||
finally:
|
||||
if add_to_ipaservers:
|
||||
os.environ['KRB5CCNAME'] = ccache
|
||||
|
||||
installer._ca_enabled = ca_enabled
|
||||
installer._kra_enabled = kra_enabled
|
||||
installer._ca_file = cafile
|
||||
installer._ca_file = paths.IPA_CA_CRT
|
||||
installer._fstore = fstore
|
||||
installer._sstore = sstore
|
||||
installer._config = config
|
||||
@@ -1207,7 +1309,6 @@ def install(installer):
|
||||
fstore = installer._fstore
|
||||
sstore = installer._sstore
|
||||
config = installer._config
|
||||
cafile = installer._ca_file
|
||||
dirsrv_pkcs12_info = installer._dirsrv_pkcs12_info
|
||||
http_pkcs12_info = installer._http_pkcs12_info
|
||||
pkinit_pkcs12_info = installer._pkinit_pkcs12_info
|
||||
@@ -1222,6 +1323,24 @@ def install(installer):
|
||||
if tasks.configure_pkcs11_modules(fstore):
|
||||
print("Disabled p11-kit-proxy")
|
||||
|
||||
_hostname, _sep, host_domain = config.host_name.partition('.')
|
||||
fstore.backup_file(paths.KRB5_CONF)
|
||||
|
||||
# Write a new krb5.conf in case any values changed finding the
|
||||
# right server to configure against (for CA, KRA).
|
||||
logger.debug("Installing against server %s", config.master_host_name)
|
||||
configure_krb5_conf(
|
||||
cli_realm=api.env.realm,
|
||||
cli_domain=api.env.domain,
|
||||
cli_server=[config.master_host_name],
|
||||
cli_kdc=[config.master_host_name],
|
||||
dnsok=False,
|
||||
filename=paths.KRB5_CONF,
|
||||
client_domain=host_domain,
|
||||
client_hostname=config.host_name,
|
||||
configure_sssd=False
|
||||
)
|
||||
|
||||
if installer._add_to_ipaservers:
|
||||
try:
|
||||
conn.connect(ccache=installer._ccache)
|
||||
@@ -1241,18 +1360,10 @@ def install(installer):
|
||||
|
||||
try:
|
||||
conn.connect(ccache=ccache)
|
||||
|
||||
# Update and istall updated CA file
|
||||
cafile = install_ca_cert(conn, api.env.basedn, api.env.realm, cafile)
|
||||
install_ca_cert(conn, api.env.basedn, api.env.realm, cafile,
|
||||
destfile=paths.KDC_CA_BUNDLE_PEM)
|
||||
install_ca_cert(conn, api.env.basedn, api.env.realm, cafile,
|
||||
destfile=paths.CA_BUNDLE_PEM)
|
||||
|
||||
# Configure dirsrv
|
||||
ds = install_replica_ds(config, options, ca_enabled,
|
||||
remote_api,
|
||||
ca_file=cafile,
|
||||
ca_file=paths.IPA_CA_CRT,
|
||||
pkcs12_info=dirsrv_pkcs12_info,
|
||||
fstore=fstore)
|
||||
|
||||
@@ -1303,7 +1414,7 @@ def install(installer):
|
||||
auto_redirect=not options.no_ui_redirect,
|
||||
pkcs12_info=http_pkcs12_info,
|
||||
ca_is_configured=ca_enabled,
|
||||
ca_file=cafile,
|
||||
ca_file=paths.IPA_CA_CRT,
|
||||
fstore=fstore)
|
||||
|
||||
# Need to point back to ourself after the cert for HTTP is obtained
|
||||
@@ -1313,10 +1424,10 @@ def install(installer):
|
||||
otpd.create_instance('OTPD', config.host_name,
|
||||
ipautil.realm_to_suffix(config.realm_name))
|
||||
|
||||
if kra_enabled:
|
||||
if options.setup_kra and kra_enabled:
|
||||
# A KRA peer always provides a CA, too.
|
||||
mode = custodiainstance.CustodiaModes.KRA_PEER
|
||||
elif ca_enabled:
|
||||
elif options.setup_ca and ca_enabled:
|
||||
mode = custodiainstance.CustodiaModes.CA_PEER
|
||||
else:
|
||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
||||
@@ -1328,6 +1439,8 @@ def install(installer):
|
||||
options.domain_name = config.domain_name
|
||||
options.host_name = config.host_name
|
||||
options.dm_password = config.dirman_password
|
||||
# Always call ca.install() if there is a CA in the topology
|
||||
# to ensure the RA agent is present.
|
||||
ca.install(False, config, options, custodia=custodia)
|
||||
|
||||
# configure PKINIT now that all required services are in place
|
||||
@@ -1340,6 +1453,7 @@ def install(installer):
|
||||
ds.finalize_replica_config()
|
||||
|
||||
if kra_enabled:
|
||||
# The KRA installer checks for itself the status of setup_kra
|
||||
kra.install(api, config, options, custodia=custodia)
|
||||
|
||||
service.print_msg("Restarting the KDC")
|
||||
@@ -1352,8 +1466,9 @@ def install(installer):
|
||||
if options.setup_dns:
|
||||
dns.install(False, True, options, api)
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install(False, options, fstore, api)
|
||||
# Always call adtrust.install
|
||||
# if --setup-adtrust is not specified, only the SID part is executed
|
||||
adtrust.install(False, options, fstore, api)
|
||||
|
||||
if options.hidden_replica:
|
||||
# Set services to hidden
|
||||
@@ -1388,6 +1503,9 @@ def install(installer):
|
||||
'''.format(ca_servers[0]))
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
if options.setup_ca:
|
||||
clean_up_hsm_nicknames(api)
|
||||
|
||||
|
||||
def init(installer):
|
||||
installer.unattended = not installer.interactive
|
||||
|
||||
@@ -11,14 +11,13 @@ import re
|
||||
import os
|
||||
import glob
|
||||
import shutil
|
||||
import pwd
|
||||
import fileinput
|
||||
import ssl
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from augeas import Augeas
|
||||
from pkg_resources import parse_version
|
||||
|
||||
from ipalib import api, x509
|
||||
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
||||
@@ -37,6 +36,7 @@ from ipapython import ipautil, version
|
||||
from ipapython import ipaldap
|
||||
from ipapython import directivesetter
|
||||
from ipapython.dn import DN
|
||||
from ipapython.version import KRB5_BUILD_VERSION
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
from ipaserver import servroles
|
||||
@@ -173,12 +173,17 @@ def find_version(filename):
|
||||
else:
|
||||
return -1
|
||||
|
||||
def upgrade_file(sub_dict, filename, template, add=False):
|
||||
|
||||
def upgrade_file(sub_dict, filename, template, add=False, force=False):
|
||||
"""
|
||||
Get the version from the current and template files and update the
|
||||
installed configuration file if there is a new template.
|
||||
|
||||
If add is True then create a new configuration file.
|
||||
|
||||
If force is True then the version comparison is skipped. This should
|
||||
be used judiciously. It does not override add nor will it affect
|
||||
files that don't exist (version == -1).
|
||||
"""
|
||||
old = int(find_version(filename))
|
||||
new = int(find_version(template))
|
||||
@@ -200,7 +205,10 @@ def upgrade_file(sub_dict, filename, template, add=False):
|
||||
"overwritten. A backup of the original will be made.",
|
||||
filename)
|
||||
|
||||
if old < new or (add and old == 0):
|
||||
if force:
|
||||
logger.error("Forcing update of template %s", template)
|
||||
|
||||
if ((old < new) or (add and old == 0)) or force:
|
||||
backup_file(filename, new)
|
||||
update_conf(sub_dict, filename, template)
|
||||
logger.info("Upgraded %s to version %d", filename, new)
|
||||
@@ -359,6 +367,21 @@ def upgrade_adtrust_config():
|
||||
else:
|
||||
logger.warning("Error updating Samba registry: %s", e)
|
||||
|
||||
logger.info("[Change 'server role' from "
|
||||
"'CLASSIC PRIMARY DOMAIN CONTROLLER' "
|
||||
"to 'IPA PRIMARY DOMAIN CONTROLLER' in Samba configuration]")
|
||||
|
||||
args = [paths.NET, "conf", "setparm", "global",
|
||||
"server role", "IPA PRIMARY DOMAIN CONTROLLER"]
|
||||
|
||||
try:
|
||||
ipautil.run(args)
|
||||
except ipautil.CalledProcessError as e:
|
||||
# Only report an error if return code is not 255
|
||||
# which indicates that the new server role is not supported
|
||||
# and we don't need to do anything
|
||||
if e.returncode != 255:
|
||||
logger.warning("Error updating Samba registry: %s", e)
|
||||
|
||||
def ca_configure_profiles_acl(ca):
|
||||
logger.info('[Authorizing RA Agent to modify profiles]')
|
||||
@@ -389,15 +412,34 @@ def ca_enable_ldap_profile_subsystem(ca):
|
||||
needs_update = False
|
||||
directive = None
|
||||
try:
|
||||
for i in range(15):
|
||||
i = 0
|
||||
while True:
|
||||
# find profile subsystem
|
||||
directive = "subsystem.{}.id".format(i)
|
||||
value = directivesetter.get_directive(
|
||||
paths.CA_CS_CFG_PATH,
|
||||
directive,
|
||||
separator='=')
|
||||
if not value:
|
||||
logger.error('Unable to find profile subsystem in %s',
|
||||
paths.CA_CS_CFG_PATH)
|
||||
return False
|
||||
if value != 'profile':
|
||||
i = i + 1
|
||||
continue
|
||||
|
||||
# check profile subsystem class name
|
||||
directive = "subsystem.{}.class".format(i)
|
||||
value = directivesetter.get_directive(
|
||||
paths.CA_CS_CFG_PATH,
|
||||
directive,
|
||||
separator='=')
|
||||
if value == 'com.netscape.cmscore.profile.ProfileSubsystem':
|
||||
if value != 'com.netscape.cmscore.profile.LDAPProfileSubsystem':
|
||||
needs_update = True
|
||||
break
|
||||
|
||||
# break after finding profile subsystem
|
||||
break
|
||||
|
||||
except OSError as e:
|
||||
logger.error('Cannot read CA configuration file "%s": %s',
|
||||
paths.CA_CS_CFG_PATH, e)
|
||||
@@ -440,6 +482,16 @@ def ca_ensure_lightweight_cas_container(ca):
|
||||
return cainstance.ensure_lightweight_cas_container()
|
||||
|
||||
|
||||
def ca_enable_lightweight_ca_monitor(ca):
|
||||
logger.info('[Enabling LWCA monitor]')
|
||||
|
||||
if not ca.is_configured():
|
||||
logger.info('CA is not configured')
|
||||
return False
|
||||
|
||||
return cainstance.enable_lightweight_ca_monitor()
|
||||
|
||||
|
||||
def ca_add_default_ocsp_uri(ca):
|
||||
logger.info('[Adding default OCSP URI configuration]')
|
||||
if not ca.is_configured():
|
||||
@@ -484,20 +536,6 @@ def ca_disable_publish_cert(ca):
|
||||
return True # restart needed
|
||||
|
||||
|
||||
def upgrade_ca_audit_cert_validity(ca):
|
||||
"""
|
||||
Update the Dogtag audit signing certificate.
|
||||
|
||||
Returns True if restart is needed, False otherwise.
|
||||
"""
|
||||
logger.info('[Verifying that CA audit signing cert has 2 year validity]')
|
||||
if ca.is_configured():
|
||||
return ca.set_audit_renewal()
|
||||
else:
|
||||
logger.info('CA is not configured')
|
||||
return False
|
||||
|
||||
|
||||
def ca_initialize_hsm_state(ca):
|
||||
"""Initializse HSM state as False / internal token
|
||||
"""
|
||||
@@ -509,6 +547,24 @@ def ca_initialize_hsm_state(ca):
|
||||
ca.set_hsm_state(config)
|
||||
|
||||
|
||||
def dnssec_set_openssl_engine(dnskeysyncd):
|
||||
"""
|
||||
Setup OpenSSL engine for BIND
|
||||
"""
|
||||
if constants.NAMED_OPENSSL_ENGINE is None:
|
||||
return False
|
||||
|
||||
if sysupgrade.get_upgrade_state('dns', 'openssl_engine'):
|
||||
return False
|
||||
|
||||
logger.info('[Set OpenSSL engine for BIND]')
|
||||
dnskeysyncd.setup_named_openssl_conf()
|
||||
dnskeysyncd.setup_named_sysconfig()
|
||||
dnskeysyncd.setup_ipa_dnskeysyncd_sysconfig()
|
||||
sysupgrade.set_upgrade_state('dns', 'openssl_engine', True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def certificate_renewal_update(ca, kra, ds, http):
|
||||
"""
|
||||
@@ -626,9 +682,9 @@ def certificate_renewal_update(ca, kra, ds, http):
|
||||
|
||||
# Ok, now we need to stop tracking, then we can start tracking them
|
||||
# again with new configuration:
|
||||
ca.stop_tracking_certificates(stop_certmonger=False)
|
||||
ca.stop_tracking_certificates()
|
||||
if kra.is_installed():
|
||||
kra.stop_tracking_certificates(stop_certmonger=False)
|
||||
kra.stop_tracking_certificates()
|
||||
ds.stop_tracking_certificates(serverid)
|
||||
http.stop_tracking_certificates()
|
||||
|
||||
@@ -670,7 +726,7 @@ def http_certificate_ensure_ipa_ca_dnsname(http):
|
||||
|
||||
try:
|
||||
cert.match_hostname(expect)
|
||||
except ssl.CertificateError:
|
||||
except x509.ssl_match_hostname.CertificateError:
|
||||
if certs.is_ipa_issued_cert(api, cert):
|
||||
request_id = certmonger.get_request_id(
|
||||
{'cert-file': paths.HTTPD_CERT_FILE})
|
||||
@@ -719,8 +775,7 @@ def copy_crl_file(old_path, new_path=None):
|
||||
os.symlink(realpath, new_path)
|
||||
else:
|
||||
shutil.copy2(old_path, new_path)
|
||||
pent = pwd.getpwnam(constants.PKI_USER)
|
||||
os.chown(new_path, pent.pw_uid, pent.pw_gid)
|
||||
constants.PKI_USER.chown(new_path)
|
||||
|
||||
tasks.restore_context(new_path)
|
||||
|
||||
@@ -899,7 +954,7 @@ def uninstall_dogtag_9(ds, http):
|
||||
ca = dogtaginstance.DogtagInstance(
|
||||
api.env.realm, "CA", "certificate server",
|
||||
nss_db=paths.VAR_LIB_PKI_CA_ALIAS_DIR)
|
||||
ca.stop_tracking_certificates(False)
|
||||
ca.stop_tracking_certificates()
|
||||
|
||||
if serverid is not None:
|
||||
# drop the trailing / off the config_dirname so the directory
|
||||
@@ -1068,8 +1123,7 @@ def update_http_keytab(http):
|
||||
'Cannot remove file %s (%s). Please remove the file manually.',
|
||||
paths.OLD_IPA_KEYTAB, e
|
||||
)
|
||||
pent = pwd.getpwnam(http.keytab_user)
|
||||
os.chown(http.keytab, pent.pw_uid, pent.pw_gid)
|
||||
http.keytab_user.chown(http.keytab)
|
||||
|
||||
|
||||
def ds_enable_sidgen_extdom_plugins(ds):
|
||||
@@ -1079,11 +1133,24 @@ def ds_enable_sidgen_extdom_plugins(ds):
|
||||
|
||||
if sysupgrade.get_upgrade_state('ds', 'enable_ds_sidgen_extdom_plugins'):
|
||||
logger.debug('sidgen and extdom plugins are enabled already')
|
||||
return
|
||||
return False
|
||||
|
||||
ds.add_sidgen_plugin(api.env.basedn)
|
||||
ds.add_extdom_plugin(api.env.basedn)
|
||||
sysupgrade.set_upgrade_state('ds', 'enable_ds_sidgen_extdom_plugins', True)
|
||||
return True
|
||||
|
||||
|
||||
def ds_enable_graceperiod_plugin(ds):
|
||||
"""Graceperiod is a newer DS plugin so needs to be enabled on upgrade"""
|
||||
if sysupgrade.get_upgrade_state('ds', 'enable_ds_graceperiod_plugin'):
|
||||
logger.debug('graceperiod is enabled already')
|
||||
return False
|
||||
|
||||
ds.config_graceperiod_module()
|
||||
sysupgrade.set_upgrade_state('ds', 'enable_ds_graceperiod_plugin', True)
|
||||
return True
|
||||
|
||||
|
||||
def ca_upgrade_schema(ca):
|
||||
logger.info('[Upgrading CA schema]')
|
||||
@@ -1091,9 +1158,24 @@ def ca_upgrade_schema(ca):
|
||||
logger.info('CA is not configured')
|
||||
return False
|
||||
|
||||
# ACME schema file moved in pki-server-10.9.0-0.3
|
||||
# ACME database connections were abstrated in pki-acme-10.10.0
|
||||
for path in [
|
||||
'/usr/share/pki/acme/conf/database/ds/schema.ldif',
|
||||
'/usr/share/pki/acme/conf/database/ldap/schema.ldif',
|
||||
'/usr/share/pki/acme/database/ldap/schema.ldif',
|
||||
]:
|
||||
if os.path.exists(path):
|
||||
acme_schema_ldif = path
|
||||
break
|
||||
else:
|
||||
logger.info('ACME schema is not available')
|
||||
return False
|
||||
|
||||
schema_files=[
|
||||
'/usr/share/pki/server/conf/schema-certProfile.ldif',
|
||||
'/usr/share/pki/server/conf/schema-authority.ldif',
|
||||
acme_schema_ldif,
|
||||
]
|
||||
try:
|
||||
modified = schemaupdate.update_schema(schema_files, ldapi=True)
|
||||
@@ -1122,6 +1204,16 @@ def add_default_caacl(ca):
|
||||
sysupgrade.set_upgrade_state('caacl', 'add_default_caacl', True)
|
||||
|
||||
|
||||
def add_agent_to_security_domain_admins():
|
||||
user_dn = DN(('uid', "ipara"), ('ou', 'People'), ('o', 'ipaca'))
|
||||
group_dn = DN(('cn', 'Security Domain Administrators'), ('ou', 'groups'),
|
||||
('o', 'ipaca'))
|
||||
try:
|
||||
api.Backend.ldap2.add_entry_to_group(user_dn, group_dn, 'uniqueMember')
|
||||
except ipalib.errors.AlreadyGroupMember:
|
||||
pass
|
||||
|
||||
|
||||
def setup_pkinit(krb):
|
||||
logger.info("[Setup PKINIT]")
|
||||
|
||||
@@ -1217,6 +1309,29 @@ def enable_server_snippet():
|
||||
tasks.restore_context(paths.KRB5_FREEIPA_SERVER)
|
||||
|
||||
|
||||
def setup_kpasswd_server(krb):
|
||||
logger.info("[Setup kpasswd_server]")
|
||||
aug = Augeas(
|
||||
flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD,
|
||||
loadpath=paths.USR_SHARE_IPA_DIR,
|
||||
)
|
||||
try:
|
||||
aug.transform("IPAKrb5", paths.KRB5_CONF)
|
||||
aug.load()
|
||||
|
||||
kpass_srv_path = "/files{}/realms/{}/kpasswd_server"
|
||||
kpass_srv_path = kpass_srv_path.format(paths.KRB5_CONF, krb.realm)
|
||||
|
||||
if aug.match(kpass_srv_path):
|
||||
return
|
||||
|
||||
aug.set(kpass_srv_path, f"{krb.fqdn}:464")
|
||||
aug.save()
|
||||
|
||||
finally:
|
||||
aug.close()
|
||||
|
||||
|
||||
def ntpd_cleanup(fqdn, fstore):
|
||||
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||
timeconf.restore_forced_timeservices(sstore, 'ntpd')
|
||||
@@ -1228,7 +1343,7 @@ def ntpd_cleanup(fqdn, fstore):
|
||||
try:
|
||||
instance.disable()
|
||||
instance.stop()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.debug("Service ntpd was not disabled or stopped")
|
||||
|
||||
for ntpd_file in [paths.NTP_CONF, paths.NTP_STEP_TICKERS,
|
||||
@@ -1395,13 +1510,20 @@ def upgrade_bind(fstore):
|
||||
logger.info("DNS service is not configured")
|
||||
return False
|
||||
|
||||
# get rid of old upgrade states
|
||||
bind_switch_service(bind)
|
||||
|
||||
# get rid of old states
|
||||
bind_old_states(bind)
|
||||
bind_old_upgrade_states()
|
||||
|
||||
# only upgrade with drop-in is missing and /etc/resolv.conf is a link to
|
||||
# resolve1's stub resolver config file.
|
||||
has_resolved_ipa_conf = os.path.isfile(paths.SYSTEMD_RESOLVED_IPA_CONF)
|
||||
if not has_resolved_ipa_conf and detect_resolve1_resolv_conf():
|
||||
ip_addresses = installutils.resolve_ip_addresses_nss(
|
||||
api.env.host
|
||||
)
|
||||
bind.ip_addresses = ip_addresses
|
||||
bind.setup_resolv_conf()
|
||||
logger.info("Updated systemd-resolved configuration")
|
||||
|
||||
@@ -1412,6 +1534,9 @@ def upgrade_bind(fstore):
|
||||
else:
|
||||
bind_started = False
|
||||
|
||||
# create or update autobind entry
|
||||
bind.setup_autobind()
|
||||
|
||||
try:
|
||||
changed = bind.setup_named_conf(backup=True)
|
||||
if changed:
|
||||
@@ -1428,6 +1553,38 @@ def upgrade_bind(fstore):
|
||||
return changed
|
||||
|
||||
|
||||
def bind_switch_service(bind):
|
||||
"""
|
||||
Mask either named or named-pkcs11, we need to run only one,
|
||||
running both can cause unexpected errors.
|
||||
"""
|
||||
named_conflict_name = bind.named_conflict.systemd_name
|
||||
named_conflict_old = sysupgrade.get_upgrade_state('dns', 'conflict_named')
|
||||
|
||||
# nothing changed
|
||||
if named_conflict_old and named_conflict_old == named_conflict_name:
|
||||
return False
|
||||
|
||||
bind.switch_service()
|
||||
|
||||
sysupgrade.set_upgrade_state('dns', 'conflict_named', named_conflict_name)
|
||||
return True
|
||||
|
||||
|
||||
def bind_old_states(bind):
|
||||
"""Remove old states
|
||||
"""
|
||||
# no longer used states
|
||||
old_states = [
|
||||
"enabled",
|
||||
"running",
|
||||
"named-regular-enabled",
|
||||
"named-regular-running",
|
||||
]
|
||||
for state in old_states:
|
||||
bind.delete_state(state)
|
||||
|
||||
|
||||
def bind_old_upgrade_states():
|
||||
"""Remove old upgrade states
|
||||
"""
|
||||
@@ -1453,6 +1610,51 @@ def bind_old_upgrade_states():
|
||||
sysupgrade.remove_upgrade_state("dns", state)
|
||||
|
||||
|
||||
def ca_update_acme_configuration(ca, fqdn):
|
||||
"""
|
||||
Re-apply the templates in case anyting has been updated.
|
||||
"""
|
||||
logger.info('[Updating ACME configuration]')
|
||||
if not os.path.isdir(os.path.join(paths.PKI_TOMCAT, 'acme')):
|
||||
logger.info('ACME is not deployed, skipping')
|
||||
return
|
||||
|
||||
if not os.path.exists(paths.PKI_ACME_ISSUER_CONF):
|
||||
logger.info('ACME configuration file %s is missing',
|
||||
paths.PKI_ACME_ISSUER_CONF)
|
||||
return
|
||||
|
||||
password = directivesetter.get_directive(
|
||||
paths.PKI_ACME_ISSUER_CONF,
|
||||
'password',
|
||||
separator='=')
|
||||
acme_user = ca.acme_uid(fqdn)
|
||||
sub_dict = dict(
|
||||
FQDN=fqdn,
|
||||
USER=acme_user,
|
||||
PASSWORD=password,
|
||||
)
|
||||
for template_name, target in cainstance.ACME_CONFIG_FILES:
|
||||
upgrade_file(sub_dict, target,
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR,
|
||||
template_name))
|
||||
|
||||
|
||||
def set_default_grace_time():
|
||||
dn = DN(
|
||||
('cn', 'global_policy'), ('cn', api.env.realm),
|
||||
('cn', 'kerberos'), api.env.basedn
|
||||
)
|
||||
entry = api.Backend.ldap2.get_entry(dn)
|
||||
for (a,_v) in entry.items():
|
||||
if a.lower() == 'passwordgracelimit':
|
||||
return
|
||||
|
||||
entry['objectclass'].append('ipapwdpolicy')
|
||||
entry['passwordgracelimit'] = -1
|
||||
api.Backend.ldap2.update_entry(entry)
|
||||
|
||||
|
||||
def upgrade_configuration():
|
||||
"""
|
||||
Execute configuration upgrade of the IPA services
|
||||
@@ -1505,6 +1707,7 @@ def upgrade_configuration():
|
||||
IPA_CCACHES=paths.IPA_CCACHES,
|
||||
IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET,
|
||||
KDCPROXY_CONFIG=paths.KDCPROXY_CONFIG,
|
||||
DOMAIN=api.env.domain,
|
||||
)
|
||||
|
||||
subject_base = find_subject_base()
|
||||
@@ -1548,20 +1751,26 @@ def upgrade_configuration():
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR,
|
||||
"ipa-kdc-proxy.conf.template"))
|
||||
if ca.is_configured():
|
||||
# Ensure that the drop-in file is present
|
||||
if not os.path.isfile(paths.SYSTEMD_PKI_TOMCAT_IPA_CONF):
|
||||
ca.add_ipa_wait()
|
||||
|
||||
# Handle upgrade of AJP connector configuration
|
||||
ca.secure_ajp_connector()
|
||||
rewrite = ca.secure_ajp_connector()
|
||||
if ca.ajp_secret:
|
||||
sub_dict['DOGTAG_AJP_SECRET'] = "secret={}".format(
|
||||
ca.ajp_secret)
|
||||
else:
|
||||
sub_dict['DOGTAG_AJP_SECRET'] = ''
|
||||
|
||||
upgrade_file(
|
||||
sub_dict,
|
||||
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR,
|
||||
"ipa-pki-proxy.conf.template"),
|
||||
add=True)
|
||||
# force=True will ensure the secret is updated if it changes
|
||||
if rewrite:
|
||||
upgrade_file(
|
||||
sub_dict,
|
||||
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR,
|
||||
"ipa-pki-proxy.conf.template"),
|
||||
add=True, force=True)
|
||||
else:
|
||||
if os.path.isfile(paths.HTTPD_IPA_PKI_PROXY_CONF):
|
||||
os.remove(paths.HTTPD_IPA_PKI_PROXY_CONF)
|
||||
@@ -1585,6 +1794,18 @@ def upgrade_configuration():
|
||||
else:
|
||||
logger.info('ephemeralRequest is already enabled')
|
||||
|
||||
if tasks.is_fips_enabled():
|
||||
logger.info('[Ensuring KRA OAEP wrap algo is enabled in FIPS]')
|
||||
value = directivesetter.get_directive(
|
||||
paths.KRA_CS_CFG_PATH,
|
||||
'keyWrap.useOAEP',
|
||||
separator='=')
|
||||
if value is None or value.lower() != 'true':
|
||||
logger.info('Use the OAEP key wrap algo')
|
||||
kra.enable_oaep_wrap_algo()
|
||||
else:
|
||||
logger.info('OAEP key wrap algo is already enabled')
|
||||
|
||||
# several upgrade steps require running CA. If CA is configured,
|
||||
# always run ca.start() because we need to wait until CA is really ready
|
||||
# by checking status using http
|
||||
@@ -1636,7 +1857,13 @@ def upgrade_configuration():
|
||||
ds.realm = api.env.realm
|
||||
ds.suffix = ipautil.realm_to_suffix(api.env.realm)
|
||||
|
||||
ds_enable_sidgen_extdom_plugins(ds)
|
||||
if any([
|
||||
ds_enable_sidgen_extdom_plugins(ds),
|
||||
ds_enable_graceperiod_plugin(ds)
|
||||
]):
|
||||
ds.restart(ds.serverid)
|
||||
|
||||
set_default_grace_time()
|
||||
|
||||
if not http.is_kdcproxy_configured():
|
||||
logger.info('[Enabling KDC Proxy]')
|
||||
@@ -1658,12 +1885,12 @@ def upgrade_configuration():
|
||||
(otpdinstance.OtpdInstance(), 'OTPD'),
|
||||
)
|
||||
|
||||
for service, ldap_name in simple_service_list:
|
||||
for svc, ldap_name in simple_service_list:
|
||||
try:
|
||||
if not service.is_configured():
|
||||
service.create_instance(ldap_name, fqdn,
|
||||
ipautil.realm_to_suffix(api.env.realm),
|
||||
realm=api.env.realm)
|
||||
if not svc.is_configured():
|
||||
svc.create_instance(ldap_name, fqdn,
|
||||
ipautil.realm_to_suffix(api.env.realm),
|
||||
realm=api.env.realm)
|
||||
except ipalib.errors.DuplicateEntry:
|
||||
pass
|
||||
|
||||
@@ -1673,6 +1900,10 @@ def upgrade_configuration():
|
||||
if not dnskeysyncd.is_configured():
|
||||
dnskeysyncd.create_instance(fqdn, api.env.realm)
|
||||
dnskeysyncd.start_dnskeysyncd()
|
||||
else:
|
||||
if dnssec_set_openssl_engine(dnskeysyncd):
|
||||
dnskeysyncd.start_dnskeysyncd()
|
||||
dnskeysyncd.set_dyndb_ldap_workdir_permissions()
|
||||
|
||||
cleanup_kdc(fstore)
|
||||
cleanup_adtrust(fstore)
|
||||
@@ -1684,15 +1915,18 @@ def upgrade_configuration():
|
||||
custodia = custodiainstance.CustodiaInstance(api.env.host, api.env.realm)
|
||||
custodia.upgrade_instance()
|
||||
|
||||
# Don't include schema upgrades in restart consideration, see
|
||||
# https://pagure.io/freeipa/issue/9204
|
||||
ca_upgrade_schema(ca)
|
||||
|
||||
ca_restart = any([
|
||||
ca_restart,
|
||||
ca_upgrade_schema(ca),
|
||||
upgrade_ca_audit_cert_validity(ca),
|
||||
certificate_renewal_update(ca, kra, ds, http),
|
||||
ca_enable_pkix(ca),
|
||||
ca_configure_profiles_acl(ca),
|
||||
ca_configure_lightweight_ca_acls(ca),
|
||||
ca_ensure_lightweight_cas_container(ca),
|
||||
ca_enable_lightweight_ca_monitor(ca),
|
||||
ca_add_default_ocsp_uri(ca),
|
||||
ca_disable_publish_cert(ca),
|
||||
])
|
||||
@@ -1719,7 +1953,10 @@ def upgrade_configuration():
|
||||
cainstance.repair_profile_caIPAserviceCert()
|
||||
ca.setup_lightweight_ca_key_retrieval()
|
||||
cainstance.ensure_ipa_authority_entry()
|
||||
ca.setup_acme()
|
||||
ca_update_acme_configuration(ca, fqdn)
|
||||
ca_initialize_hsm_state(ca)
|
||||
add_agent_to_security_domain_admins()
|
||||
|
||||
migrate_to_authselect()
|
||||
add_systemd_user_hbac()
|
||||
@@ -1750,11 +1987,22 @@ def upgrade_configuration():
|
||||
setup_spake(krb)
|
||||
setup_pkinit(krb)
|
||||
enable_server_snippet()
|
||||
setup_kpasswd_server(krb)
|
||||
|
||||
if KRB5_BUILD_VERSION >= parse_version('1.20'):
|
||||
krb.pac_tkt_sign_support_enable()
|
||||
|
||||
# Must be executed after certificate_renewal_update
|
||||
# (see function docstring for details)
|
||||
http_certificate_ensure_ipa_ca_dnsname(http)
|
||||
|
||||
# Convert configuredService to either enabledService or hiddenService
|
||||
# depending on the state of the server role. This is to fix situations
|
||||
# when deployment has happened before introduction of hidden replicas
|
||||
# as those services will stay as configuredService and will not get
|
||||
# started after upgrade, rendering the system non-functioning
|
||||
service.sync_services_state(fqdn)
|
||||
|
||||
if not ds_running:
|
||||
ds.stop(ds.serverid)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user