Files
freeipa/install/tools/ipa-replica-install
T

512 lines
20 KiB
Python
Raw Normal View History

#! /usr/bin/python -E
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
2010-12-09 13:59:11 +01:00
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
2010-12-09 13:59:11 +01:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import sys
import socket
import os, pwd, traceback, logging, shutil
2011-01-28 15:45:19 -05:00
import grp
2011-09-05 11:04:17 +02:00
from optparse import OptionGroup
from ipapython import ipautil
2010-12-07 18:23:05 -05:00
from ipaserver.install import dsinstance, installutils, krbinstance, service
from ipaserver.install import bindinstance, httpinstance, ntpinstance, certs
2011-09-20 10:40:16 +02:00
from ipaserver.install.replication import replica_conn_check
from ipaserver.install.installutils import HostnameLocalhost, resolve_host
from ipaserver.install.installutils import ReplicaConfig, expand_replica_info, read_replica_info
from ipaserver.install.installutils import get_host_name
from ipaserver.plugins.ldap2 import ldap2
from ipaserver.install import cainstance
from ipapython import version
from ipalib import api, errors, util
2010-10-29 20:24:31 +02:00
from ipapython.config import IPAOptionParser
2011-01-28 15:45:19 -05:00
from ipapython import sysrestore
from ipapython import services as ipaservices
2010-12-07 18:23:05 -05:00
CACERT="/etc/ipa/ca.crt"
REPLICA_INFO_TOP_DIR=None
def parse_options():
usage = "%prog [options] REPLICA_FILE"
2010-10-29 20:24:31 +02:00
parser = IPAOptionParser(usage=usage, version=version.VERSION)
2011-09-05 11:04:17 +02:00
basic_group = OptionGroup(parser, "basic options")
basic_group.add_option("--setup-ca", dest="setup_ca", action="store_true",
default=False, help="configure a dogtag CA")
basic_group.add_option("-p", "--password", dest="password", sensitive=True,
help="Directory Manager (existing master) password")
2011-09-05 11:04:17 +02:00
basic_group.add_option("-w", "--admin-password", dest="admin_password", sensitive=True,
help="Admin user Kerberos password used for connection check")
2011-09-05 11:04:17 +02:00
basic_group.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false",
help="do not configure ntp", default=True)
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("--skip-conncheck", dest="skip_conncheck", action="store_true",
default=False, help="skip connection check to remote master")
basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="gather extra debugging information")
basic_group.add_option("-U", "--unattended", dest="unattended", action="store_true",
default=False, help="unattended installation never prompts the user")
parser.add_option_group(basic_group)
cert_group = OptionGroup(parser, "certificate system options")
cert_group.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
default=True, help="disables pkinit setup steps")
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")
2011-09-05 11:04:17 +02:00
dns_group.add_option("--forwarder", dest="forwarders", action="append",
type="ip", help="Add a DNS forwarder")
2011-09-05 11:04:17 +02:00
dns_group.add_option("--no-forwarders", dest="no_forwarders", action="store_true",
2009-09-01 23:28:52 +02:00
default=False, help="Do not add any DNS forwarders, use root servers instead")
2011-09-05 11:04:17 +02:00
dns_group.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use")
dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true",
2011-01-04 08:55:47 -05:00
default=False, help="Do not create reverse DNS zone")
2011-09-05 11:04:17 +02:00
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")
2011-09-05 11:04:17 +02:00
parser.add_option_group(dns_group)
options, args = parser.parse_args()
2010-10-29 20:24:31 +02:00
safe_options = parser.get_safe_opts(options)
if len(args) != 1:
parser.error("you must provide a file generated by ipa-replica-prepare")
2009-09-01 23:28:52 +02:00
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")
2011-07-11 10:14:53 +02:00
if options.reverse_zone:
parser.error("You cannot specify a --reverse-zone option without the --setup-dns option")
2011-01-04 08:55:47 -05:00
if options.no_reverse:
parser.error("You cannot specify a --no-reverse option without the --setup-dns option")
2009-09-01 23:28:52 +02:00
elif options.forwarders and options.no_forwarders:
parser.error("You cannot specify a --forwarder option together with --no-forwarders")
elif not options.forwarders and not options.no_forwarders:
parser.error("You must specify at least one --forwarder option or --no-forwarders option")
2011-07-11 10:14:53 +02:00
elif options.reverse_zone and options.no_reverse:
parser.error("You cannot specify a --reverse-zone option together with --no-reverse")
2009-09-01 23:28:52 +02:00
2010-10-29 20:24:31 +02:00
return safe_options, options, args[0]
def get_dirman_password():
return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False)
def set_owner(config, dir):
2011-01-28 15:45:19 -05:00
pw = pwd.getpwnam(dsinstance.DS_USER)
os.chown(dir, pw.pw_uid, pw.pw_gid)
2010-12-07 18:23:05 -05:00
def install_replica_ds(config):
dsinstance.check_existing_installation()
dsinstance.check_ports()
# if we have a pkcs12 file, create the cert db from
# that. Otherwise the ds setup will create the CA
# cert
pkcs12_info = None
if ipautil.file_exists(config.dir + "/dscert.p12"):
pkcs12_info = (config.dir + "/dscert.p12",
config.dir + "/dirsrv_pin.txt")
ds = dsinstance.DsInstance()
2011-01-28 15:45:19 -05:00
ds.create_replica(config.realm_name,
2010-12-07 18:23:05 -05:00
config.master_host_name, config.host_name,
config.domain_name, config.dirman_password,
pkcs12_info)
2008-03-27 09:33:01 -04:00
return ds
def install_krb(config, setup_pkinit=False):
krb = krbinstance.KrbInstance()
#pkinit files
pkcs12_info = None
if ipautil.file_exists(config.dir + "/pkinitcert.p12"):
pkcs12_info = (config.dir + "/pkinitcert.p12",
config.dir + "/pkinit_pin.txt")
2011-01-28 15:45:19 -05:00
krb.create_replica(config.realm_name,
2011-01-11 10:27:48 -05:00
config.master_host_name, config.host_name,
config.domain_name, config.dirman_password,
setup_pkinit, pkcs12_info)
def install_ca_cert(config):
2010-12-07 18:23:05 -05:00
cafile = config.dir + "/ca.crt"
if not ipautil.file_exists(cafile):
raise RuntimeError("Ca cert file is not available")
try:
shutil.copy(cafile, CACERT)
os.chmod(CACERT, 0444)
except Exception, e:
print "error copying files: " + str(e)
sys.exit(1)
def install_http(config, auto_redirect):
# if we have a pkcs12 file, create the cert db from
# that. Otherwise the ds setup will create the CA
# cert
pkcs12_info = None
if ipautil.file_exists(config.dir + "/httpcert.p12"):
pkcs12_info = (config.dir + "/httpcert.p12",
config.dir + "/http_pin.txt")
http = httpinstance.HTTPInstance()
http.create_instance(config.realm_name, config.host_name, config.domain_name, config.dirman_password, False, pkcs12_info, self_signed_ca=True, auto_redirect=auto_redirect)
# Now copy the autoconfiguration files
if ipautil.file_exists(config.dir + "/preferences.html"):
try:
shutil.copy(config.dir + "/preferences.html", "/usr/share/ipa/html/preferences.html")
shutil.copy(config.dir + "/configure.jar", "/usr/share/ipa/html/configure.jar")
except Exception, e:
print "error copying files: " + str(e)
sys.exit(1)
2009-09-01 23:28:52 +02:00
def install_bind(config, options):
api.Backend.ldap2.connect(bind_dn="cn=Directory Manager",
bind_pw=config.dirman_password)
2009-09-01 23:28:52 +02:00
if options.forwarders:
forwarders = options.forwarders
else:
forwarders = ()
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
ip_address = resolve_host(config.host_name)
2010-12-01 17:22:56 +01:00
if not ip_address:
sys.exit("Unable to resolve IP address for host name")
ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
ip_address = str(ip)
2011-01-04 08:55:47 -05:00
2011-07-11 10:14:53 +02:00
if options.reverse_zone:
if not bindinstance.verify_reverse_zone(options.reverse_zone, ip):
sys.exit(1)
reverse_zone = bindinstance.normalize_zone(options.reverse_zone)
else:
2011-07-11 10:14:53 +02:00
reverse_zone = bindinstance.find_reverse_zone(ip)
if reverse_zone is None and not options.no_reverse:
reverse_zone = bindinstance.get_reverse_zone_default(ip)
if not options.unattended and bindinstance.create_reverse():
reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip)
if reverse_zone is not None:
print "Using reverse zone %s" % reverse_zone
bind.setup(config.host_name, ip_address, config.realm_name,
config.domain_name, forwarders, options.conf_ntp, reverse_zone)
bind.create_instance()
def install_dns_records(config, options):
2011-01-24 11:42:53 -05:00
if not bindinstance.dns_container_exists(config.master_host_name,
util.realm_to_suffix(config.realm_name)):
return
2011-01-24 11:42:53 -05:00
# We have to force to connect to the remote master because we do this step
# before our DS server is installed.
cur_uri = api.Backend.ldap2.ldap_uri
object.__setattr__(api.Backend.ldap2, 'ldap_uri',
'ldaps://%s' % config.master_host_name)
api.Backend.ldap2.connect(bind_dn="cn=Directory Manager",
2011-01-24 11:42:53 -05:00
bind_pw=config.dirman_password,
tls_cacertfile=CACERT)
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
ip_address = resolve_host(config.host_name)
if not ip_address:
sys.exit("Unable to resolve IP address for host name")
ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
ip_address = str(ip)
2011-07-11 10:14:53 +02:00
reverse_zone = bindinstance.find_reverse_zone(ip)
2011-07-11 10:14:53 +02:00
bind.add_master_dns_records(config.host_name, ip_address,
config.realm_name, config.domain_name,
2011-07-11 10:14:53 +02:00
reverse_zone, options.conf_ntp)
2011-01-24 11:42:53 -05:00
#set it back to the default
api.Backend.ldap2.disconnect()
object.__setattr__(api.Backend.ldap2, 'ldap_uri', cur_uri)
def check_dirsrv():
serverids = dsinstance.check_existing_installation()
if serverids:
print ""
print "An existing Directory Server has been detected."
2008-08-06 19:17:13 +02:00
if not ipautil.user_input("Do you wish to remove it and create a new one?", False):
print ""
print "Only a single Directory Server instance is allowed on an IPA"
print "server, the one used by IPA itself."
sys.exit(1)
try:
ipaservices.knownservices.dirsrv.stop()
except:
pass
for serverid in serverids:
dsinstance.erase_ds_instance_data(serverid)
(ds_unsecure, ds_secure) = dsinstance.check_ports()
if not ds_unsecure or not ds_secure:
print "IPA requires ports 389 and 636 for the Directory Server."
print "These are currently in use:"
if not ds_unsecure:
print "\t389"
if not ds_secure:
print "\t636"
sys.exit(1)
def check_bind():
if not bindinstance.check_inst(unattended=True):
print "Aborting installation"
sys.exit(1)
def main():
2010-10-29 20:24:31 +02:00
safe_options, options, filename = parse_options()
2008-03-27 09:33:01 -04:00
installutils.standard_logging_setup("/var/log/ipareplica-install.log", options.debug)
2010-10-29 20:24:31 +02:00
logging.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))
if not ipautil.file_exists(filename):
sys.exit("Replica file %s does not exist" % filename)
client_fstore = sysrestore.FileStore('/var/lib/ipa-client/sysrestore')
if client_fstore.has_files():
sys.exit("IPA client is already configured on this system.\n"
+ "Please uninstall it first before configuring the replica.")
2011-01-28 15:45:19 -05:00
global sstore
sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
# check the bind is installed
if options.setup_dns:
check_bind()
check_dirsrv()
# get the directory manager password
dirman_password = options.password
if not dirman_password:
try:
dirman_password = get_dirman_password()
except KeyboardInterrupt:
sys.exit(0)
try:
top_dir, dir = expand_replica_info(filename, dirman_password)
global REPLICA_INFO_TOP_DIR
REPLICA_INFO_TOP_DIR = top_dir
except Exception, e:
print "ERROR: Failed to decrypt or open the replica file."
print "Verify you entered the correct Directory Manager password."
sys.exit(1)
config = ReplicaConfig()
read_replica_info(dir, config)
config.dirman_password = dirman_password
try:
host = get_host_name(options.no_host_dns)
except RuntimeError, e:
logging.error(str(e))
sys.exit(1)
if config.host_name != host:
try:
print "This replica was created for '%s' but this machine is named '%s'" % (config.host_name, host)
2008-08-06 19:17:13 +02:00
if not ipautil.user_input("This may cause problems. Continue?", True):
sys.exit(0)
config.host_name = host
print ""
except KeyboardInterrupt:
sys.exit(0)
config.dir = dir
config.setup_ca = options.setup_ca
# check connection
if not options.skip_conncheck:
replica_conn_check(config.master_host_name, config.host_name, config.realm_name, options.setup_ca, options.admin_password)
# Create the management framework config file
# Note: We must do this before bootstraping and finalizing ipalib.api
2011-06-17 14:19:45 +02:00
old_umask = os.umask(022) # must be readable for httpd
try:
fd = open("/etc/ipa/default.conf", "w")
fd.write("[global]\n")
fd.write("host=" + config.host_name + "\n")
2011-06-17 14:19:45 +02:00
fd.write("basedn=" + util.realm_to_suffix(config.realm_name) + "\n")
fd.write("realm=" + config.realm_name + "\n")
fd.write("domain=" + config.domain_name + "\n")
fd.write("xmlrpc_uri=https://%s/ipa/xml\n" % config.host_name)
fd.write("ldap_uri=ldapi://%%2fvar%%2frun%%2fslapd-%s.socket\n" % dsinstance.realm_to_serverid(config.realm_name))
if ipautil.file_exists(config.dir + "/cacert.p12"):
fd.write("enable_ra=True\n")
fd.write("ra_plugin=dogtag\n")
fd.write("mode=production\n")
fd.close()
finally:
os.umask(old_umask)
api.bootstrap(in_server=True)
api.finalize()
2011-01-28 15:45:19 -05:00
# Create DS group if it doesn't exist yet
try:
grp.getgrnam(dsinstance.DS_GROUP)
logging.debug("ds group %s exists" % dsinstance.DS_GROUP)
group_exists = True
except KeyError:
group_exists = False
args = ["/usr/sbin/groupadd", "-r", dsinstance.DS_GROUP]
try:
ipautil.run(args)
logging.debug("done adding DS group")
except ipautil.CalledProcessError, e:
logging.critical("failed to add DS group: %s" % e)
sstore.backup_state("install", "group_exists", group_exists)
#Automatically disable pkinit w/ dogtag until that is supported
#[certs.ipa_self_signed() must be called only after api.finalize()]
if not ipautil.file_exists(config.dir + "/pkinitcert.p12") and not certs.ipa_self_signed():
options.setup_pkinit = False
2010-12-07 18:23:05 -05:00
# Install CA cert so that we can do SSL connections with ldap
install_ca_cert(config)
# Try out the password
2010-12-07 18:23:05 -05:00
ldapuri = 'ldaps://%s' % config.master_host_name
try:
conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='')
2010-12-07 18:23:05 -05:00
conn.connect(bind_dn='cn=directory manager',
bind_pw=config.dirman_password,
tls_cacertfile=CACERT)
try:
entry = conn.find_entries(u'fqdn=%s' % host, ['dn', 'fqdn'], u'%s,%s' % (api.env.container_host, api.env.basedn))
2010-11-23 13:22:56 +01:00
print "The host %s already exists on the master server. Depending on your configuration, you may perform the following:\n" % host
print "Remove the replication agreement, if any:"
print " %% ipa-replica-manage del %s" % host
2010-11-23 13:22:56 +01:00
print "Remove the host entry:"
print " %% ipa host-del %s" % host
sys.exit(3)
except errors.NotFound:
pass
conn.disconnect()
except errors.ACIError:
sys.exit("\nThe password provided is incorrect for LDAP server %s" % config.master_host_name)
except errors.LDAPError:
sys.exit("\nUnable to connect to LDAP server %s" % config.master_host_name)
2008-02-20 11:03:46 -05:00
# Configure ntpd
if options.conf_ntp:
ntp = ntpinstance.NTPInstance()
ntp.create_instance()
# Configure the CA if necessary
(CA, cs) = cainstance.install_replica_ca(config)
2011-01-24 11:42:53 -05:00
# Always try to install DNS records
install_dns_records(config, options)
2008-02-20 11:03:46 -05:00
# Configure dirsrv
2010-12-07 18:23:05 -05:00
ds = install_replica_ds(config)
# We need to ldap_enable the CA now that DS is up and running
if CA and config.setup_ca:
2011-01-25 18:46:26 +01:00
CA.ldap_enable('CA', config.host_name, config.dirman_password,
util.realm_to_suffix(config.realm_name))
cs.add_simple_service('dogtagldap/%s@%s' % (config.host_name, config.realm_name))
cs.add_cert_to_service()
2011-03-10 00:06:15 -05:00
install_krb(config, setup_pkinit=options.setup_pkinit)
install_http(config, auto_redirect=options.ui_redirect)
if CA:
CA.import_ra_cert(dir + "/ra.p12")
CA.fix_ra_perms()
ipaservices.knownservices.httpd.restart()
# The DS instance is created before the keytab, add the SSL cert we
# generated
ds.add_cert_to_service()
# Apply any LDAP updates. Needs to be done after the replica is synced-up
service.print_msg("Applying LDAP updates")
ds.apply_updates()
ipaservices.knownservices.dirsrv.restart()
ipaservices.knownservices.krb5kdc.restart()
ipaservices.knownservices.httpd.restart()
if options.setup_dns:
install_bind(config, options)
# Call client install script
try:
ipautil.run(["/usr/sbin/ipa-client-install", "--on-master", "--unattended", "--domain", config.domain_name, "--server", config.host_name, "--realm", config.realm_name])
except Exception, e:
print "Configuration of client side components failed!"
print "ipa-client-install returned: " + str(e)
raise RuntimeError("Failed to configure the client")
2008-03-27 09:33:01 -04:00
ds.replica_populate()
2008-03-27 09:33:01 -04:00
ds.init_memberof()
2011-04-26 10:39:29 +02:00
#Everything installed properly, activate ipa service.
ipaservices.knownservices.ipa.enable()
2011-04-26 10:39:29 +02:00
try:
if not os.geteuid()==0:
sys.exit("\nYou must be root to run this script.\n")
main()
sys.exit(0)
except SystemExit, e:
sys.exit(e)
except socket.error, (errno, errstr):
print errstr
except HostnameLocalhost:
print "The hostname resolves to the localhost address (127.0.0.1/::1)"
print "Please change your /etc/hosts file so that the hostname"
print "resolves to the ip address of your network interface."
print ""
print "Please fix your /etc/hosts file and restart the setup program"
except Exception, e:
print "creation of replica failed: %s" % str(e)
message = str(e)
for str in traceback.format_tb(sys.exc_info()[2]):
message = message + "\n" + str
logging.debug(message)
except KeyboardInterrupt:
print "Installation cancelled."
finally:
# always try to remove decrypted replica file
try:
if REPLICA_INFO_TOP_DIR:
shutil.rmtree(REPLICA_INFO_TOP_DIR)
except OSError:
pass
print ""
print "Your system may be partly configured."
print "Run /usr/sbin/ipa-server-install --uninstall to clean up."
# the only way to get here is on error or ^C
sys.exit(1)