freeipa/ipaserver/install/kra.py
Rob Crittenden 7ab1bcb2d3 Re-organize HSM validation to be more consistent/less duplication
hsm_validator() was more or less bolted in place late in the
development cycle in in order to catch some of the more common
problems: bad token name, bad password, etc.

There was a fair bit of duplication and had the side-effect of not
reading in the token password from the --token-password-file option
in some cases.

This patch also re-adds a lost feature where an exception is raised if
both the --token-password and --token-password-file options are passed
in.

This also needs to be enforced on initial server, replica and when
called by ipa-kra-install. Given that each has a unique subject of
options some duplication remains.

Fixes: https://pagure.io/freeipa/issue/9603

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2024-07-15 10:26:54 -04:00

224 lines
7.0 KiB
Python

#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
"""
KRA installer module
"""
from __future__ import absolute_import
import logging
import os
from ipalib import api
from ipalib.kinit import kinit_keytab
from ipaplatform import services
from ipaplatform.paths import paths
from ipapython import ipautil
from ipapython.admintool import ScriptError
from ipapython.install.core import group
from ipaserver.install import ca, cainstance
from ipaserver.install import krainstance
from ipaserver.install import dsinstance
from ipaserver.install import installutils
from ipaserver.install import service as _service
from . import dogtag
logger = logging.getLogger(__name__)
def install_check(api, replica_config, options):
if replica_config is not None and not replica_config.setup_kra:
return
kra = krainstance.KRAInstance(api.env.realm)
if kra.is_installed():
raise RuntimeError("KRA is already installed.")
if not options.setup_ca:
if cainstance.is_ca_installed_locally():
if api.env.dogtag_version >= 10:
# correct dogtag version of CA installed
pass
else:
raise RuntimeError(
"Dogtag must be version 10.2 or above to install KRA")
else:
raise RuntimeError(
"Dogtag CA is not installed. Please install the CA first")
if replica_config is not None:
if not api.Command.kra_is_enabled()['result']:
raise RuntimeError(
"KRA is not installed on the master system. Please use "
"'ipa-kra-install' command to install the first instance.")
if api.env.ca_host is not None and api.env.ca_host != api.env.host:
raise RuntimeError(
"KRA can not be installed when 'ca_host' is overriden in "
"IPA configuration file.")
# There are three scenarios for installing a KRA
# 1. At install time of the initial server
# 2. Using ipa-kra-install
# 3. At install time of a replica
#
# These tests are done in reverse order. If we are doing a
# replica install we can check the remote CA.
#
# If we are running ipa-kra-install then there must be a CA
# use that.
#
# If initial install we either have the token options or we don't.
cai = cainstance.CAInstance()
if replica_config is not None:
(token_name, token_library_path) = ca.lookup_hsm_configuration(api)
elif cai.is_configured() and cai.hsm_enabled:
(token_name, token_library_path) = ca.lookup_hsm_configuration(api)
elif 'token_name' in options.__dict__:
token_name = options.token_name
token_library_path = options.token_library_path
else:
token_name = None
if replica_config is not None:
if (
token_name
and options.token_password_file
and options.token_password
):
raise ScriptError(
"token-password and token-password-file are mutually exclusive"
)
if options.token_password_file:
with open(options.token_password_file, "r") as fd:
options.token_password = fd.readline().strip()
if (
token_name
and not options.token_password_file
and not options.token_password
):
if options.unattended:
raise ScriptError("HSM token password required")
token_password = installutils.read_password(
f"HSM token '{token_name}'", confirm=False
)
if token_password is None:
raise ScriptError("HSM token password required")
else:
options.token_password = token_password
if token_name:
ca.hsm_validator(token_name, token_library_path, options.token_password)
def install(api, replica_config, options, custodia):
if replica_config is None:
if not options.setup_kra:
return
realm_name = api.env.realm
dm_password = options.dm_password
host_name = api.env.host
subject_base = dsinstance.DsInstance().find_subject_base()
pkcs12_info = None
master_host = None
promote = False
else:
if not replica_config.setup_kra:
return
cai = cainstance.CAInstance()
if not cai.hsm_enabled:
krafile = os.path.join(replica_config.dir, 'kracert.p12')
with ipautil.private_ccache():
ccache = os.environ['KRB5CCNAME']
kinit_keytab(
'host/{env.host}@{env.realm}'.format(env=api.env),
paths.KRB5_KEYTAB,
ccache)
custodia.get_kra_keys(
krafile,
replica_config.dirman_password)
else:
krafile = None
realm_name = replica_config.realm_name
dm_password = replica_config.dirman_password
host_name = replica_config.host_name
subject_base = replica_config.subject_base
pkcs12_info = (krafile,)
master_host = replica_config.kra_host_name
promote = True
ca_subject = ca.lookup_ca_subject(api, subject_base)
kra = krainstance.KRAInstance(realm_name)
kra.configure_instance(
realm_name, host_name, dm_password, dm_password,
subject_base=subject_base,
ca_subject=ca_subject,
pkcs12_info=pkcs12_info,
master_host=master_host,
promote=promote,
pki_config_override=options.pki_config_override,
token_password=options.token_password
)
_service.print_msg("Restarting the directory server")
ds = dsinstance.DsInstance()
ds.restart()
kra.enable_client_auth_to_db()
# Restart apache for new proxy config file
services.knownservices.httpd.restart(capture_output=True)
# Restarted named to restore bind-dyndb-ldap operation, see
# https://pagure.io/freeipa/issue/5813
named = services.knownservices.named # alias for current named
if named.is_running():
named.restart(capture_output=True)
def uninstall_check(options):
"""IPA needs to be running so pkidestroy can unregister KRA"""
kra = krainstance.KRAInstance(api.env.realm)
if not kra.is_installed():
return
result = ipautil.run([paths.IPACTL, 'status'],
raiseonerr=False)
if result.returncode not in [0, 4]:
try:
logger.info(
"Starting services to unregister KRA from security domain")
ipautil.run([paths.IPACTL, 'start'])
except Exception:
logger.info("Re-starting IPA failed, continuing uninstall")
def uninstall():
kra = krainstance.KRAInstance(api.env.realm)
kra.stop_tracking_certificates()
if kra.is_installed():
kra.uninstall()
@group
class KRAInstallInterface(dogtag.DogtagInstallInterface):
"""
Interface of the KRA installer
Knobs defined here will be available in:
* ipa-server-install
* ipa-replica-prepare
* ipa-replica-install
* ipa-kra-install
"""
description = "KRA"