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

610 lines
23 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 tempfile, os, pwd, traceback, logging, shutil
2011-01-28 15:45:19 -05:00
import grp
from ConfigParser import SafeConfigParser
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
from ipaserver.install.replication import check_replication_plugin
from ipaserver.install.installutils import HostnameLocalhost, resolve_host
from ipaserver.plugins.ldap2 import ldap2
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
2010-12-07 18:23:05 -05:00
CACERT="/etc/ipa/ca.crt"
REPLICA_INFO_TOP_DIR=None
class ReplicaConfig:
def __init__(self):
self.realm_name = ""
2008-05-23 14:51:50 -04:00
self.domain_name = ""
self.master_host_name = ""
self.dirman_password = ""
self.host_name = ""
self.dir = ""
2010-11-01 13:51:14 -04:00
self.subject_base = ""
def parse_options():
usage = "%prog [options] REPLICA_FILE"
2010-10-29 20:24:31 +02:00
parser = IPAOptionParser(usage=usage, version=version.VERSION)
2008-02-20 11:03:46 -05:00
parser.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false",
help="do not configure ntp", default=True)
parser.add_option("-d", "--debug", dest="debug", action="store_true",
default=False, help="gather extra debugging information")
2010-10-29 20:24:31 +02:00
parser.add_option("-p", "--password", dest="password", sensitive=True,
help="Directory Manager (existing master) password")
parser.add_option("-w", "--admin-password", dest="admin_password", sensitive=True,
help="Admin user Kerberos password used for connection check")
parser.add_option("--setup-dns", dest="setup_dns", action="store_true",
default=False, help="configure bind with our zone")
2009-09-01 23:28:52 +02:00
parser.add_option("--forwarder", dest="forwarders", action="append",
type="ip", help="Add a DNS forwarder")
2009-09-01 23:28:52 +02:00
parser.add_option("--no-forwarders", dest="no_forwarders", action="store_true",
default=False, help="Do not add any DNS forwarders, use root servers instead")
2011-01-04 08:55:47 -05:00
parser.add_option("--no-reverse", dest="no_reverse", action="store_true",
default=False, help="Do not create reverse DNS zone")
parser.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
default=False,
help="Do not use DNS for hostname lookup during installation")
parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
default=True, help="disables pkinit setup steps")
parser.add_option("--skip-conncheck", dest="skip_conncheck", action="store_true",
default=False, help="skip connection check to remote master")
parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
default=False, help="unattended installation never prompts the user")
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-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")
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 expand_info(filename, password):
top_dir = tempfile.mkdtemp("ipa")
tarfile = top_dir+"/files.tar"
dir = top_dir + "/realm_info"
ipautil.decrypt_file(filename, tarfile, password, top_dir)
ipautil.run(["tar", "xf", tarfile, "-C", top_dir])
os.remove(tarfile)
return top_dir, dir
def read_info(dir, rconfig):
filename = dir + "/realm_info"
fd = open(filename)
config = SafeConfigParser()
config.readfp(fd)
rconfig.realm_name = config.get("realm", "realm_name")
rconfig.master_host_name = config.get("realm", "master_host_name")
rconfig.domain_name = config.get("realm", "domain_name")
rconfig.host_name = config.get("realm", "destination_host")
2010-01-20 11:26:20 -05:00
rconfig.subject_base = config.get("realm", "subject_base")
def get_host_name(no_host_dns):
hostname = installutils.get_fqdn()
try:
installutils.verify_fqdn(hostname, no_host_dns)
except RuntimeError, e:
logging.error(str(e))
sys.exit(1)
return hostname
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)
def install_ca(config):
# FIXME, need to pass along the CA plugin to use
2010-03-30 15:25:46 -04:00
cafile = config.dir + "/cacert.p12"
if not ipautil.file_exists(cafile):
# CA not used on the server, return empty instances
return (None, None)
try:
from ipaserver.install import cainstance
except ImportError:
print >> sys.stderr, "Import failed: %s" % sys.exc_value
sys.exit(1)
if not cainstance.check_inst():
print "A CA was specified but the dogtag certificate server"
print "is not installed on the system"
print "Please install dogtag and restart the setup program"
sys.exit(1)
2011-03-10 00:06:15 -05:00
pkcs12_info = None
if ipautil.file_exists(config.dir + "/dogtagcert.p12"):
pkcs12_info = (config.dir + "/dogtagcert.p12",
2011-03-10 00:06:15 -05:00
config.dir + "/dirsrv_pin.txt")
cs = cainstance.CADSInstance()
2011-01-28 15:45:19 -05:00
cs.create_instance(config.realm_name, config.host_name,
2011-03-10 00:06:15 -05:00
config.domain_name, config.dirman_password,
pkcs12_info)
cs.load_pkcs12()
cs.enable_ssl()
cs.restart_instance()
ca = cainstance.CAInstance(config.realm_name, certs.NSS_DIR)
2011-01-28 15:45:19 -05:00
ca.configure_instance(config.host_name, config.dirman_password,
config.dirman_password, pkcs12_info=(cafile,),
master_host=config.master_host_name,
subject_base=config.subject_base)
# The dogtag DS instance needs to be restarted after installation.
# The procedure for this is: stop dogtag, stop DS, start DS, start
# dogtag
#
# The service_name trickery is due to the service naming we do
# internally. In the case of the dogtag DS the name doesn't match the
# unix service.
service_name = cs.service_name
service.print_msg("Restarting the directory and certificate servers")
cs.service_name = "dirsrv"
ca.stop()
2011-03-10 00:06:15 -05:00
cs.stop("PKI-IPA")
cs.start("PKI-IPA")
ca.start()
cs.service_name = service_name
return (ca, cs)
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()
ldappwd_filename = config.dir + "/ldappwd"
kpasswd_filename = config.dir + "/kpasswd.keytab"
#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,
ldappwd_filename, kpasswd_filename,
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):
# 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)
# 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 = installutils.parse_ip_address(ip_address)
ip_address = str(ip)
2011-05-27 20:29:33 +02:00
ip_prefixlen = ip.prefixlen
2011-01-04 08:55:47 -05:00
create_reverse = True
if options.unattended:
# In unattended mode just use the cmdline flag
create_reverse = not options.no_reverse
else:
if options.no_reverse:
create_reverse = False
else:
# In interactive mode, if the flag was not explicitly
# specified, ask the user
create_reverse = bindinstance.create_reverse()
2011-01-04 08:55:47 -05:00
2011-05-27 20:29:33 +02:00
bind.setup(config.host_name, ip_address, ip_prefixlen, config.realm_name,
2010-11-11 19:27:27 +01:00
config.domain_name, forwarders, options.conf_ntp, create_reverse)
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 = installutils.parse_ip_address(ip_address)
ip_address = str(ip)
2011-05-27 20:29:33 +02:00
ip_prefixlen = ip.prefixlen
2011-05-27 20:29:33 +02:00
bind.add_master_dns_records(config.host_name, ip_address, ip_prefixlen,
config.realm_name, config.domain_name,
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:
service.stop("dirsrv")
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():
if not check_replication_plugin():
sys.exit(1)
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_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_info(dir, config)
config.dirman_password = dirman_password
host = get_host_name(options.no_host_dns)
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
# check connection
if not options.skip_conncheck:
print "Run connection check to master"
args = ["/usr/sbin/ipa-replica-conncheck", "--master", config.master_host_name,
"--auto-master-check", "--realm", config.realm_name,
"--principal", "admin",
"--hostname", config.host_name]
if options.admin_password:
args.extend(["--password", options.admin_password])
cafile = config.dir + "/cacert.p12"
if ipautil.file_exists(cafile): # with CA
args.append('--check-ca')
logging.debug("Running ipa-replica-conncheck with following arguments: %s" %
" ".join(args))
(stdin, stderr, returncode) = ipautil.run(args,raiseonerr=False, capture_output=False)
if returncode != 0:
sys.exit("Connection check failed!" +
"\nPlease fix your network settings according to error messages above." +
"\nIf the check results are not valid it can be skipped with --skip-conncheck parameter.")
else:
print "Connection check OK"
# 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) = install_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
2010-12-10 14:53:06 -05:00
if 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)
if CA:
CA.import_ra_cert(dir + "/ra.p12")
CA.fix_ra_perms()
service.restart("httpd")
2010-01-20 11:26:20 -05:00
service.print_msg("Setting the certificate subject base")
CA.set_subject_in_config(util.realm_to_suffix(config.realm_name))
# 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()
service.restart("dirsrv")
service.restart("krb5kdc")
2010-01-20 11:26:20 -05:00
service.restart("httpd")
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.
service.chkconfig_on('ipa')
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)