install: Migrate ipa-server-install to the install framework

https://fedorahosted.org/freeipa/ticket/4468

Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
Jan Cholasta 2015-06-02 12:36:14 +00:00
parent 9e9c01fba2
commit eb959221e1
4 changed files with 660 additions and 499 deletions

View File

@ -20,369 +20,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# requires the following packages:
# fedora-ds-base
# openldap-clients
# nss-tools
import sys
import os
import signal
import random
from optparse import OptionGroup, OptionValueError, SUPPRESS_HELP
from ipaserver.install import bindinstance
from ipaserver.install import installutils
from ipaserver.install import server
from ipaserver.install.server import (
validate_dm_password, validate_admin_password)
from ipapython import version
from ipalib import constants
from ipapython.config import IPAOptionParser
from ipalib.util import validate_domain_name
from ipapython.ipa_log_manager import root_logger, standard_logging_setup
from ipapython.dn import DN
from ipapython.install import cli
from ipaplatform.paths import paths
VALID_SUBJECT_ATTRS = ['st', 'o', 'ou', 'dnqualifier', 'c',
'serialnumber', 'l', 'title', 'sn', 'givenname',
'initials', 'generationqualifier', 'dc', 'mail',
'uid', 'postaladdress', 'postalcode', 'postofficebox',
'houseidentifier', 'e', 'street', 'pseudonym',
'incorporationlocality', 'incorporationstate',
'incorporationcountry', 'businesscategory']
from ipaserver.install.server import Server
def subject_callback(option, opt_str, value, parser):
"""
Make sure the certificate subject base is a valid DN
"""
v = unicode(value, 'utf-8')
if any(ord(c) < 0x20 for c in v):
raise OptionValueError("Subject base must not contain control characters")
if '&' in v:
raise OptionValueError("Subject base must not contain an ampersand (\"&\")")
try:
dn = DN(v)
for rdn in dn:
if rdn.attr.lower() not in VALID_SUBJECT_ATTRS:
raise OptionValueError('%s=%s has invalid attribute: "%s"' % (opt_str, value, rdn.attr))
except ValueError, e:
raise OptionValueError('%s=%s has invalid subject base format: %s' % (opt_str, value, e))
parser.values.subject = dn
ServerInstall = cli.install_tool(
Server,
command_name='ipa-server-install',
log_file_name=paths.IPASERVER_INSTALL_LOG,
debug_option=True,
uninstall_log_file_name=paths.IPASERVER_UNINSTALL_LOG,
)
def parse_options():
# Guaranteed to give a random 200k range below the 2G mark (uint32_t limit)
namespace = random.randint(1, 10000) * 200000
parser = IPAOptionParser(version=version.VERSION)
basic_group = OptionGroup(parser, "basic options")
basic_group.add_option("-r", "--realm", dest="realm_name",
help="realm name")
basic_group.add_option("-n", "--domain", dest="domain_name",
help="domain name")
basic_group.add_option("-p", "--ds-password", dest="dm_password",
sensitive=True, help="Directory Manager password")
basic_group.add_option("-P", "--master-password",
dest="master_password", sensitive=True,
help=SUPPRESS_HELP)
basic_group.add_option("-a", "--admin-password",
sensitive=True, dest="admin_password",
help="admin user kerberos password")
basic_group.add_option("--mkhomedir",
dest="mkhomedir",
action="store_true",
default=False,
help="create home directories for users "
"on their first login")
basic_group.add_option("--hostname", dest="host_name", help="fully qualified name of server")
basic_group.add_option("--domain-level", dest="domainlevel", help="IPA domain level",
default=constants.MAX_DOMAIN_LEVEL, type=int)
basic_group.add_option("--ip-address", dest="ip_addresses",
type="ip", ip_local=True, action="append", default=[],
help="Master Server IP Address. This option can be used multiple times",
metavar="IP_ADDRESS")
basic_group.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false",
help="do not configure ntp", default=True)
basic_group.add_option("--idstart", dest="idstart", default=namespace, type=int,
help="The starting value for the IDs range (default random)")
basic_group.add_option("--idmax", dest="idmax", default=0, type=int,
help="The max value value for the IDs range (default: idstart+199999)")
basic_group.add_option("--no_hbac_allow", dest="hbac_allow", default=False,
action="store_true",
help="Don't install allow_all HBAC rule")
basic_group.add_option("--no-ui-redirect", dest="ui_redirect", action="store_false",
default=True, help="Do not automatically redirect to the Web UI")
basic_group.add_option("--ssh-trust-dns", dest="trust_sshfp", default=False, action="store_true",
help="configure OpenSSH client to trust DNS SSHFP records")
basic_group.add_option("--no-ssh", dest="conf_ssh", default=True, action="store_false",
help="do not configure OpenSSH client")
basic_group.add_option("--no-sshd", dest="conf_sshd", default=True, action="store_false",
help="do not configure OpenSSH server")
basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="print debugging information")
basic_group.add_option("-U", "--unattended", dest="unattended", action="store_true",
default=False, help="unattended (un)installation never prompts the user")
parser.add_option_group(basic_group)
cert_group = OptionGroup(parser, "certificate system options")
cert_group.add_option("", "--external-ca", dest="external_ca", action="store_true",
default=False, help="Generate a CSR for the IPA CA certificate to be signed by an external CA")
cert_group.add_option("--external-ca-type", dest="external_ca_type",
type="choice", choices=("generic", "ms-cs"),
help="Type of the external CA")
cert_group.add_option("--external-cert-file", dest="external_cert_files",
action="append", metavar="FILE",
help="File containing the IPA CA certificate and the external CA certificate chain")
cert_group.add_option("--external_cert_file", dest="external_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--external_ca_file", dest="external_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
default=True, help="disables pkinit setup steps")
cert_group.add_option("--dirsrv-cert-file", dest="dirsrv_cert_files",
action="append", metavar="FILE",
help="File containing the Directory Server SSL certificate and private key")
cert_group.add_option("--dirsrv_pkcs12", dest="dirsrv_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--http-cert-file", dest="http_cert_files",
action="append", metavar="FILE",
help="File containing the Apache Server SSL certificate and private key")
cert_group.add_option("--http_pkcs12", dest="http_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--pkinit-cert-file", dest="pkinit_cert_files",
action="append", metavar="FILE",
help="File containing the Kerberos KDC SSL certificate and private key")
cert_group.add_option("--pkinit_pkcs12", dest="pkinit_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--dirsrv-pin", dest="dirsrv_pin", sensitive=True,
metavar="PIN",
help="The password to unlock the Directory Server private key")
cert_group.add_option("--dirsrv_pin", dest="dirsrv_pin", sensitive=True,
help=SUPPRESS_HELP)
cert_group.add_option("--http-pin", dest="http_pin", sensitive=True,
metavar="PIN",
help="The password to unlock the Apache Server private key")
cert_group.add_option("--http_pin", dest="http_pin", sensitive=True,
help=SUPPRESS_HELP)
cert_group.add_option("--pkinit-pin", dest="pkinit_pin", sensitive=True,
metavar="PIN",
help="The password to unlock the Kerberos KDC private key")
cert_group.add_option("--pkinit_pin", dest="pkinit_pin", sensitive=True,
help=SUPPRESS_HELP)
cert_group.add_option("--dirsrv-cert-name", dest="dirsrv_cert_name",
metavar="NAME",
help="Name of the Directory Server SSL certificate to install")
cert_group.add_option("--http-cert-name", dest="http_cert_name",
metavar="NAME",
help="Name of the Apache Server SSL certificate to install")
cert_group.add_option("--pkinit-cert-name", dest="pkinit_cert_name",
metavar="NAME",
help="Name of the Kerberos KDC SSL certificate to install")
cert_group.add_option("--ca-cert-file", dest="ca_cert_files",
action="append", metavar="FILE",
help="File containing CA certificates for the service certificate files")
cert_group.add_option("--root-ca-file", dest="ca_cert_files",
action="append",
help=SUPPRESS_HELP)
cert_group.add_option("--subject", action="callback", callback=subject_callback,
type="string",
help="The certificate subject base (default O=<realm-name>)")
cert_group.add_option("--ca-signing-algorithm", dest="ca_signing_algorithm",
type="choice",
choices=('SHA1withRSA', 'SHA256withRSA', 'SHA512withRSA'),
help="Signing algorithm of the IPA CA certificate")
parser.add_option_group(cert_group)
dns_group = OptionGroup(parser, "DNS options")
dns_group.add_option("--setup-dns", dest="setup_dns", action="store_true",
default=False, help="configure bind with our zone")
dns_group.add_option("--forwarder", dest="forwarders", action="append",
type="ip", help="Add a DNS forwarder. This option can be used multiple times")
dns_group.add_option("--no-forwarders", dest="no_forwarders", action="store_true",
default=False, help="Do not add any DNS forwarders, use root servers instead")
dns_group.add_option("--reverse-zone", dest="reverse_zones",
help="The reverse DNS zone to use. This option can be used multiple times",
action="append", default=[], metavar="REVERSE_ZONE")
dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true",
default=False, help="Do not create reverse DNS zone")
dns_group.add_option("--no-dnssec-validation", dest="no_dnssec_validation", action="store_true",
default=False, help="Disable DNSSEC validation")
dns_group.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback,
type="string",
help="DNS zone manager e-mail address. Defaults to hostmaster@DOMAIN")
dns_group.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
default=False,
help="Do not use DNS for hostname lookup during installation")
dns_group.add_option("--no-dns-sshfp", dest="create_sshfp", default=True, action="store_false",
help="Do not automatically create DNS SSHFP records")
parser.add_option_group(dns_group)
uninstall_group = OptionGroup(parser, "uninstall options")
uninstall_group.add_option("", "--uninstall", dest="uninstall", action="store_true",
default=False, help="uninstall an existing installation. The uninstall can " \
"be run with --unattended option")
parser.add_option_group(uninstall_group)
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
if options.dm_password is not None:
try:
validate_dm_password(options.dm_password)
except ValueError, e:
parser.error("DS admin password: " + str(e))
if options.admin_password is not None:
try:
validate_admin_password(options.admin_password)
except ValueError, e:
parser.error("Admin user password: " + str(e))
if options.domain_name is not None:
try:
validate_domain_name(options.domain_name)
except ValueError, e:
parser.error("invalid domain: " + unicode(e))
# Check that Domain Level is within the allowed range
if not options.uninstall:
if options.domainlevel < constants.MIN_DOMAIN_LEVEL:
parser.error("Domain Level cannot be lower than {0}"
.format(constants.MIN_DOMAIN_LEVEL))
elif options.domainlevel > constants.MAX_DOMAIN_LEVEL:
parser.error("Domain Level cannot be higher than {0}"
.format(constants.MAX_DOMAIN_LEVEL))
if not options.setup_dns:
if options.forwarders:
parser.error("You cannot specify a --forwarder option without the --setup-dns option")
if options.no_forwarders:
parser.error("You cannot specify a --no-forwarders option without the --setup-dns option")
if options.reverse_zones:
parser.error("You cannot specify a --reverse-zone option without the --setup-dns option")
if options.no_reverse:
parser.error("You cannot specify a --no-reverse option without the --setup-dns option")
if options.no_dnssec_validation:
parser.error("You cannot specify a --no-dnssec-validation option without the --setup-dns option")
elif options.forwarders and options.no_forwarders:
parser.error("You cannot specify a --forwarder option together with --no-forwarders")
elif options.reverse_zones and options.no_reverse:
parser.error("You cannot specify a --reverse-zone option together with --no-reverse")
if options.uninstall:
if (options.realm_name or
options.admin_password or options.master_password):
parser.error("In uninstall mode, -a, -r and -P options are not allowed")
elif options.unattended:
if (not options.realm_name or
not options.dm_password or not options.admin_password):
parser.error("In unattended mode you need to provide at least -r, -p and -a options")
if options.setup_dns:
if not options.forwarders and not options.no_forwarders:
parser.error("You must specify at least one --forwarder option or --no-forwarders option")
# If any of the key file options are selected, all are required.
cert_file_req = (options.dirsrv_cert_files, options.http_cert_files)
cert_file_opt = (options.pkinit_cert_files,)
if any(cert_file_req + cert_file_opt) and not all(cert_file_req):
parser.error("--dirsrv-cert-file and --http-cert-file are required if "
"any key file options are used.")
if options.unattended:
if options.dirsrv_cert_files and options.dirsrv_pin is None:
parser.error(
"You must specify --dirsrv-pin with --dirsrv-cert-file")
if options.http_cert_files and options.http_pin is None:
parser.error(
"You must specify --http-pin with --http-cert-file")
if options.pkinit_cert_files and options.pkinit_pin is None:
parser.error(
"You must specify --pkinit-pin with --pkinit-cert-file")
if options.external_cert_files and options.dirsrv_cert_files:
parser.error("Service certificate file options cannot be used with "
"the external CA options.")
if options.external_ca:
if options.external_cert_files:
parser.error("You cannot specify --external-cert-file "
"together with --external-ca")
if options.dirsrv_cert_files:
parser.error("You cannot specify service certificate file options "
"together with --external-ca")
if options.external_ca_type and not options.external_ca:
parser.error(
"You cannot specify --external-ca-type without --external-ca")
if (options.external_cert_files and
any(not os.path.isabs(path) for path in options.external_cert_files)):
parser.error("--external-cert-file must use an absolute path")
if options.idmax == 0:
options.idmax = int(options.idstart) + 200000 - 1
if options.idmax < options.idstart:
parser.error("idmax (%u) cannot be smaller than idstart (%u)" %
(options.idmax, options.idstart))
#Automatically disable pkinit w/ dogtag until that is supported
options.setup_pkinit = False
options.dnssec_master = False
return safe_options, options
def signal_handler(signum, frame):
raise KeyboardInterrupt
def main():
safe_options, options = parse_options()
if os.getegid() != 0:
sys.exit("Must be root to set up server")
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
if not options.uninstall:
standard_logging_setup(paths.IPASERVER_INSTALL_LOG,
debug=options.debug)
else:
standard_logging_setup(paths.IPASERVER_UNINSTALL_LOG,
debug=options.debug)
root_logger.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
root_logger.debug("missing options might be asked for interactively later\n")
root_logger.debug('IPA version %s' % version.VENDOR_VERSION)
if not options.uninstall:
server.install_check(options)
server.install(options)
else:
server.uninstall_check(options)
server.uninstall(options)
if __name__ == '__main__':
# FIXME: Common option parsing, logging setup, etc should be factored
# out from all install scripts
safe_options, options = parse_options()
if options.uninstall:
log_file_name = paths.IPASERVER_UNINSTALL_LOG
else:
log_file_name = paths.IPASERVER_INSTALL_LOG
installutils.run_script(main, log_file_name=log_file_name,
operation_name='ipa-server-install')
ServerInstall.run_cli()

View File

@ -2,9 +2,8 @@
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
from .install import install_check, install, uninstall_check, uninstall
from .install import Server
from .replicainstall import install_check as replica_install_check
from .replicainstall import install as replica_install
from .upgrade import upgrade_check, upgrade
from .install import validate_dm_password, validate_admin_password

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ import os
import shutil
import socket
import sys
import tempfile
from ipapython import dogtag, ipautil, sysrestore
from ipapython.dn import DN
@ -25,8 +26,6 @@ from ipaserver.install import (
from ipaserver.install.installutils import create_replica_config
from ipaserver.install.replication import (
ReplicationManager, replica_conn_check)
from ipaserver.install.server.install import (
init_private_ccache, destroy_private_ccache)
DIRMAN_DN = DN(('cn', 'directory manager'))
REPLICA_INFO_TOP_DIR = None
@ -286,6 +285,31 @@ def remove_replica_info_dir():
pass
def init_private_ccache():
global original_ccache
global temp_ccache
(desc, temp_ccache) = tempfile.mkstemp(prefix='krbcc')
os.close(desc)
original_ccache = os.environ.get('KRB5CCNAME')
os.environ['KRB5CCNAME'] = temp_ccache
def destroy_private_ccache():
global original_ccache
global temp_ccache
if original_ccache is not None:
os.environ['KRB5CCNAME'] = original_ccache
else:
os.environ.pop('KRB5CCNAME', None)
if os.path.exists(temp_ccache):
os.remove(temp_ccache)
def common_cleanup(func):
def decorated(*args, **kwargs):
try: