mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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>
This commit is contained in:
@@ -1311,7 +1311,9 @@ fi
|
|||||||
%dir %{python_sitelib}/ipapython/secrets
|
%dir %{python_sitelib}/ipapython/secrets
|
||||||
%{python_sitelib}/ipapython/secrets/*.py*
|
%{python_sitelib}/ipapython/secrets/*.py*
|
||||||
%dir %{python_sitelib}/ipalib
|
%dir %{python_sitelib}/ipalib
|
||||||
%{python_sitelib}/ipalib/*
|
%{python_sitelib}/ipalib/*.py*
|
||||||
|
%dir %{python_sitelib}/ipalib/install
|
||||||
|
%{python_sitelib}/ipalib/install/*.py*
|
||||||
%dir %{python_sitelib}/ipaplatform
|
%dir %{python_sitelib}/ipaplatform
|
||||||
%{python_sitelib}/ipaplatform/*
|
%{python_sitelib}/ipaplatform/*
|
||||||
%{python_sitelib}/ipapython-*.egg-info
|
%{python_sitelib}/ipapython-*.egg-info
|
||||||
|
|||||||
27
ipaclient/install/automount.py
Normal file
27
ipaclient/install/automount.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Automount installer module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipalib.install import service
|
||||||
|
from ipalib.install.service import enroll_only
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
|
||||||
|
|
||||||
|
class AutomountInstallInterface(service.ServiceInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface of the automount installer
|
||||||
|
|
||||||
|
Knobs defined here will be available in:
|
||||||
|
* ipa-client-install
|
||||||
|
* ipa-client-automount
|
||||||
|
"""
|
||||||
|
|
||||||
|
automount_location = knob(
|
||||||
|
str, 'default',
|
||||||
|
description="Automount location",
|
||||||
|
)
|
||||||
|
automount_location = enroll_only(automount_location)
|
||||||
@@ -43,6 +43,9 @@ from ipalib import (
|
|||||||
x509,
|
x509,
|
||||||
)
|
)
|
||||||
from ipalib.constants import CACERT
|
from ipalib.constants import CACERT
|
||||||
|
from ipalib.install import hostname as hostname_
|
||||||
|
from ipalib.install import service
|
||||||
|
from ipalib.install.service import enroll_only, prepare_only
|
||||||
from ipalib.rpc import delete_persistent_client_session_data
|
from ipalib.rpc import delete_persistent_client_session_data
|
||||||
from ipalib.util import (
|
from ipalib.util import (
|
||||||
broadcast_ip_address_warning,
|
broadcast_ip_address_warning,
|
||||||
@@ -62,6 +65,8 @@ from ipapython import (
|
|||||||
)
|
)
|
||||||
from ipapython.admintool import ScriptError
|
from ipapython.admintool import ScriptError
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
|
from ipapython.install import typing
|
||||||
|
from ipapython.install.core import knob
|
||||||
from ipapython.ipa_log_manager import root_logger
|
from ipapython.ipa_log_manager import root_logger
|
||||||
from ipapython.ipautil import (
|
from ipapython.ipautil import (
|
||||||
CalledProcessError,
|
CalledProcessError,
|
||||||
@@ -74,6 +79,10 @@ from ipapython.ipautil import (
|
|||||||
)
|
)
|
||||||
from ipapython.ssh import SSHPublicKey
|
from ipapython.ssh import SSHPublicKey
|
||||||
|
|
||||||
|
from . import automount
|
||||||
|
|
||||||
|
NoneType = type(None)
|
||||||
|
|
||||||
SUCCESS = 0
|
SUCCESS = 0
|
||||||
CLIENT_INSTALL_ERROR = 1
|
CLIENT_INSTALL_ERROR = 1
|
||||||
CLIENT_NOT_CONFIGURED = 2
|
CLIENT_NOT_CONFIGURED = 2
|
||||||
@@ -3298,3 +3307,188 @@ def uninstall(options):
|
|||||||
|
|
||||||
if rv:
|
if rv:
|
||||||
raise ScriptError(rval=rv)
|
raise ScriptError(rval=rv)
|
||||||
|
|
||||||
|
|
||||||
|
class ClientInstallInterface(hostname_.HostNameInstallInterface,
|
||||||
|
service.ServiceAdminInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface of the client installer
|
||||||
|
|
||||||
|
Knobs defined here will be available in:
|
||||||
|
* ipa-client-install
|
||||||
|
* ipa-server-install
|
||||||
|
* ipa-replica-prepare
|
||||||
|
* ipa-replica-install
|
||||||
|
"""
|
||||||
|
|
||||||
|
fixed_primary = knob(
|
||||||
|
None,
|
||||||
|
description="Configure sssd to use fixed server as primary IPA server",
|
||||||
|
)
|
||||||
|
fixed_primary = enroll_only(fixed_primary)
|
||||||
|
|
||||||
|
principal = knob(
|
||||||
|
bases=service.ServiceAdminInstallInterface.principal,
|
||||||
|
description="principal to use to join the IPA realm",
|
||||||
|
)
|
||||||
|
principal = enroll_only(principal)
|
||||||
|
|
||||||
|
host_password = knob(
|
||||||
|
str, None,
|
||||||
|
sensitive=True,
|
||||||
|
)
|
||||||
|
host_password = enroll_only(host_password)
|
||||||
|
|
||||||
|
keytab = knob(
|
||||||
|
str, None,
|
||||||
|
description="path to backed up keytab from previous enrollment",
|
||||||
|
cli_names=[None, '-k'],
|
||||||
|
)
|
||||||
|
keytab = enroll_only(keytab)
|
||||||
|
|
||||||
|
mkhomedir = knob(
|
||||||
|
None,
|
||||||
|
description="create home directories for users on their first login",
|
||||||
|
)
|
||||||
|
mkhomedir = enroll_only(mkhomedir)
|
||||||
|
|
||||||
|
force_join = knob(
|
||||||
|
None,
|
||||||
|
description="Force client enrollment even if already enrolled",
|
||||||
|
)
|
||||||
|
force_join = enroll_only(force_join)
|
||||||
|
|
||||||
|
ntp_servers = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[str], None,
|
||||||
|
description="ntp server to use. This option can be used multiple "
|
||||||
|
"times",
|
||||||
|
cli_names='--ntp-server',
|
||||||
|
cli_metavar='NTP_SERVER',
|
||||||
|
)
|
||||||
|
ntp_servers = enroll_only(ntp_servers)
|
||||||
|
|
||||||
|
no_ntp = knob(
|
||||||
|
None,
|
||||||
|
description="do not configure ntp",
|
||||||
|
cli_names=[None, '-N'],
|
||||||
|
)
|
||||||
|
no_ntp = enroll_only(no_ntp)
|
||||||
|
|
||||||
|
force_ntpd = knob(
|
||||||
|
None,
|
||||||
|
description="Stop and disable any time&date synchronization services "
|
||||||
|
"besides ntpd",
|
||||||
|
)
|
||||||
|
force_ntpd = enroll_only(force_ntpd)
|
||||||
|
|
||||||
|
nisdomain = knob(
|
||||||
|
str, None,
|
||||||
|
description="NIS domain name",
|
||||||
|
)
|
||||||
|
nisdomain = enroll_only(nisdomain)
|
||||||
|
|
||||||
|
no_nisdomain = knob(
|
||||||
|
None,
|
||||||
|
description="do not configure NIS domain name",
|
||||||
|
)
|
||||||
|
no_nisdomain = enroll_only(no_nisdomain)
|
||||||
|
|
||||||
|
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_sudo = knob(
|
||||||
|
None,
|
||||||
|
description="do not configure SSSD as data source for sudo",
|
||||||
|
)
|
||||||
|
no_sudo = enroll_only(no_sudo)
|
||||||
|
|
||||||
|
no_dns_sshfp = knob(
|
||||||
|
None,
|
||||||
|
description="do not automatically create DNS SSHFP records",
|
||||||
|
)
|
||||||
|
no_dns_sshfp = enroll_only(no_dns_sshfp)
|
||||||
|
|
||||||
|
kinit_attempts = knob(
|
||||||
|
int, 5,
|
||||||
|
description="number of attempts to obtain host TGT (defaults to 5).",
|
||||||
|
)
|
||||||
|
kinit_attempts = enroll_only(kinit_attempts)
|
||||||
|
|
||||||
|
@kinit_attempts.validator
|
||||||
|
def kinit_attempts(self, value):
|
||||||
|
if value < 1:
|
||||||
|
raise ValueError("expects an integer greater than 0.")
|
||||||
|
|
||||||
|
request_cert = knob(
|
||||||
|
None,
|
||||||
|
description="request certificate for the machine",
|
||||||
|
)
|
||||||
|
request_cert = prepare_only(request_cert)
|
||||||
|
|
||||||
|
permit = knob(
|
||||||
|
None,
|
||||||
|
description="disable access rules by default, permit all access.",
|
||||||
|
)
|
||||||
|
permit = enroll_only(permit)
|
||||||
|
|
||||||
|
enable_dns_updates = knob(
|
||||||
|
None,
|
||||||
|
description="Configures the machine to attempt dns updates when the "
|
||||||
|
"ip address changes.",
|
||||||
|
)
|
||||||
|
enable_dns_updates = enroll_only(enable_dns_updates)
|
||||||
|
|
||||||
|
no_krb5_offline_passwords = knob(
|
||||||
|
None,
|
||||||
|
description="Configure SSSD not to store user password when the "
|
||||||
|
"server is offline",
|
||||||
|
)
|
||||||
|
no_krb5_offline_passwords = enroll_only(no_krb5_offline_passwords)
|
||||||
|
|
||||||
|
preserve_sssd = knob(
|
||||||
|
None,
|
||||||
|
description="Preserve old SSSD configuration if possible",
|
||||||
|
)
|
||||||
|
preserve_sssd = enroll_only(preserve_sssd)
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(ClientInstallInterface, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
if self.servers and not self.domain_name:
|
||||||
|
raise RuntimeError(
|
||||||
|
"--server cannot be used without providing --domain")
|
||||||
|
|
||||||
|
if self.force_ntpd and self.no_ntp:
|
||||||
|
raise RuntimeError(
|
||||||
|
"--force-ntpd cannot be used together with --no-ntp")
|
||||||
|
|
||||||
|
if self.no_nisdomain and self.nisdomain:
|
||||||
|
raise RuntimeError(
|
||||||
|
"--no-nisdomain cannot be used together with --nisdomain")
|
||||||
|
|
||||||
|
if self.ip_addresses:
|
||||||
|
if self.enable_dns_updates:
|
||||||
|
raise RuntimeError(
|
||||||
|
"--ip-address cannot be used together with"
|
||||||
|
" --enable-dns-updates")
|
||||||
|
|
||||||
|
if self.all_ip_addresses:
|
||||||
|
raise RuntimeError(
|
||||||
|
"--ip-address cannot be used together with"
|
||||||
|
"--all-ip-addresses")
|
||||||
|
|||||||
3
ipalib/install/__init__.py
Normal file
3
ipalib/install/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
59
ipalib/install/hostname.py
Normal file
59
ipalib/install/hostname.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Host name installer module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipapython.install import typing
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
from ipapython.ipautil import CheckedIPAddress
|
||||||
|
|
||||||
|
from . import service
|
||||||
|
from .service import prepare_only
|
||||||
|
|
||||||
|
|
||||||
|
class HostNameInstallInterface(service.ServiceInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface common to all service installers which create DNS address
|
||||||
|
records for `host_name`
|
||||||
|
"""
|
||||||
|
|
||||||
|
ip_addresses = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[CheckedIPAddress], None,
|
||||||
|
description="Specify IP address that should be added to DNS. This "
|
||||||
|
"option can be used multiple times",
|
||||||
|
cli_names='--ip-address',
|
||||||
|
cli_metavar='IP_ADDRESS',
|
||||||
|
)
|
||||||
|
ip_addresses = prepare_only(ip_addresses)
|
||||||
|
|
||||||
|
@ip_addresses.validator
|
||||||
|
def ip_addresses(self, values):
|
||||||
|
for value in values:
|
||||||
|
try:
|
||||||
|
CheckedIPAddress(value, match_local=True)
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError("invalid IP address {0}: {1}".format(
|
||||||
|
value, e))
|
||||||
|
|
||||||
|
all_ip_addresses = knob(
|
||||||
|
None,
|
||||||
|
description="All routable IP addresses configured on any inteface "
|
||||||
|
"will be added to DNS",
|
||||||
|
)
|
||||||
|
all_ip_addresses = prepare_only(all_ip_addresses)
|
||||||
|
|
||||||
|
no_host_dns = knob(
|
||||||
|
None,
|
||||||
|
description="Do not use DNS for hostname lookup during installation",
|
||||||
|
)
|
||||||
|
no_host_dns = prepare_only(no_host_dns)
|
||||||
|
|
||||||
|
no_wait_for_dns = knob(
|
||||||
|
None,
|
||||||
|
description="do not wait until the host is resolvable in DNS",
|
||||||
|
)
|
||||||
|
no_wait_for_dns = prepare_only(no_wait_for_dns)
|
||||||
178
ipalib/install/service.py
Normal file
178
ipalib/install/service.py
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Base service installer module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipalib.util import validate_domain_name
|
||||||
|
from ipapython.install import common, core, typing
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_only(obj):
|
||||||
|
"""
|
||||||
|
Decorator which makes an installer attribute appear only in the prepare
|
||||||
|
phase of the install
|
||||||
|
"""
|
||||||
|
obj.__exclude__ = getattr(obj, '__exclude__', set()) | {'enroll'}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def enroll_only(obj):
|
||||||
|
"""
|
||||||
|
Decorator which makes an installer attribute appear only in the enroll
|
||||||
|
phase of the install
|
||||||
|
"""
|
||||||
|
obj.__exclude__ = getattr(obj, '__exclude__', set()) | {'prepare'}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def master_install_only(obj):
|
||||||
|
"""
|
||||||
|
Decorator which makes an installer attribute appear only in master install
|
||||||
|
"""
|
||||||
|
obj.__exclude__ = getattr(obj, '__exclude__', set()) | {'replica_install'}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def replica_install_only(obj):
|
||||||
|
"""
|
||||||
|
Decorator which makes an installer attribute appear only in replica install
|
||||||
|
"""
|
||||||
|
obj.__exclude__ = getattr(obj, '__exclude__', set()) | {'master_install'}
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _does(cls, arg):
|
||||||
|
def remove(name):
|
||||||
|
def removed(self):
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
return property(removed)
|
||||||
|
|
||||||
|
return type(
|
||||||
|
cls.__name__,
|
||||||
|
(cls,),
|
||||||
|
{
|
||||||
|
n: remove(n) for n in dir(cls)
|
||||||
|
if arg in getattr(getattr(cls, n), '__exclude__', set())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def prepares(cls):
|
||||||
|
"""
|
||||||
|
Returns installer class stripped of attributes not related to the prepare
|
||||||
|
phase of the install
|
||||||
|
"""
|
||||||
|
return _does(cls, 'prepare')
|
||||||
|
|
||||||
|
|
||||||
|
def enrolls(cls):
|
||||||
|
"""
|
||||||
|
Returns installer class stripped of attributes not related to the enroll
|
||||||
|
phase of the install
|
||||||
|
"""
|
||||||
|
return _does(cls, 'enroll')
|
||||||
|
|
||||||
|
|
||||||
|
def installs_master(cls):
|
||||||
|
"""
|
||||||
|
Returns installer class stripped of attributes not related to master
|
||||||
|
install
|
||||||
|
"""
|
||||||
|
return _does(cls, 'master_install')
|
||||||
|
|
||||||
|
|
||||||
|
def installs_replica(cls):
|
||||||
|
"""
|
||||||
|
Returns installer class stripped of attributes not related to replica
|
||||||
|
install
|
||||||
|
"""
|
||||||
|
return _does(cls, 'replica_install')
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceInstallInterface(common.Installable,
|
||||||
|
common.Interactive,
|
||||||
|
core.Composite):
|
||||||
|
"""
|
||||||
|
Interface common to all service installers
|
||||||
|
"""
|
||||||
|
|
||||||
|
domain_name = knob(
|
||||||
|
str, None,
|
||||||
|
description="domain name",
|
||||||
|
cli_names='--domain',
|
||||||
|
)
|
||||||
|
|
||||||
|
@domain_name.validator
|
||||||
|
def domain_name(self, value):
|
||||||
|
validate_domain_name(value)
|
||||||
|
|
||||||
|
servers = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[str], None,
|
||||||
|
description="FQDN of IPA server",
|
||||||
|
cli_names='--server',
|
||||||
|
cli_metavar='SERVER',
|
||||||
|
)
|
||||||
|
|
||||||
|
realm_name = knob(
|
||||||
|
str, None,
|
||||||
|
description="realm name",
|
||||||
|
cli_names='--realm',
|
||||||
|
)
|
||||||
|
|
||||||
|
host_name = knob(
|
||||||
|
str, None,
|
||||||
|
description="The hostname of this machine (FQDN). If specified, the "
|
||||||
|
"hostname will be set and the system configuration will "
|
||||||
|
"be updated to persist over reboot. By default the result "
|
||||||
|
"of getfqdn() call from Python's socket module is used.",
|
||||||
|
cli_names='--hostname',
|
||||||
|
)
|
||||||
|
|
||||||
|
ca_cert_files = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[str], None,
|
||||||
|
description="load the CA certificate from this file",
|
||||||
|
cli_names='--ca-cert-file',
|
||||||
|
cli_metavar='FILE',
|
||||||
|
)
|
||||||
|
|
||||||
|
replica_file = knob(
|
||||||
|
str, None,
|
||||||
|
description="a file generated by ipa-replica-prepare",
|
||||||
|
)
|
||||||
|
replica_file = enroll_only(replica_file)
|
||||||
|
replica_file = replica_install_only(replica_file)
|
||||||
|
|
||||||
|
dm_password = knob(
|
||||||
|
str, None,
|
||||||
|
sensitive=True,
|
||||||
|
description="Directory Manager password (for the existing master)",
|
||||||
|
)
|
||||||
|
dm_password = enroll_only(dm_password)
|
||||||
|
dm_password = replica_install_only(dm_password)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAdminInstallInterface(ServiceInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface common to all service installers which require admin user
|
||||||
|
authentication
|
||||||
|
"""
|
||||||
|
|
||||||
|
principal = knob(
|
||||||
|
str, None,
|
||||||
|
)
|
||||||
|
principal = enroll_only(principal)
|
||||||
|
principal = replica_install_only(principal)
|
||||||
|
|
||||||
|
admin_password = knob(
|
||||||
|
str, None,
|
||||||
|
sensitive=True,
|
||||||
|
)
|
||||||
|
admin_password = enroll_only(admin_password)
|
||||||
|
admin_password = replica_install_only(admin_password)
|
||||||
@@ -34,5 +34,6 @@ if __name__ == '__main__':
|
|||||||
package_dir={'ipalib': ''},
|
package_dir={'ipalib': ''},
|
||||||
packages=[
|
packages=[
|
||||||
"ipalib",
|
"ipalib",
|
||||||
|
"ipalib.install",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,11 +2,24 @@
|
|||||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||||
#
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
CA installer module
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import enum
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from ipaserver.install import cainstance, custodiainstance, dsinstance, bindinstance
|
import six
|
||||||
|
|
||||||
|
from ipalib.install.service import enroll_only, master_install_only, replica_install_only
|
||||||
|
from ipapython.install import typing
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
from ipaserver.install import (cainstance,
|
||||||
|
custodiainstance,
|
||||||
|
dsinstance,
|
||||||
|
bindinstance)
|
||||||
from ipapython import ipautil, certdb
|
from ipapython import ipautil, certdb
|
||||||
from ipapython.admintool import ScriptError
|
from ipapython.admintool import ScriptError
|
||||||
from ipaplatform import services
|
from ipaplatform import services
|
||||||
@@ -17,6 +30,19 @@ from ipalib import api, certstore, x509
|
|||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipapython.ipa_log_manager import root_logger
|
from ipapython.ipa_log_manager import root_logger
|
||||||
|
|
||||||
|
from . import conncheck, dogtag
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
external_cert_file = None
|
external_cert_file = None
|
||||||
external_ca_file = None
|
external_ca_file = None
|
||||||
|
|
||||||
@@ -270,3 +296,109 @@ def uninstall():
|
|||||||
ca_instance.stop_tracking_certificates()
|
ca_instance.stop_tracking_certificates()
|
||||||
if ca_instance.is_configured():
|
if ca_instance.is_configured():
|
||||||
ca_instance.uninstall()
|
ca_instance.uninstall()
|
||||||
|
|
||||||
|
|
||||||
|
class ExternalCAType(enum.Enum):
|
||||||
|
GENERIC = 'generic'
|
||||||
|
MS_CS = 'ms-cs'
|
||||||
|
|
||||||
|
|
||||||
|
class CASigningAlgorithm(enum.Enum):
|
||||||
|
SHA1_WITH_RSA = 'SHA1withRSA'
|
||||||
|
SHA_256_WITH_RSA = 'SHA256withRSA'
|
||||||
|
SHA_512_WITH_RSA = 'SHA512withRSA'
|
||||||
|
|
||||||
|
|
||||||
|
class CAInstallInterface(dogtag.DogtagInstallInterface,
|
||||||
|
conncheck.ConnCheckInterface):
|
||||||
|
"""
|
||||||
|
Interface of the CA installer
|
||||||
|
|
||||||
|
Knobs defined here will be available in:
|
||||||
|
* ipa-server-install
|
||||||
|
* ipa-replica-prepare
|
||||||
|
* ipa-replica-install
|
||||||
|
* ipa-ca-install
|
||||||
|
"""
|
||||||
|
|
||||||
|
principal = knob(
|
||||||
|
bases=conncheck.ConnCheckInterface.principal,
|
||||||
|
description="User allowed to manage replicas",
|
||||||
|
cli_names=(
|
||||||
|
list(conncheck.ConnCheckInterface.principal.cli_names) + ['-P']),
|
||||||
|
)
|
||||||
|
principal = enroll_only(principal)
|
||||||
|
principal = replica_install_only(principal)
|
||||||
|
|
||||||
|
admin_password = knob(
|
||||||
|
bases=conncheck.ConnCheckInterface.admin_password,
|
||||||
|
description="Admin user Kerberos password used for connection check",
|
||||||
|
cli_names=(
|
||||||
|
list(conncheck.ConnCheckInterface.admin_password.cli_names) +
|
||||||
|
['-w']),
|
||||||
|
)
|
||||||
|
admin_password = enroll_only(admin_password)
|
||||||
|
admin_password = replica_install_only(admin_password)
|
||||||
|
|
||||||
|
external_ca = knob(
|
||||||
|
None,
|
||||||
|
description=("Generate a CSR for the IPA CA certificate to be signed "
|
||||||
|
"by an external CA"),
|
||||||
|
)
|
||||||
|
external_ca = master_install_only(external_ca)
|
||||||
|
|
||||||
|
external_ca_type = knob(
|
||||||
|
ExternalCAType, None,
|
||||||
|
description="Type of the external CA",
|
||||||
|
)
|
||||||
|
external_ca_type = master_install_only(external_ca_type)
|
||||||
|
|
||||||
|
external_cert_files = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[str], None,
|
||||||
|
description=("File containing the IPA CA certificate and the external "
|
||||||
|
"CA certificate chain"),
|
||||||
|
cli_names='--external-cert-file',
|
||||||
|
cli_deprecated_names=['--external_cert_file', '--external_ca_file'],
|
||||||
|
cli_metavar='FILE',
|
||||||
|
)
|
||||||
|
external_cert_files = master_install_only(external_cert_files)
|
||||||
|
|
||||||
|
@external_cert_files.validator
|
||||||
|
def external_cert_files(self, value):
|
||||||
|
if any(not os.path.isabs(path) for path in value):
|
||||||
|
raise ValueError("must use an absolute path")
|
||||||
|
|
||||||
|
subject = knob(
|
||||||
|
str, None,
|
||||||
|
description="The certificate subject base (default O=<realm-name>)",
|
||||||
|
)
|
||||||
|
subject = master_install_only(subject)
|
||||||
|
|
||||||
|
@subject.validator
|
||||||
|
def subject(self, value):
|
||||||
|
v = unicode(value, 'utf-8')
|
||||||
|
if any(ord(c) < 0x20 for c in v):
|
||||||
|
raise ValueError("must not contain control characters")
|
||||||
|
if '&' in v:
|
||||||
|
raise ValueError("must not contain an ampersand (\"&\")")
|
||||||
|
try:
|
||||||
|
dn = DN(v)
|
||||||
|
for rdn in dn:
|
||||||
|
if rdn.attr.lower() not in VALID_SUBJECT_ATTRS:
|
||||||
|
raise ValueError("invalid attribute: \"%s\"" % rdn.attr)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError("invalid subject base format: %s" % e)
|
||||||
|
|
||||||
|
ca_signing_algorithm = knob(
|
||||||
|
CASigningAlgorithm, None,
|
||||||
|
description="Signing algorithm of the IPA CA certificate",
|
||||||
|
)
|
||||||
|
ca_signing_algorithm = master_install_only(ca_signing_algorithm)
|
||||||
|
|
||||||
|
skip_schema_check = knob(
|
||||||
|
None,
|
||||||
|
description="skip check for updated CA DS schema on the remote master",
|
||||||
|
)
|
||||||
|
skip_schema_check = enroll_only(skip_schema_check)
|
||||||
|
skip_schema_check = replica_install_only(skip_schema_check)
|
||||||
|
|||||||
25
ipaserver/install/conncheck.py
Normal file
25
ipaserver/install/conncheck.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Connection check module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipalib.install import service
|
||||||
|
from ipalib.install.service import enroll_only, replica_install_only
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
|
||||||
|
|
||||||
|
class ConnCheckInterface(service.ServiceAdminInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface common to all installers which perform connection check to the
|
||||||
|
remote master.
|
||||||
|
"""
|
||||||
|
|
||||||
|
skip_conncheck = knob(
|
||||||
|
None,
|
||||||
|
description="skip connection check to remote master",
|
||||||
|
)
|
||||||
|
skip_conncheck = enroll_only(skip_conncheck)
|
||||||
|
skip_conncheck = replica_install_only(skip_conncheck)
|
||||||
@@ -2,11 +2,19 @@
|
|||||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||||
#
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
DNS installer module
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import enum
|
||||||
|
|
||||||
# absolute import is necessary because IPA module dns clashes with python-dns
|
# absolute import is necessary because IPA module dns clashes with python-dns
|
||||||
from dns import resolver
|
from dns import resolver
|
||||||
|
import six
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
@@ -14,6 +22,8 @@ from subprocess import CalledProcessError
|
|||||||
from ipalib import api
|
from ipalib import api
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
from ipalib import util
|
from ipalib import util
|
||||||
|
from ipalib.install import hostname
|
||||||
|
from ipalib.install.service import enroll_only, prepare_only
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipaplatform.constants import constants
|
from ipaplatform.constants import constants
|
||||||
from ipaplatform import services
|
from ipaplatform import services
|
||||||
@@ -21,6 +31,9 @@ from ipapython import ipautil
|
|||||||
from ipapython import sysrestore
|
from ipapython import sysrestore
|
||||||
from ipapython import dnsutil
|
from ipapython import dnsutil
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
|
from ipapython.dnsutil import check_zone_overlap
|
||||||
|
from ipapython.install import typing
|
||||||
|
from ipapython.install.core import knob
|
||||||
from ipapython.ipa_log_manager import root_logger
|
from ipapython.ipa_log_manager import root_logger
|
||||||
from ipapython.admintool import ScriptError
|
from ipapython.admintool import ScriptError
|
||||||
from ipapython.ipautil import user_input
|
from ipapython.ipautil import user_input
|
||||||
@@ -32,6 +45,9 @@ from ipaserver.install import dnskeysyncinstance
|
|||||||
from ipaserver.install import odsexporterinstance
|
from ipaserver.install import odsexporterinstance
|
||||||
from ipaserver.install import opendnssecinstance
|
from ipaserver.install import opendnssecinstance
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
ip_addresses = []
|
ip_addresses = []
|
||||||
reverse_zones = []
|
reverse_zones = []
|
||||||
|
|
||||||
@@ -392,3 +408,119 @@ def uninstall():
|
|||||||
dnskeysync = dnskeysyncinstance.DNSKeySyncInstance(fstore)
|
dnskeysync = dnskeysyncinstance.DNSKeySyncInstance(fstore)
|
||||||
if dnskeysync.is_configured():
|
if dnskeysync.is_configured():
|
||||||
dnskeysync.uninstall()
|
dnskeysync.uninstall()
|
||||||
|
|
||||||
|
|
||||||
|
class DNSForwardPolicy(enum.Enum):
|
||||||
|
ONLY = 'only'
|
||||||
|
FIRST = 'first'
|
||||||
|
|
||||||
|
|
||||||
|
class DNSInstallInterface(hostname.HostNameInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface of the DNS installer
|
||||||
|
|
||||||
|
Knobs defined here will be available in:
|
||||||
|
* ipa-server-install
|
||||||
|
* ipa-replica-prepare
|
||||||
|
* ipa-replica-install
|
||||||
|
* ipa-dns-install
|
||||||
|
"""
|
||||||
|
|
||||||
|
allow_zone_overlap = knob(
|
||||||
|
None,
|
||||||
|
description="Create DNS zone even if it already exists",
|
||||||
|
)
|
||||||
|
allow_zone_overlap = prepare_only(allow_zone_overlap)
|
||||||
|
|
||||||
|
reverse_zones = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[str], [],
|
||||||
|
description=("The reverse DNS zone to use. This option can be used "
|
||||||
|
"multiple times"),
|
||||||
|
cli_names='--reverse-zone',
|
||||||
|
cli_metavar='REVERSE_ZONE',
|
||||||
|
)
|
||||||
|
reverse_zones = prepare_only(reverse_zones)
|
||||||
|
|
||||||
|
@reverse_zones.validator
|
||||||
|
def reverse_zones(self, values):
|
||||||
|
if not self.allow_zone_overlap:
|
||||||
|
for zone in values:
|
||||||
|
check_zone_overlap(zone)
|
||||||
|
|
||||||
|
no_reverse = knob(
|
||||||
|
None,
|
||||||
|
description="Do not create new reverse DNS zone",
|
||||||
|
)
|
||||||
|
no_reverse = prepare_only(no_reverse)
|
||||||
|
|
||||||
|
auto_reverse = knob(
|
||||||
|
None,
|
||||||
|
description="Create necessary reverse zones",
|
||||||
|
)
|
||||||
|
auto_reverse = prepare_only(auto_reverse)
|
||||||
|
|
||||||
|
zonemgr = knob(
|
||||||
|
str, None,
|
||||||
|
description=("DNS zone manager e-mail address. Defaults to "
|
||||||
|
"hostmaster@DOMAIN"),
|
||||||
|
)
|
||||||
|
zonemgr = prepare_only(zonemgr)
|
||||||
|
|
||||||
|
@zonemgr.validator
|
||||||
|
def zonemgr(self, value):
|
||||||
|
# validate the value first
|
||||||
|
try:
|
||||||
|
# IDNA support requires unicode
|
||||||
|
encoding = getattr(sys.stdin, 'encoding', None)
|
||||||
|
if encoding is None:
|
||||||
|
encoding = 'utf-8'
|
||||||
|
value = value.decode(encoding)
|
||||||
|
bindinstance.validate_zonemgr_str(value)
|
||||||
|
except ValueError as e:
|
||||||
|
# FIXME we can do this in better way
|
||||||
|
# https://fedorahosted.org/freeipa/ticket/4804
|
||||||
|
# decode to proper stderr encoding
|
||||||
|
stderr_encoding = getattr(sys.stderr, 'encoding', None)
|
||||||
|
if stderr_encoding is None:
|
||||||
|
stderr_encoding = 'utf-8'
|
||||||
|
error = unicode(e).encode(stderr_encoding)
|
||||||
|
raise ValueError(error)
|
||||||
|
|
||||||
|
forwarders = knob(
|
||||||
|
# pylint: disable=invalid-sequence-index
|
||||||
|
typing.List[ipautil.CheckedIPAddress], None,
|
||||||
|
description=("Add a DNS forwarder. This option can be used multiple "
|
||||||
|
"times"),
|
||||||
|
cli_names='--forwarder',
|
||||||
|
)
|
||||||
|
forwarders = enroll_only(forwarders)
|
||||||
|
|
||||||
|
no_forwarders = knob(
|
||||||
|
None,
|
||||||
|
description="Do not add any DNS forwarders, use root servers instead",
|
||||||
|
)
|
||||||
|
no_forwarders = enroll_only(no_forwarders)
|
||||||
|
|
||||||
|
auto_forwarders = knob(
|
||||||
|
None,
|
||||||
|
description="Use DNS forwarders configured in /etc/resolv.conf",
|
||||||
|
)
|
||||||
|
auto_forwarders = enroll_only(auto_forwarders)
|
||||||
|
|
||||||
|
forward_policy = knob(
|
||||||
|
DNSForwardPolicy, None,
|
||||||
|
description=("DNS forwarding policy for global forwarders"),
|
||||||
|
)
|
||||||
|
forward_policy = enroll_only(forward_policy)
|
||||||
|
|
||||||
|
no_dnssec_validation = knob(
|
||||||
|
None,
|
||||||
|
description="Disable DNSSEC validation",
|
||||||
|
)
|
||||||
|
no_dnssec_validation = enroll_only(no_dnssec_validation)
|
||||||
|
|
||||||
|
dnssec_master = False
|
||||||
|
disable_dnssec_master = False
|
||||||
|
kasp_db_file = None
|
||||||
|
force = False
|
||||||
|
|||||||
25
ipaserver/install/dogtag.py
Normal file
25
ipaserver/install/dogtag.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dogtag-based service installer module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipalib.install import service
|
||||||
|
from ipalib.install.service import prepare_only, replica_install_only
|
||||||
|
from ipapython.install.core import knob
|
||||||
|
|
||||||
|
|
||||||
|
class DogtagInstallInterface(service.ServiceInstallInterface):
|
||||||
|
"""
|
||||||
|
Interface common to all Dogtag-based service installers
|
||||||
|
"""
|
||||||
|
|
||||||
|
ca_file = knob(
|
||||||
|
str, None,
|
||||||
|
description="location of CA PKCS#12 file",
|
||||||
|
cli_metavar='FILE',
|
||||||
|
)
|
||||||
|
ca_file = prepare_only(ca_file)
|
||||||
|
ca_file = replica_install_only(ca_file)
|
||||||
@@ -2,6 +2,10 @@
|
|||||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||||
#
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
KRA installer module
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
@@ -15,7 +19,9 @@ from ipaserver.install import custodiainstance
|
|||||||
from ipaserver.install import cainstance
|
from ipaserver.install import cainstance
|
||||||
from ipaserver.install import krainstance
|
from ipaserver.install import krainstance
|
||||||
from ipaserver.install import dsinstance
|
from ipaserver.install import dsinstance
|
||||||
from ipaserver.install import service
|
from ipaserver.install import service as _service
|
||||||
|
|
||||||
|
from . import dogtag
|
||||||
|
|
||||||
|
|
||||||
def install_check(api, replica_config, options):
|
def install_check(api, replica_config, options):
|
||||||
@@ -109,7 +115,7 @@ def install(api, replica_config, options):
|
|||||||
ra_only=ra_only,
|
ra_only=ra_only,
|
||||||
promote=promote)
|
promote=promote)
|
||||||
|
|
||||||
service.print_msg("Restarting the directory server")
|
_service.print_msg("Restarting the directory server")
|
||||||
ds = dsinstance.DsInstance()
|
ds = dsinstance.DsInstance()
|
||||||
ds.restart()
|
ds.restart()
|
||||||
|
|
||||||
@@ -134,3 +140,15 @@ def uninstall(standalone):
|
|||||||
kra.stop_tracking_certificates(stop_certmonger=not standalone)
|
kra.stop_tracking_certificates(stop_certmonger=not standalone)
|
||||||
if kra.is_installed():
|
if kra.is_installed():
|
||||||
kra.uninstall()
|
kra.uninstall()
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
|||||||
@@ -2,7 +2,539 @@
|
|||||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
# 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 .install import Server
|
||||||
from .replicainstall import Replica
|
from .replicainstall import Replica
|
||||||
|
|
||||||
from .upgrade import upgrade_check, upgrade
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user