mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Allow upgrading CA-less to CA-full using ipa-ca-install.
Part of https://fedorahosted.org/freeipa/ticket/3737 Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
committed by
Petr Viktorin
parent
8bbdfff102
commit
d27e77adc5
@@ -22,6 +22,7 @@ import sys
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
import os, shutil
|
import os, shutil
|
||||||
|
from ConfigParser import RawConfigParser
|
||||||
|
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
|
|
||||||
@@ -29,15 +30,17 @@ from ipaserver.install import installutils, service
|
|||||||
from ipaserver.install import certs
|
from ipaserver.install import certs
|
||||||
from ipaserver.install.installutils import (HostnameLocalhost, ReplicaConfig,
|
from ipaserver.install.installutils import (HostnameLocalhost, ReplicaConfig,
|
||||||
expand_replica_info, read_replica_info, get_host_name, BadHostError,
|
expand_replica_info, read_replica_info, get_host_name, BadHostError,
|
||||||
private_ccache, read_replica_info_dogtag_port)
|
private_ccache, read_replica_info_dogtag_port, validate_external_cert)
|
||||||
from ipaserver.install import dsinstance, cainstance, bindinstance
|
from ipaserver.install import dsinstance, cainstance, bindinstance
|
||||||
from ipaserver.install.replication import replica_conn_check
|
from ipaserver.install.replication import replica_conn_check
|
||||||
from ipapython import version
|
from ipapython import version
|
||||||
from ipalib import api, util
|
from ipalib import api, util, certstore, x509
|
||||||
|
from ipalib.constants import CACERT
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipapython.config import IPAOptionParser
|
from ipapython.config import IPAOptionParser
|
||||||
from ipapython import sysrestore
|
from ipapython import sysrestore
|
||||||
from ipapython import dogtag
|
from ipapython import dogtag
|
||||||
|
from ipapython import certdb
|
||||||
from ipapython.ipa_log_manager import *
|
from ipapython.ipa_log_manager import *
|
||||||
from ipaplatform import services
|
from ipaplatform import services
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
@@ -63,14 +66,34 @@ def parse_options():
|
|||||||
default=False, help="skip check for updated CA DS schema on the remote master")
|
default=False, help="skip check for updated CA DS schema on the remote master")
|
||||||
parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
|
parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
|
||||||
default=False, help="unattended installation never prompts the user")
|
default=False, help="unattended installation never prompts the user")
|
||||||
|
parser.add_option("--external-ca", dest="external_ca", action="store_true",
|
||||||
|
default=False, help="Generate a CSR to be signed by an external CA")
|
||||||
|
parser.add_option("--external_cert_file", dest="external_cert_file",
|
||||||
|
help="PEM file containing a certificate signed by the external CA")
|
||||||
|
parser.add_option("--external_ca_file", dest="external_ca_file",
|
||||||
|
help="PEM file containing the external CA chain")
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
safe_options = parser.get_safe_opts(options)
|
safe_options = parser.get_safe_opts(options)
|
||||||
|
|
||||||
if len(args) != 1:
|
if args:
|
||||||
parser.error("you must provide a file generated by ipa-replica-prepare")
|
filename = args[0]
|
||||||
|
|
||||||
return safe_options, options, args[0]
|
if len(args) != 1:
|
||||||
|
parser.error("you must provide a file generated by "
|
||||||
|
"ipa-replica-prepare")
|
||||||
|
else:
|
||||||
|
filename = None
|
||||||
|
|
||||||
|
if options.external_ca:
|
||||||
|
if options.external_cert_file:
|
||||||
|
parser.error("You cannot specify --external_cert_file "
|
||||||
|
"together with --external-ca")
|
||||||
|
if options.external_ca_file:
|
||||||
|
parser.error("You cannot specify --external_ca_file together "
|
||||||
|
"with --external-ca")
|
||||||
|
|
||||||
|
return safe_options, options, filename
|
||||||
|
|
||||||
def get_dirman_password():
|
def get_dirman_password():
|
||||||
return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False)
|
return installutils.read_password("Directory Manager (existing master)", confirm=False, validate=False)
|
||||||
@@ -83,20 +106,18 @@ def install_dns_records(config, options):
|
|||||||
return
|
return
|
||||||
|
|
||||||
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
bind = bindinstance.BindInstance(dm_password=config.dirman_password)
|
||||||
|
disconnect = False
|
||||||
try:
|
try:
|
||||||
|
if not api.Backend.ldap2.isconnected():
|
||||||
api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
|
api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
|
||||||
bind_pw=config.dirman_password)
|
bind_pw=config.dirman_password)
|
||||||
|
disconnect = True
|
||||||
bind.add_ipa_ca_dns_records(config.host_name, config.domain_name)
|
bind.add_ipa_ca_dns_records(config.host_name, config.domain_name)
|
||||||
finally:
|
finally:
|
||||||
if api.Backend.ldap2.isconnected():
|
if api.Backend.ldap2.isconnected() and disconnect:
|
||||||
api.Backend.ldap2.disconnect()
|
api.Backend.ldap2.disconnect()
|
||||||
|
|
||||||
def main():
|
def install_replica(safe_options, options, filename):
|
||||||
safe_options, options, filename = parse_options()
|
|
||||||
|
|
||||||
if os.geteuid() != 0:
|
|
||||||
sys.exit("\nYou must be root to run this script.\n")
|
|
||||||
|
|
||||||
standard_logging_setup(log_file_name, debug=options.debug)
|
standard_logging_setup(log_file_name, debug=options.debug)
|
||||||
|
|
||||||
root_logger.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))
|
root_logger.debug('%s was invoked with argument "%s" and options: %s' % (sys.argv[0], filename, safe_options))
|
||||||
@@ -204,6 +225,188 @@ def main():
|
|||||||
root_logger.error(str(e))
|
root_logger.error(str(e))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def install_master(safe_options, options):
|
||||||
|
standard_logging_setup(paths.IPASERVER_CA_INSTALL_LOG, debug=options.debug)
|
||||||
|
|
||||||
|
root_logger.debug(
|
||||||
|
"%s was invoked with options: %s" % (sys.argv[0], safe_options))
|
||||||
|
root_logger.debug("IPA version %s" % version.VENDOR_VERSION)
|
||||||
|
|
||||||
|
global sstore
|
||||||
|
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||||
|
|
||||||
|
if not dsinstance.DsInstance().is_configured():
|
||||||
|
sys.exit("IPA server is not configured on this system.\n")
|
||||||
|
|
||||||
|
api.bootstrap(in_server=True)
|
||||||
|
api.finalize()
|
||||||
|
|
||||||
|
if api.env.enable_ra:
|
||||||
|
sys.exit("CA is already installed.\n")
|
||||||
|
|
||||||
|
dm_password = options.password
|
||||||
|
if not dm_password:
|
||||||
|
if options.unattended:
|
||||||
|
sys.exit('Directory Manager password required')
|
||||||
|
try:
|
||||||
|
dm_password = get_dirman_password()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(0)
|
||||||
|
if dm_password is None:
|
||||||
|
sys.exit("Directory Manager password required")
|
||||||
|
|
||||||
|
api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
|
||||||
|
bind_pw=dm_password)
|
||||||
|
|
||||||
|
config = api.Command['config_show']()['result']
|
||||||
|
subject_base = config['ipacertificatesubjectbase'][0]
|
||||||
|
|
||||||
|
if options.external_ca:
|
||||||
|
if cainstance.is_step_one_done():
|
||||||
|
print ("CA is already installed.\nRun the installer with "
|
||||||
|
"--external-cert-file and --external-ca-file.")
|
||||||
|
sys.exit(1)
|
||||||
|
elif options.external_cert_file:
|
||||||
|
if not cainstance.is_step_one_done():
|
||||||
|
print ("CA is not installed yet. To install with an external CA "
|
||||||
|
"is a two-stage process.\nFirst run the installer with "
|
||||||
|
"--external-ca.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
validate_external_cert(options.external_cert_file,
|
||||||
|
options.external_ca_file, subject_base)
|
||||||
|
except ValueError, e:
|
||||||
|
print e
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if options.external_cert_file:
|
||||||
|
external = 2
|
||||||
|
elif options.external_ca:
|
||||||
|
external = 1
|
||||||
|
else:
|
||||||
|
external = 0
|
||||||
|
|
||||||
|
realm_name = api.env.realm
|
||||||
|
domain_name = api.env.domain
|
||||||
|
host_name = api.env.host
|
||||||
|
|
||||||
|
dirname = dsinstance.config_dirname(
|
||||||
|
dsinstance.realm_to_serverid(realm_name))
|
||||||
|
cadb = certs.CertDB(realm_name, subject_base=subject_base)
|
||||||
|
dsdb = certs.CertDB(realm_name, nssdir=dirname, subject_base=subject_base)
|
||||||
|
|
||||||
|
for db in (cadb, dsdb):
|
||||||
|
for nickname, trust_flags in db.list_certs():
|
||||||
|
if nickname in (certdb.get_ca_nickname(realm_name),
|
||||||
|
'ipaCert',
|
||||||
|
'Signing-Cert'):
|
||||||
|
print ("Certificate with nickname %s is present in %s, "
|
||||||
|
"cannot continue." % (nickname, db.secdir))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
cert = db.get_cert_from_db(nickname)
|
||||||
|
if not cert:
|
||||||
|
continue
|
||||||
|
subject = DN(str(x509.get_subject(cert)))
|
||||||
|
if subject in (DN('CN=Certificate Authority', subject_base),
|
||||||
|
DN('CN=IPA RA', subject_base),
|
||||||
|
DN('CN=Object Signing Cert', subject_base)):
|
||||||
|
print ("Certificate with subject %s is present in %s, "
|
||||||
|
"cannot continue." % (subject, db.secdir))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
ca = cainstance.CAInstance(realm_name, certs.NSS_DIR,
|
||||||
|
dogtag_constants=dogtag.install_constants)
|
||||||
|
ca.create_ra_agent_db = False
|
||||||
|
if external == 0:
|
||||||
|
ca.configure_instance(host_name, domain_name, dm_password,
|
||||||
|
dm_password, subject_base=subject_base)
|
||||||
|
elif external == 1:
|
||||||
|
ca.configure_instance(host_name, domain_name, dm_password,
|
||||||
|
dm_password, csr_file=paths.ROOT_IPA_CSR,
|
||||||
|
subject_base=subject_base)
|
||||||
|
else:
|
||||||
|
ca.configure_instance(host_name, domain_name, dm_password,
|
||||||
|
dm_password,
|
||||||
|
cert_file=options.external_cert_file,
|
||||||
|
cert_chain_file=options.external_ca_file,
|
||||||
|
subject_base=subject_base)
|
||||||
|
|
||||||
|
ca.stop(ca.dogtag_constants.PKI_INSTANCE_NAME)
|
||||||
|
|
||||||
|
ca.ldap_enable('CA', host_name, dm_password,
|
||||||
|
ipautil.realm_to_suffix(realm_name), ['caRenewalMaster'])
|
||||||
|
|
||||||
|
ca.enable_client_auth_to_db()
|
||||||
|
|
||||||
|
# Install CA DNS records
|
||||||
|
config = ReplicaConfig()
|
||||||
|
config.realm_name = realm_name
|
||||||
|
config.domain_name = domain_name
|
||||||
|
config.host_name = config.master_host_name = host_name
|
||||||
|
config.dirman_password = dm_password
|
||||||
|
install_dns_records(config, options)
|
||||||
|
|
||||||
|
# We need to restart apache as we drop a new config file in there
|
||||||
|
services.knownservices.httpd.restart(capture_output=True)
|
||||||
|
|
||||||
|
# Update config file
|
||||||
|
parser = RawConfigParser()
|
||||||
|
parser.read(paths.IPA_DEFAULT_CONF)
|
||||||
|
parser.set('global', 'enable_ra', 'True')
|
||||||
|
parser.set('global', 'ra_plugin', 'dogtag')
|
||||||
|
parser.set('global', 'dogtag_version',
|
||||||
|
str(ca.dogtag_constants.DOGTAG_VERSION))
|
||||||
|
with open(paths.IPA_DEFAULT_CONF, 'w') as f:
|
||||||
|
parser.write(f)
|
||||||
|
|
||||||
|
# Store the new IPA CA cert chain in DS NSS database and LDAP
|
||||||
|
cadb = certs.CertDB(realm_name, subject_base=subject_base)
|
||||||
|
dsdb = certs.CertDB(realm_name, nssdir=dirname, subject_base=subject_base)
|
||||||
|
trust_flags = dict(reversed(cadb.list_certs()))
|
||||||
|
trust_chain = cadb.find_root_cert('ipaCert')[:-1]
|
||||||
|
for nickname in trust_chain[:-1]:
|
||||||
|
cert = cadb.get_cert_from_db(nickname, pem=False)
|
||||||
|
dsdb.add_cert(cert, nickname, trust_flags[nickname])
|
||||||
|
certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn,
|
||||||
|
cert, nickname, trust_flags[nickname])
|
||||||
|
|
||||||
|
nickname = trust_chain[-1]
|
||||||
|
cert = cadb.get_cert_from_db(nickname, pem=False)
|
||||||
|
dsdb.add_cert(cert, nickname, trust_flags[nickname])
|
||||||
|
certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn,
|
||||||
|
cert, nickname, trust_flags[nickname],
|
||||||
|
config_ipa=True, config_compat=True)
|
||||||
|
|
||||||
|
# Restart DS
|
||||||
|
ds = dsinstance.DsInstance()
|
||||||
|
ds.init_info(realm_name, host_name, domain_name, dm_password, subject_base,
|
||||||
|
1101, 1100, None)
|
||||||
|
ds.restart(ds.serverid)
|
||||||
|
|
||||||
|
# Store DS CA cert in Dogtag NSS database
|
||||||
|
dogtagdb = certs.CertDB(realm_name, nssdir=ca.dogtag_constants.ALIAS_DIR)
|
||||||
|
trust_flags = dict(reversed(dsdb.list_certs()))
|
||||||
|
server_certs = dsdb.find_server_certs()
|
||||||
|
trust_chain = dsdb.find_root_cert(server_certs[0][0])[:-1]
|
||||||
|
nickname = trust_chain[-1]
|
||||||
|
cert = dsdb.get_cert_from_db(nickname)
|
||||||
|
dogtagdb.add_cert(cert, nickname, trust_flags[nickname])
|
||||||
|
|
||||||
|
ca.start(ca.dogtag_constants.PKI_INSTANCE_NAME)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
safe_options, options, filename = parse_options()
|
||||||
|
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
sys.exit("\nYou must be root to run this script.\n")
|
||||||
|
|
||||||
|
if filename is not None:
|
||||||
|
install_replica(safe_options, options, filename)
|
||||||
|
else:
|
||||||
|
install_master(safe_options, options)
|
||||||
|
|
||||||
fail_message = '''
|
fail_message = '''
|
||||||
Your system may be partly configured.
|
Your system may be partly configured.
|
||||||
Run /usr/sbin/ipa-server-install --uninstall to clean up.
|
Run /usr/sbin/ipa-server-install --uninstall to clean up.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.\" A man page for ipa-replica-install
|
.\" A man page for ipa-ca-install
|
||||||
.\" Copyright (C) 2011 Red Hat, Inc.
|
.\" Copyright (C) 2011 Red Hat, Inc.
|
||||||
.\"
|
.\"
|
||||||
.\" This program is free software; you can redistribute it and/or modify
|
.\" This program is free software; you can redistribute it and/or modify
|
||||||
@@ -18,13 +18,15 @@
|
|||||||
.\"
|
.\"
|
||||||
.TH "ipa-ca-install" "1" "Jun 17 2011" "FreeIPA" "FreeIPA Manual Pages"
|
.TH "ipa-ca-install" "1" "Jun 17 2011" "FreeIPA" "FreeIPA Manual Pages"
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
ipa\-ca\-install \- Install a CA on a replica
|
ipa\-ca\-install \- Install a CA on a server
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
ipa\-ca\-install [\fIOPTION\fR]... replica_file
|
ipa\-ca\-install [\fIOPTION\fR]... [replica_file]
|
||||||
.SH "DESCRIPTION"
|
.SH "DESCRIPTION"
|
||||||
Adds a CA as an IPA\-managed service. This requires that the IPA server is already installed and configured.
|
Adds a CA as an IPA\-managed service. This requires that the IPA server is already installed and configured.
|
||||||
|
|
||||||
The replica_file is created using the ipa\-replica\-prepare utility and should be the same one used when originally installing the replica.
|
The replica_file is created using the ipa\-replica\-prepare utility and should be the same one used when originally installing the replica.
|
||||||
|
|
||||||
|
Alternatively, you can run ipa\-ca\-install without replica_file to upgrade from CA-less to CA-full.
|
||||||
.SH "OPTIONS"
|
.SH "OPTIONS"
|
||||||
\fB\-d\fR, \fB\-\-debug\fR
|
\fB\-d\fR, \fB\-\-debug\fR
|
||||||
Enable debug logging when more verbose output is needed
|
Enable debug logging when more verbose output is needed
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ class BasePathNamespace(object):
|
|||||||
IPAREPLICA_CONNCHECK_LOG = "/var/log/ipareplica-conncheck.log"
|
IPAREPLICA_CONNCHECK_LOG = "/var/log/ipareplica-conncheck.log"
|
||||||
IPAREPLICA_INSTALL_LOG = "/var/log/ipareplica-install.log"
|
IPAREPLICA_INSTALL_LOG = "/var/log/ipareplica-install.log"
|
||||||
IPARESTORE_LOG = "/var/log/iparestore.log"
|
IPARESTORE_LOG = "/var/log/iparestore.log"
|
||||||
|
IPASERVER_CA_INSTALL_LOG = "/var/log/ipaserver-ca-install.log"
|
||||||
IPASERVER_INSTALL_LOG = "/var/log/ipaserver-install.log"
|
IPASERVER_INSTALL_LOG = "/var/log/ipaserver-install.log"
|
||||||
IPASERVER_UNINSTALL_LOG = "/var/log/ipaserver-uninstall.log"
|
IPASERVER_UNINSTALL_LOG = "/var/log/ipaserver-uninstall.log"
|
||||||
IPAUPGRADE_LOG = "/var/log/ipaupgrade.log"
|
IPAUPGRADE_LOG = "/var/log/ipaupgrade.log"
|
||||||
|
|||||||
@@ -618,8 +618,8 @@ class CAInstance(service.Service):
|
|||||||
os.remove(cfg_file)
|
os.remove(cfg_file)
|
||||||
|
|
||||||
if self.external == 1:
|
if self.external == 1:
|
||||||
print "The next step is to get %s signed by your CA and re-run ipa-server-install as:" % self.csr_file
|
print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
|
||||||
print "ipa-server-install --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate"
|
print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
shutil.move(paths.CA_BACKUP_KEYS_P12, \
|
shutil.move(paths.CA_BACKUP_KEYS_P12, \
|
||||||
@@ -777,8 +777,8 @@ class CAInstance(service.Service):
|
|||||||
raise RuntimeError('Configuration of CA failed')
|
raise RuntimeError('Configuration of CA failed')
|
||||||
|
|
||||||
if self.external == 1:
|
if self.external == 1:
|
||||||
print "The next step is to get %s signed by your CA and re-run ipa-server-install as:" % self.csr_file
|
print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
|
||||||
print "ipa-server-install --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate"
|
print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# pkisilent makes a copy of the CA PKCS#12 file for us but gives
|
# pkisilent makes a copy of the CA PKCS#12 file for us but gives
|
||||||
|
|||||||
Reference in New Issue
Block a user