diff options
author | Jan Cholasta <jcholast@redhat.com> | 2016-11-09 12:44:22 +0100 |
---|---|---|
committer | Jan Cholasta <jcholast@redhat.com> | 2016-11-11 12:17:25 +0100 |
commit | a8fdb8de8248fe24f382e44b05293405b0b309ac (patch) | |
tree | 01054c439fb2d64506951f18d163bf341ef869a7 /ipaserver/install/server | |
parent | 08a446a6bc516936497c1e0f278a699148f6330c (diff) | |
download | freeipa-a8fdb8de8248fe24f382e44b05293405b0b309ac.tar.gz freeipa-a8fdb8de8248fe24f382e44b05293405b0b309ac.tar.xz freeipa-a8fdb8de8248fe24f382e44b05293405b0b309ac.zip |
install: introduce installer class hierarchy
Add class hierarchy which allows inherting knob definitions between the
various client and server install scripts.
https://fedorahosted.org/freeipa/ticket/6392
Reviewed-By: Martin Basti <mbasti@redhat.com>
Diffstat (limited to 'ipaserver/install/server')
-rw-r--r-- | ipaserver/install/server/__init__.py | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py index da2ceec9c..a8b56d6fe 100644 --- a/ipaserver/install/server/__init__.py +++ b/ipaserver/install/server/__init__.py @@ -2,7 +2,539 @@ # Copyright (C) 2015 FreeIPA Contributors see COPYING for license # +""" +Server installer module +""" + +from __future__ import print_function + +import collections +import os.path +import random + +from ipaclient.install import client +from ipalib import constants +from ipalib.install.service import (enroll_only, + master_install_only, + prepare_only, + replica_install_only) +from ipalib.util import validate_domain_name +from ipapython import ipautil +from ipapython.dnsutil import check_zone_overlap +from ipapython.install import typing +from ipapython.install.core import knob + +from .install import validate_admin_password, validate_dm_password from .install import Server from .replicainstall import Replica from .upgrade import upgrade_check, upgrade + +from .. import ca, conncheck, dns, kra + + +class ServerInstallInterface(client.ClientInstallInterface, + ca.CAInstallInterface, + kra.KRAInstallInterface, + dns.DNSInstallInterface, + conncheck.ConnCheckInterface): + """ + Interface of server installers + + Knobs defined here will be available in: + * ipa-server-install + * ipa-replica-prepare + * ipa-replica-install + """ + + force_join = False + kinit_attempts = 1 + fixed_primary = True + ntp_servers = None + force_ntpd = False + permit = False + enable_dns_updates = False + no_krb5_offline_passwords = False + preserve_sssd = False + + domain_name = knob( + bases=client.ClientInstallInterface.domain_name, + # pylint: disable=no-member + cli_names=(list(client.ClientInstallInterface.domain_name.cli_names) + + ['-n']), + ) + domain_name = replica_install_only(domain_name) + + new_domain_name = knob( + bases=client.ClientInstallInterface.domain_name, + cli_names=['--domain', '-n'], + cli_metavar='DOMAIN_NAME', + ) + new_domain_name = master_install_only(new_domain_name) + + @new_domain_name.validator + def new_domain_name(self, value): + validate_domain_name(value) + if (self.setup_dns and + not self.allow_zone_overlap): # pylint: disable=no-member + print("Checking DNS domain %s, please wait ..." % value) + check_zone_overlap(value, False) + + servers = knob( + bases=client.ClientInstallInterface.servers, + description="fully qualified name of IPA server to enroll to", + ) + servers = enroll_only(servers) + + realm_name = knob( + bases=client.ClientInstallInterface.realm_name, + cli_names=(list(client.ClientInstallInterface.realm_name.cli_names) + + ['-r']), + ) + + host_name = knob( + bases=client.ClientInstallInterface.host_name, + description="fully qualified name of this host", + ) + + ca_cert_files = knob( + bases=client.ClientInstallInterface.ca_cert_files, + description="File containing CA certificates for the service " + "certificate files", + cli_deprecated_names='--root-ca-file', + ) + ca_cert_files = prepare_only(ca_cert_files) + + new_dm_password = knob( + str, None, + sensitive=True, + description="Directory Manager password", + cli_names='--dm-password', + cli_metavar='DM_PASSWORD', + ) + new_dm_password = master_install_only(new_dm_password) + + @new_dm_password.validator + def new_dm_password(self, value): + validate_dm_password(value) + + ip_addresses = knob( + bases=client.ClientInstallInterface.ip_addresses, + description="Server IP Address. This option can be used multiple " + "times", + ) + + principal = knob( + bases=client.ClientInstallInterface.principal, + description="User Principal allowed to promote replicas and join IPA " + "realm", + cli_names=(list(client.ClientInstallInterface.principal.cli_names) + + ['-P']), + ) + principal = replica_install_only(principal) + + admin_password = knob( + bases=client.ClientInstallInterface.admin_password, + description="Kerberos password for the specified admin principal", + ) + admin_password = replica_install_only(admin_password) + + new_admin_password = knob( + str, None, + sensitive=True, + description="admin user kerberos password", + cli_names='--admin-password', + cli_metavar='ADMIN_PASSWORD', + ) + new_admin_password = master_install_only(new_admin_password) + + @new_admin_password.validator + def new_admin_password(self, value): + validate_admin_password(value) + + master_password = knob( + str, None, + sensitive=True, + deprecated=True, + description="kerberos master password (normally autogenerated)", + ) + master_password = master_install_only(master_password) + + domain_level = knob( + int, constants.MAX_DOMAIN_LEVEL, + description="IPA domain level", + deprecated=True, + ) + domain_level = master_install_only(domain_level) + + @domain_level.validator + def domain_level(self, value): + # Check that Domain Level is within the allowed range + if value < constants.MIN_DOMAIN_LEVEL: + raise ValueError( + "Domain Level cannot be lower than {0}".format( + constants.MIN_DOMAIN_LEVEL)) + elif value > constants.MAX_DOMAIN_LEVEL: + raise ValueError( + "Domain Level cannot be higher than {0}".format( + constants.MAX_DOMAIN_LEVEL)) + + setup_ca = knob( + None, + description="configure a dogtag CA", + ) + setup_ca = enroll_only(setup_ca) + + setup_kra = knob( + None, + description="configure a dogtag KRA", + ) + setup_kra = enroll_only(setup_kra) + + setup_dns = knob( + None, + description="configure bind with our zone", + ) + setup_dns = enroll_only(setup_dns) + + idstart = knob( + int, random.randint(1, 10000) * 200000, + description="The starting value for the IDs range (default random)", + ) + idstart = master_install_only(idstart) + + idmax = knob( + int, + description=("The max value for the IDs range (default: " + "idstart+199999)"), + ) + idmax = master_install_only(idmax) + + @idmax.default_getter + def idmax(self): + return self.idstart + 200000 - 1 + + no_hbac_allow = knob( + None, + description="Don't install allow_all HBAC rule", + cli_deprecated_names='--no_hbac_allow', + ) + no_hbac_allow = master_install_only(no_hbac_allow) + + ignore_topology_disconnect = knob( + None, + description="do not check whether server uninstall disconnects the " + "topology (domain level 1+)", + ) + ignore_topology_disconnect = master_install_only(ignore_topology_disconnect) + + ignore_last_of_role = knob( + None, + description="do not check whether server uninstall removes last " + "CA/DNS server or DNSSec master (domain level 1+)", + ) + ignore_last_of_role = master_install_only(ignore_last_of_role) + + no_pkinit = knob( + None, + description="disables pkinit setup steps", + ) + no_pkinit = prepare_only(no_pkinit) + + no_ui_redirect = knob( + None, + description="Do not automatically redirect to the Web UI", + ) + no_ui_redirect = enroll_only(no_ui_redirect) + + ssh_trust_dns = knob( + None, + description="configure OpenSSH client to trust DNS SSHFP records", + ) + ssh_trust_dns = enroll_only(ssh_trust_dns) + + no_ssh = knob( + None, + description="do not configure OpenSSH client", + ) + no_ssh = enroll_only(no_ssh) + + no_sshd = knob( + None, + description="do not configure OpenSSH server", + ) + no_sshd = enroll_only(no_sshd) + + no_dns_sshfp = knob( + None, + description="Do not automatically create DNS SSHFP records", + ) + no_dns_sshfp = enroll_only(no_dns_sshfp) + + dirsrv_config_file = knob( + str, None, + description="The path to LDIF file that will be used to modify " + "configuration of dse.ldif during installation of the " + "directory server instance", + cli_metavar='FILE', + ) + dirsrv_config_file = enroll_only(dirsrv_config_file) + + @dirsrv_config_file.validator + def dirsrv_config_file(self, value): + if not os.path.exists(value): + raise ValueError("File %s does not exist." % value) + + dirsrv_cert_files = knob( + # pylint: disable=invalid-sequence-index + typing.List[str], None, + description=("File containing the Directory Server SSL certificate " + "and private key"), + cli_names='--dirsrv-cert-file', + cli_deprecated_names='--dirsrv_pkcs12', + cli_metavar='FILE', + ) + 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"), + cli_names='--http-cert-file', + cli_deprecated_names='--http_pkcs12', + cli_metavar='FILE', + ) + 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"), + cli_names='--pkinit-cert-file', + cli_deprecated_names='--pkinit_pkcs12', + cli_metavar='FILE', + ) + pkinit_cert_files = prepare_only(pkinit_cert_files) + + dirsrv_pin = knob( + str, None, + sensitive=True, + description="The password to unlock the Directory Server private key", + cli_deprecated_names='--dirsrv_pin', + cli_metavar='PIN', + ) + dirsrv_pin = prepare_only(dirsrv_pin) + + http_pin = knob( + str, None, + sensitive=True, + description="The password to unlock the Apache Server private key", + cli_deprecated_names='--http_pin', + cli_metavar='PIN', + ) + http_pin = prepare_only(http_pin) + + pkinit_pin = knob( + str, None, + sensitive=True, + description="The password to unlock the Kerberos KDC private key", + cli_deprecated_names='--pkinit_pin', + cli_metavar='PIN', + ) + pkinit_pin = prepare_only(pkinit_pin) + + dirsrv_cert_name = knob( + str, None, + description="Name of the Directory Server SSL certificate to install", + cli_metavar='NAME', + ) + dirsrv_cert_name = prepare_only(dirsrv_cert_name) + + http_cert_name = knob( + str, None, + description="Name of the Apache Server SSL certificate to install", + cli_metavar='NAME', + ) + http_cert_name = prepare_only(http_cert_name) + + pkinit_cert_name = knob( + str, None, + description="Name of the Kerberos KDC SSL certificate to install", + cli_metavar='NAME', + ) + pkinit_cert_name = prepare_only(pkinit_cert_name) + + def __init__(self, **kwargs): + super(ServerInstallInterface, self).__init__(**kwargs) + + # If any of the key file options are selected, all are required. + cert_file_req = (self.dirsrv_cert_files, self.http_cert_files) + cert_file_opt = (self.pkinit_cert_files,) + if any(cert_file_req + cert_file_opt) and not all(cert_file_req): + raise RuntimeError( + "--dirsrv-cert-file and --http-cert-file are required if any " + "key file options are used.") + + if not self.interactive: + if self.dirsrv_cert_files and self.dirsrv_pin is None: + raise RuntimeError( + "You must specify --dirsrv-pin with --dirsrv-cert-file") + if self.http_cert_files and self.http_pin is None: + raise RuntimeError( + "You must specify --http-pin with --http-cert-file") + if self.pkinit_cert_files and self.pkinit_pin is None: + raise RuntimeError( + "You must specify --pkinit-pin with --pkinit-cert-file") + + if not self.setup_dns: + if self.forwarders: + raise RuntimeError( + "You cannot specify a --forwarder option without the " + "--setup-dns option") + if self.auto_forwarders: + raise RuntimeError( + "You cannot specify a --auto-forwarders option without " + "the --setup-dns option") + if self.no_forwarders: + raise RuntimeError( + "You cannot specify a --no-forwarders option without the " + "--setup-dns option") + if self.forward_policy: + raise RuntimeError( + "You cannot specify a --forward-policy option without the " + "--setup-dns option") + if self.reverse_zones: + raise RuntimeError( + "You cannot specify a --reverse-zone option without the " + "--setup-dns option") + if self.auto_reverse: + raise RuntimeError( + "You cannot specify a --auto-reverse option without the " + "--setup-dns option") + if self.no_reverse: + raise RuntimeError( + "You cannot specify a --no-reverse option without the " + "--setup-dns option") + if self.no_dnssec_validation: + raise RuntimeError( + "You cannot specify a --no-dnssec-validation option " + "without the --setup-dns option") + elif self.forwarders and self.no_forwarders: + raise RuntimeError( + "You cannot specify a --forwarder option together with " + "--no-forwarders") + elif self.auto_forwarders and self.no_forwarders: + raise RuntimeError( + "You cannot specify a --auto-forwarders option together with " + "--no-forwarders") + elif self.reverse_zones and self.no_reverse: + raise RuntimeError( + "You cannot specify a --reverse-zone option together with " + "--no-reverse") + elif self.auto_reverse and self.no_reverse: + raise RuntimeError( + "You cannot specify a --auto-reverse option together with " + "--no-reverse") + + if not hasattr(self, 'replica_file'): + if self.external_cert_files and self.dirsrv_cert_files: + raise RuntimeError( + "Service certificate file options cannot be used with the " + "external CA options.") + + if self.external_ca_type and not self.external_ca: + raise RuntimeError( + "You cannot specify --external-ca-type without " + "--external-ca") + + if self.uninstalling: + if (self.realm_name or self.new_admin_password or + self.master_password): + raise RuntimeError( + "In uninstall mode, -a, -r and -P options are not " + "allowed") + elif not self.interactive: + if (not self.realm_name or not self.new_dm_password or + not self.new_admin_password): + raise RuntimeError( + "In unattended mode you need to provide at least -r, " + "-p and -a options") + if self.setup_dns: + if (not self.forwarders and + not self.no_forwarders and + not self.auto_forwarders): + raise RuntimeError( + "You must specify at least one of --forwarder, " + "--auto-forwarders, or --no-forwarders options") + + any_ignore_option_true = any( + [self.ignore_topology_disconnect, self.ignore_last_of_role]) + if any_ignore_option_true and not self.uninstalling: + raise RuntimeError( + "'--ignore-topology-disconnect/--ignore-last-of-role' " + "options can be used only during uninstallation") + + if self.idmax < self.idstart: + raise RuntimeError( + "idmax (%s) cannot be smaller than idstart (%s)" % + (self.idmax, self.idstart)) + else: + cert_file_req = (self.dirsrv_cert_files, self.http_cert_files) + cert_file_opt = (self.pkinit_cert_files,) + + if self.replica_file is None: + # If any of the PKCS#12 options are selected, all are required. + if any(cert_file_req + cert_file_opt) and not all(cert_file_req): + raise RuntimeError( + "--dirsrv-cert-file and --http-cert-file are required " + "if any PKCS#12 options are used") + + if self.servers and not self.domain_name: + raise RuntimeError( + "The --server option cannot be used without providing " + "domain via the --domain option") + + else: + if not ipautil.file_exists(self.replica_file): + raise RuntimeError( + "Replica file %s does not exist" % self.replica_file) + + if any(cert_file_req + cert_file_opt): + raise RuntimeError( + "You cannot specify any of --dirsrv-cert-file, " + "--http-cert-file, or --pkinit-cert-file together " + "with replica file") + + CLIKnob = collections.namedtuple('CLIKnob', ('value', 'name')) + + conflicting_knobs = ( + CLIKnob(self.realm_name, '--realm'), + CLIKnob(self.domain_name, '--domain'), + CLIKnob(self.host_name, '--hostname'), + CLIKnob(self.servers, '--server'), + CLIKnob(self.principal, '--principal'), + ) + + if any([k.value is not None for k in conflicting_knobs]): + conflicting_knob_names = [ + knob.name for knob in conflicting_knobs + if knob.value is not None + ] + + raise RuntimeError( + "You cannot specify '{0}' option(s) with replica file." + .format(", ".join(conflicting_knob_names)) + ) + + if self.setup_dns: + if (not self.forwarders and + not self.no_forwarders and + not self.auto_forwarders): + raise RuntimeError( + "You must specify at least one of --forwarder, " + "--auto-forwarders, or --no-forwarders options") + + # Automatically disable pkinit w/ dogtag until that is supported + self.no_pkinit = True |