summaryrefslogtreecommitdiffstats
path: root/ipaserver/install
diff options
context:
space:
mode:
Diffstat (limited to 'ipaserver/install')
-rw-r--r--ipaserver/install/cainstance.py561
-rw-r--r--ipaserver/install/dogtaginstance.py399
-rw-r--r--ipaserver/install/dsinstance.py87
-rw-r--r--ipaserver/install/installutils.py107
-rw-r--r--ipaserver/install/ipa_backup.py2
-rw-r--r--ipaserver/install/ipa_kra_install.py243
-rw-r--r--ipaserver/install/ipa_replica_prepare.py1
-rw-r--r--ipaserver/install/krainstance.py346
8 files changed, 1375 insertions, 371 deletions
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 2a8ecc00c..e8bb7d701 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -19,53 +19,51 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-import pwd
+import array
+import base64
+import binascii
+import ConfigParser
+import dbus
+import httplib
+import ldap
import os
-import sys
+import pwd
import re
+import shutil
+import stat
+import subprocess
+import sys
+import syslog
import time
-import ldap
-import base64
-import array
import tempfile
-import binascii
-import shutil
-import httplib
import urllib
import xml.dom.minidom
-import stat
-import syslog
-import ConfigParser
-import dbus
-from ipapython import dogtag
-from ipapython.certdb import get_ca_nickname
-from ipapython import certmonger
from ipalib import api
from ipalib import pkcs10, x509
from ipalib import errors
-from ipapython.dn import DN
-import subprocess
-import traceback
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipaplatform.tasks import tasks
+
+from ipapython import dogtag
+from ipapython import certmonger
from ipapython import ipautil
from ipapython import ipaldap
-from ipaplatform.tasks import tasks
-from ipaserver.install import service
-from ipaserver.install import installutils
-from ipaserver.install import dsinstance
+from ipapython.certdb import get_ca_nickname
+from ipapython.dn import DN
+from ipapython.ipa_log_manager import log_mgr,\
+ standard_logging_setup, root_logger
+
from ipaserver.install import certs
-from ipaserver.install.installutils import stopped_service
+from ipaserver.install import dsinstance
+from ipaserver.install import installutils
+from ipaserver.install import service
+from ipaserver.install.dogtaginstance import DogtagInstance
+from ipaserver.install.dogtaginstance import PKI_USER, DEFAULT_DSPORT
from ipaserver.plugins import ldap2
-from ipapython.ipa_log_manager import *
-from ipaplatform import services
-from ipaplatform.paths import paths
-
-HTTPD_CONFD = paths.HTTPD_CONF_D_DIR
-DEFAULT_DSPORT = dogtag.install_constants.DS_PORT
-PKI_USER = "pkiuser"
-PKI_DS_USER = dogtag.install_constants.DS_USER
# When IPA is installed with DNS support, this CNAME should hold all IPA
# replicas with CA configured
@@ -88,22 +86,6 @@ RootDNPwd= $PASSWORD
ConfigFile = /usr/share/pki/ca/conf/database.ldif
"""
-def check_inst():
- """
- Validate that the appropriate dogtag/RHCS packages have been installed.
- """
-
- # Check for a couple of binaries we need
- if not os.path.exists(dogtag.install_constants.SPAWN_BINARY):
- return False
- if not os.path.exists(dogtag.install_constants.DESTROY_BINARY):
- return False
-
- # This is the template tomcat file for a CA
- if not os.path.exists(paths.PKI_CONF_SERVER_XML):
- return False
-
- return True
def get_preop_pin(instance_root, instance_name):
# Only used for Dogtag 9
@@ -113,24 +95,27 @@ def get_preop_pin(instance_root, instance_name):
# read the config file and get the preop pin
try:
- f=open(filename)
+ f = open(filename)
except IOError, e:
root_logger.error("Cannot open configuration file." + str(e))
raise e
data = f.read()
data = data.split('\n')
- pattern = re.compile("preop.pin=(.*)" )
+ pattern = re.compile("preop.pin=(.*)")
for line in data:
match = re.search(pattern, line)
- if (match):
- preop_pin=match.group(1)
+ if match:
+ preop_pin = match.group(1)
break
if preop_pin is None:
- raise RuntimeError("Unable to find preop.pin in %s. Is your CA already configured?" % filename)
+ raise RuntimeError(
+ "Unable to find preop.pin in %s. Is your CA already configured?" %
+ filename)
return preop_pin
+
def import_pkcs12(input_file, input_passwd, cert_database,
cert_passwd):
ipautil.run([paths.PK12UTIL, "-d", cert_database,
@@ -138,21 +123,23 @@ def import_pkcs12(input_file, input_passwd, cert_database,
"-k", cert_passwd,
"-w", input_passwd])
+
def get_value(s):
"""
Parse out a name/value pair from a Javascript variable.
"""
try:
- expr = s.split('=',1)
+ expr = s.split('=', 1)
value = expr[1]
value = value.replace('\"', '')
- value = value.replace(';','')
- value = value.replace('\\n','\n')
- value = value.replace('\\r','\r')
+ value = value.replace(';', '')
+ value = value.replace('\\n', '\n')
+ value = value.replace('\\r', '\r')
return value
except IndexError:
return None
+
def find_substring(data, value):
"""
Scan through a list looking for a string that starts with value.
@@ -161,6 +148,7 @@ def find_substring(data, value):
if d.startswith(value):
return get_value(d)
+
def get_defList(data):
"""
Return a dictionary of defList name/value pairs.
@@ -196,6 +184,7 @@ def get_defList(data):
return defdict
+
def get_outputList(data):
"""
Return a dictionary of outputList name/value pairs.
@@ -221,6 +210,7 @@ def get_outputList(data):
return outputdict
+
def get_crl_files(path=None):
"""
Traverse dogtag's CRL files in default CRL publish directory or in chosen
@@ -240,8 +230,8 @@ def get_crl_files(path=None):
def is_step_one_done():
- '''Read CS.cfg and determine if step one of an external CA install is done
- '''
+ """Read CS.cfg and determine if step one of an external CA install is done
+ """
path = dogtag.install_constants.CS_CFG_PATH
if not os.path.exists(path):
return False
@@ -251,6 +241,14 @@ def is_step_one_done():
return False
+def is_ca_installed_locally():
+ """Check if CA is installed locally by checking for existence of CS.cfg
+ :return:True/False
+ """
+ path = dogtag.install_constants.CS_CFG_PATH
+ return os.path.exists(path)
+
+
class CADSInstance(service.Service):
"""Certificate Authority DS instance
@@ -258,7 +256,8 @@ class CADSInstance(service.Service):
Thus this class only does uninstallation.
"""
def __init__(self, host_name=None, realm_name=None, domain_name=None, dm_password=None, dogtag_constants=None):
- service.Service.__init__(self, "pkids",
+ service.Service.__init__(
+ self, "pkids",
service_desc="directory server for the CA",
dm_password=dm_password,
ldapi=False,
@@ -284,7 +283,7 @@ class CADSInstance(service.Service):
serverid = self.restore_state("serverid")
# Just eat this state if it exists
- running = self.restore_state("running")
+ self.restore_state("running")
if not enabled is None and not enabled:
services.knownservices.dirsrv.disable()
@@ -297,41 +296,14 @@ class CADSInstance(service.Service):
dsdb.untrack_server_cert("Server-Cert")
dsinstance.erase_ds_instance_data(serverid)
- user_exists = self.restore_state("user_exists")
+ self.restore_state("user_exists")
# At one time we removed this user on uninstall. That can potentially
- # orphan files, or worse, if another useradd runs in the intermim,
+ # orphan files, or worse, if another useradd runs in the interim,
# cause files to have a new owner.
-def stop_tracking_certificates(dogtag_constants):
- """Stop tracking our certificates. Called on uninstall.
- """
- cmonger = services.knownservices.certmonger
- services.knownservices.messagebus.start()
- cmonger.start()
-
- for nickname in ['Server-Cert cert-pki-ca',
- 'auditSigningCert cert-pki-ca',
- 'ocspSigningCert cert-pki-ca',
- 'subsystemCert cert-pki-ca',
- 'caSigningCert cert-pki-ca']:
- try:
- certmonger.stop_tracking(
- dogtag_constants.ALIAS_DIR, nickname=nickname)
- except (ipautil.CalledProcessError, RuntimeError), e:
- root_logger.error(
- "certmonger failed to stop tracking certificate: %s" % str(e))
-
- try:
- certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname='ipaCert')
- except (ipautil.CalledProcessError, RuntimeError), e:
- root_logger.error(
- "certmonger failed to stop tracking certificate: %s" % str(e))
- cmonger.stop()
-
-
-class CAInstance(service.Service):
+class CAInstance(DogtagInstance):
"""
When using a dogtag CA the DS database contains just the
server cert for DS. The mod_nss database will contain the RA agent
@@ -353,19 +325,15 @@ class CAInstance(service.Service):
if dogtag_constants is None:
dogtag_constants = dogtag.configured_constants()
- service.Service.__init__(self,
- '%sd' % dogtag_constants.PKI_INSTANCE_NAME,
- service_desc="certificate server",
- dm_password=dm_password,
- ldapi=ldapi)
-
- self.dogtag_constants = dogtag_constants
- self.realm = realm
- self.admin_password = None
- self.fqdn = host_name
- self.domain = None
- self.pkcs12_info = None
- self.clone = False
+ super(CAInstance, self).__init__(
+ realm=realm,
+ subsystem="CA",
+ service_desc="certificate server",
+ dogtag_constants=dogtag_constants,
+ host_name=host_name,
+ dm_password=dm_password,
+ ldapi=ldapi
+ )
# for external CAs
self.external = 0
@@ -374,36 +342,17 @@ class CAInstance(service.Service):
self.cert_chain_file = None
self.create_ra_agent_db = True
- # The same database is used for mod_nss because the NSS context
- # will already have been initialized by Apache by the time
- # mod_python wants to do things.
- self.canickname = None
- if realm:
- self.canickname = get_ca_nickname(realm)
- self.basedn = DN(('o', 'ipaca'))
- self.ca_agent_db = tempfile.mkdtemp(prefix = "tmp-")
+ self.canickname = get_ca_nickname(realm)
self.ra_agent_db = ra_db
- self.ra_agent_pwd = None
- if ra_db:
- self.ra_agent_pwd = ra_db + "/pwdfile.txt"
- self.ds_port = DEFAULT_DSPORT
- self.security_domain_name = "IPA"
- self.server_root = dogtag_constants.SERVER_ROOT
+ self.ra_agent_pwd = self.ra_agent_db + "/pwdfile.txt"
self.ra_cert = None
self.requestId = None
-
- def __del__(self):
- shutil.rmtree(self.ca_agent_db, ignore_errors=True)
-
- def is_installed(self):
- """
- Installing with an external CA is a two-step process. This
- is used to determine if the first step has been done.
-
- Returns True/False
- """
- return os.path.exists(os.path.join(
- self.server_root, self.dogtag_constants.PKI_INSTANCE_NAME))
+ self.tracking_reqs = (('Server-Cert cert-pki-ca', None),
+ ('auditSigningCert cert-pki-ca', None),
+ ('ocspSigningCert cert-pki-ca', None),
+ ('subsystemCert cert-pki-ca', None),
+ ('caSigningCert cert-pki-ca', 'ipaCACertRenewal'))
+ self.log = log_mgr.get_logger(self)
def configure_instance(self, host_name, domain, dm_password,
admin_password, ds_port=DEFAULT_DSPORT,
@@ -440,12 +389,12 @@ class CAInstance(service.Service):
# Determine if we are installing as an externally-signed CA and
# what stage we're in.
if csr_file is not None:
- self.csr_file=csr_file
- self.external=1
+ self.csr_file = csr_file
+ self.external = 1
elif cert_file is not None:
- self.cert_file=cert_file
- self.cert_chain_file=cert_chain_file
- self.external=2
+ self.cert_file = cert_file
+ self.cert_chain_file = cert_chain_file
+ self.external = 2
self.step("creating certificate server user", self.__create_ca_user)
if self.dogtag_constants.DOGTAG_VERSION >= 10:
@@ -454,11 +403,11 @@ class CAInstance(service.Service):
if not ipautil.dir_exists(paths.VAR_LIB_PKI_CA_DIR):
self.step("creating pki-ca instance", self.create_instance)
self.step("configuring certificate server instance", self.__configure_instance)
- self.step("stopping certificate server instance to update CS.cfg", self.__stop)
+ self.step("stopping certificate server instance to update CS.cfg", self.stop_instance)
self.step("disabling nonces", self.__disable_nonce)
self.step("set up CRL publishing", self.__enable_crl_publish)
self.step("enable PKIX certificate path discovery and validation", self.enable_pkix)
- self.step("starting certificate server instance", self.__start)
+ self.step("starting certificate server instance", self.start_instance)
# Step 1 of external is getting a CSR so we don't need to do these
# steps until we get a cert back from the external CA.
if self.external != 1:
@@ -474,33 +423,28 @@ class CAInstance(service.Service):
self.step("enabling Subject Alternative Name", self.enable_subject_alternative_name)
self.step("enabling CRL and OCSP extensions for certificates", self.__set_crl_ocsp_extensions)
self.step("setting audit signing renewal to 2 years", self.set_audit_renewal)
- self.step("configuring certificate server to start on boot", self.__enable)
+ self.step("configuring certificate server to start on boot",
+ self.enable)
if not self.clone:
- self.step("restarting certificate server", self.__restart_instance)
+ self.step("restarting certificate server", self.restart_instance)
self.step("requesting RA certificate from CA", self.__request_ra_certificate)
self.step("issuing RA agent certificate", self.__issue_ra_cert)
self.step("adding RA agent as a trusted user", self.__configure_ra)
self.step("configure certmonger for renewals", self.configure_certmonger_renewal)
- self.step("configure certificate renewals", self.configure_renewal)
+ self.step("configure certificate renewals", self.configure_cert_renewal)
if not self.clone:
self.step("configure RA certificate renewal", self.configure_agent_renewal)
self.step("configure Server-Cert certificate renewal", self.track_servercert)
- self.step("Configure HTTP to proxy connections", self.__http_proxy)
+ self.step("Configure HTTP to proxy connections",
+ self.http_proxy)
self.start_creation(runtime=210)
- def __stop(self):
- self.stop()
-
- def __start(self):
- self.start()
-
-
def __spawn_instance(self):
"""
Create and configure a new CA instance using pkispawn.
- pkispawn requires a configuration file with IPA-specific
- parameters.
+ Creates the config file with IPA specific parameters
+ and passes it to the base class to call pkispawn
"""
# Create an empty and secured file
@@ -522,7 +466,7 @@ class CAInstance(service.Service):
config.set("CA", "pki_backup_password", self.admin_password)
# Client security database
- config.set("CA", "pki_client_database_dir", self.ca_agent_db)
+ config.set("CA", "pki_client_database_dir", self.agent_db)
config.set("CA", "pki_client_database_password", self.admin_password)
config.set("CA", "pki_client_database_purge", "False")
config.set("CA", "pki_client_pkcs12_password", self.admin_password)
@@ -535,7 +479,7 @@ class CAInstance(service.Service):
config.set("CA", "pki_admin_nickname", "ipa-ca-agent")
config.set("CA", "pki_admin_subject_dn",
str(DN(('cn', 'ipa-ca-agent'), self.subject_base)))
- config.set("CA", "pki_client_admin_cert_p12", paths.CA_AGENT_P12)
+ config.set("CA", "pki_client_admin_cert_p12", paths.DOGTAG_AGENT_P12)
# Directory server
config.set("CA", "pki_ds_ldap_port", str(self.ds_port))
@@ -562,7 +506,7 @@ class CAInstance(service.Service):
config.set("CA", "pki_audit_signing_nickname", "auditSigningCert cert-pki-ca")
config.set("CA", "pki_ca_signing_nickname", "caSigningCert cert-pki-ca")
- if (self.clone):
+ if self.clone:
cafile = self.pkcs12_info[0]
shutil.copy(cafile, paths.TMP_CA_P12)
pent = pwd.getpwnam(PKI_USER)
@@ -610,22 +554,9 @@ class CAInstance(service.Service):
with open(cfg_file, "wb") as f:
config.write(f)
- # Define the things we don't want logged
- nolog = (self.admin_password, self.dm_password,)
-
- args = [paths.PKISPAWN, "-s", "CA", "-f", cfg_file ]
-
- with open(cfg_file) as f:
- root_logger.debug(
- 'Contents of pkispawn configuration file (%s):\n%s' %
- (cfg_file, ipautil.nolog_replace(f.read(), nolog)))
-
self.backup_state('installed', True)
try:
- ipautil.run(args, nolog=nolog)
- except ipautil.CalledProcessError, e:
- root_logger.critical("failed to configure ca instance %s" % e)
- raise RuntimeError('Configuration of CA failed')
+ DogtagInstance.spawn_instance(self, cfg_file)
finally:
os.remove(cfg_file)
@@ -634,10 +565,10 @@ class CAInstance(service.Service):
print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
sys.exit(0)
else:
- shutil.move(paths.CA_BACKUP_KEYS_P12, \
+ shutil.move(paths.CA_BACKUP_KEYS_P12,
paths.CACERT_P12)
- root_logger.debug("completed creating ca instance")
+ self.log.debug("completed creating ca instance")
def create_instance(self):
"""
@@ -668,29 +599,21 @@ class CAInstance(service.Service):
self.backup_state('installed', True)
ipautil.run(args, env={'PKI_HOSTNAME':self.fqdn})
- def __enable(self):
- self.backup_state("enabled", self.is_enabled())
- # We do not let the system start IPA components on its own,
- # Instead we reply on the IPA init script to start only enabled
- # components as found in our LDAP configuration tree
- # We need to install DS before we can actually ldap_enable a service.
- # so actual enablement is delayed.
-
def __create_ca_user(self):
try:
pwd.getpwnam(PKI_USER)
- root_logger.debug("ca user %s exists" % PKI_USER)
+ self.log.debug("ca user %s exists", PKI_USER)
except KeyError:
- root_logger.debug("adding ca user %s" % PKI_USER)
+ self.log.debug("adding ca user %s", PKI_USER)
args = [paths.USERADD, "-c", "CA System User",
"-d", paths.VAR_LIB,
"-s", paths.NOLOGIN,
"-M", "-r", PKI_USER]
try:
ipautil.run(args)
- root_logger.debug("done adding user")
+ self.log.debug("done adding user")
except ipautil.CalledProcessError, e:
- root_logger.critical("failed to add user %s" % e)
+ self.log.critical("failed to add user %s", e)
def __configure_instance(self):
# Only used for Dogtag 9
@@ -701,7 +624,7 @@ class CAInstance(service.Service):
args = [paths.PERL, paths.PKISILENT, "ConfigureCA",
"-cs_hostname", self.fqdn,
"-cs_port", str(self.dogtag_constants.ADMIN_SECURE_PORT),
- "-client_certdb_dir", self.ca_agent_db,
+ "-client_certdb_dir", self.agent_db,
"-client_certdb_pwd", self.admin_password,
"-preop_pin" , preop_pin,
"-domain_name", self.security_domain_name,
@@ -746,7 +669,7 @@ class CAInstance(service.Service):
else:
args.append("-external")
args.append("false")
- if (self.clone):
+ if self.clone:
"""sd = security domain --> all CS systems get registered to
a security domain. This is set to the hostname and port of
the master CA.
@@ -785,7 +708,7 @@ class CAInstance(service.Service):
ipautil.run(args, env={'PKI_HOSTNAME':self.fqdn}, nolog=nolog)
except ipautil.CalledProcessError, e:
- root_logger.critical("failed to configure ca instance %s" % e)
+ self.log.critical("failed to configure ca instance %s", e)
raise RuntimeError('Configuration of CA failed')
if self.external == 1:
@@ -798,15 +721,7 @@ class CAInstance(service.Service):
if ipautil.file_exists(paths.ROOT_TMP_CA_P12):
shutil.move(paths.ROOT_TMP_CA_P12, paths.CACERT_P12)
- root_logger.debug("completed creating ca instance")
-
- def __restart_instance(self):
- try:
- self.restart(self.dogtag_constants.PKI_INSTANCE_NAME)
- except Exception:
- # TODO: roll back here?
- root_logger.debug(traceback.format_exc())
- root_logger.critical("Failed to restart the certificate server. See the installation log for details.")
+ self.log.debug("completed creating ca instance")
def __disable_nonce(self):
# Turn off Nonces
@@ -830,9 +745,9 @@ class CAInstance(service.Service):
os.write(admin_fd, self.admin_password)
os.close(admin_fd)
- # Look thru the cert chain to get all the certs we need to add
+ # Look through the cert chain to get all the certs we need to add
# trust for
- p = subprocess.Popen([paths.CERTUTIL, "-d", self.ca_agent_db,
+ p = subprocess.Popen([paths.CERTUTIL, "-d", self.agent_db,
"-O", "-n", "ipa-ca-agent"], stdout=subprocess.PIPE)
chain = p.stdout.read()
@@ -851,7 +766,7 @@ class CAInstance(service.Service):
self.__run_certutil(
['-M', '-t', 'CT,C,C', '-n',
nick],
- database=self.ca_agent_db, pwd_file=self.admin_password)
+ database=self.agent_db, pwd_file=self.admin_password)
finally:
os.remove(admin_name)
@@ -867,12 +782,13 @@ class CAInstance(service.Service):
'-v',
'-n', 'ipa-ca-agent',
'-p', self.admin_password,
- '-d', self.ca_agent_db,
+ '-d', self.agent_db,
'-r', '/ca/agent/ca/profileReview?requestId=%s' % self.requestId,
'%s' % ipautil.format_netloc(
self.fqdn, self.dogtag_constants.AGENT_SECURE_PORT),
]
- (stdout, stderr, returncode) = ipautil.run(args, nolog=(self.admin_password,))
+ (stdout, _stderr, _returncode) = ipautil.run(
+ args, nolog=(self.admin_password,))
data = stdout.split(self.dogtag_constants.RACERT_LINE_SEP)
params = get_defList(data)
@@ -888,13 +804,14 @@ class CAInstance(service.Service):
'-v',
'-n', 'ipa-ca-agent',
'-p', self.admin_password,
- '-d', self.ca_agent_db,
+ '-d', self.agent_db,
'-e', params,
'-r', '/ca/agent/ca/profileProcess',
'%s' % ipautil.format_netloc(
self.fqdn, self.dogtag_constants.AGENT_SECURE_PORT),
]
- (stdout, stderr, returncode) = ipautil.run(args, nolog=(self.admin_password,))
+ (stdout, _stderr, _returncode) = ipautil.run(
+ args, nolog=(self.admin_password,))
data = stdout.split(self.dogtag_constants.RACERT_LINE_SEP)
outputList = get_outputList(data)
@@ -973,7 +890,7 @@ class CAInstance(service.Service):
conn.unbind()
- def __run_certutil(self, args, database=None, pwd_file=None,stdin=None):
+ def __run_certutil(self, args, database=None, pwd_file=None, stdin=None):
if not database:
database = self.ra_agent_db
if not pwd_file:
@@ -999,7 +916,7 @@ class CAInstance(service.Service):
os.close(f)
os.chmod(self.ra_agent_pwd, stat.S_IRUSR)
- (stdout, stderr, returncode) = self.__run_certutil(["-N"])
+ self.__run_certutil(["-N"])
def __get_ca_chain(self):
try:
@@ -1016,8 +933,8 @@ class CAInstance(service.Service):
try:
ipautil.run([paths.PK12UTIL,
"-n", "ipa-ca-agent",
- "-o", paths.CA_AGENT_P12,
- "-d", self.ca_agent_db,
+ "-o", paths.DOGTAG_AGENT_P12,
+ "-d", self.agent_db,
"-k", pwd_name,
"-w", pwd_name])
finally:
@@ -1035,14 +952,15 @@ class CAInstance(service.Service):
# makes openssl throw up.
data = base64.b64decode(chain)
- (certlist, stderr, returncode) = ipautil.run([paths.OPENSSL,
+ (certlist, _stderr, _returncode) = ipautil.run(
+ [paths.OPENSSL,
"pkcs7",
"-inform",
"DER",
"-print_certs",
], stdin=data)
- # Ok, now we have all the certificates in certs, walk thru it
+ # Ok, now we have all the certificates in certs, walk through it
# and pull out each certificate and add it to our database
st = 1
@@ -1057,7 +975,7 @@ class CAInstance(service.Service):
(chain_fd, chain_name) = tempfile.mkstemp()
os.write(chain_fd, certlist[st:en+25])
os.close(chain_fd)
- (rdn, subject_dn) = certs.get_cert_nickname(certlist[st:en+25])
+ (_rdn, subject_dn) = certs.get_cert_nickname(certlist[st:en+25])
if subject_dn == ca_dn:
nick = get_ca_nickname(self.realm)
trust_flags = 'CT,C,C'
@@ -1070,7 +988,7 @@ class CAInstance(service.Service):
)
finally:
os.remove(chain_name)
- subid = subid + 1
+ subid += 1
def __request_ra_certificate(self):
# Create a noise file for generating our private key
@@ -1081,7 +999,10 @@ class CAInstance(service.Service):
# Generate our CSR. The result gets put into stdout
try:
- (stdout, stderr, returncode) = self.__run_certutil(["-R", "-k", "rsa", "-g", "2048", "-s", str(DN(('CN', 'IPA RA'), self.subject_base)), "-z", noise_name, "-a"])
+ (stdout, _stderr, _returncode) = self.__run_certutil(
+ ["-R", "-k", "rsa", "-g", "2048", "-s",
+ str(DN(('CN', 'IPA RA'), self.subject_base)),
+ "-z", noise_name, "-a"])
finally:
os.remove(noise_name)
@@ -1298,75 +1219,32 @@ class CAInstance(service.Service):
'OU=pki-ipa, O=IPA', str(self.subject_base)):
print "Updating subject_base in CA template failed"
- def enable_client_auth_to_db(self):
- """
- Enable client auth connection to the internal db.
- """
- caconfig = dogtag.install_constants.CS_CFG_PATH
-
- with stopped_service(self.dogtag_constants.SERVICE_NAME,
- instance_name=self.dogtag_constants.PKI_INSTANCE_NAME):
-
- # Enable file publishing, disable LDAP
- installutils.set_directive(caconfig,
- 'authz.instance.DirAclAuthz.ldap.ldapauth.authtype',
- 'SslClientAuth', quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'authz.instance.DirAclAuthz.ldap.ldapauth.bindDN',
- 'uid=pkidbuser,ou=people,o=ipa-ca', quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname',
- 'subsystemCert cert-pki-ca', quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'authz.instance.DirAclAuthz.ldap.ldapconn.port',
- str(dogtag.install_constants.DS_SECURE_PORT),
- quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'authz.instance.DirAclAuthz.ldap.ldapconn.secureConn',
- 'true', quotes=False, separator='=')
-
- installutils.set_directive(caconfig, 'internaldb.ldapauth.authtype',
- 'SslClientAuth', quotes=False, separator='=')
- installutils.set_directive(caconfig, 'internaldb.ldapauth.bindDN',
- 'uid=pkidbuser,ou=people,o=ipa-ca', quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'internaldb.ldapauth.clientCertNickname',
- 'subsystemCert cert-pki-ca', quotes=False, separator='=')
- installutils.set_directive(caconfig, 'internaldb.ldapconn.port',
- str(dogtag.install_constants.DS_SECURE_PORT),
- quotes=False, separator='=')
- installutils.set_directive(caconfig,
- 'internaldb.ldapconn.secureConn', 'true', quotes=False,
- separator='=')
-
def uninstall(self):
- if self.is_configured():
- self.print_msg("Unconfiguring CA")
-
enabled = self.restore_state("enabled")
if not enabled is None and not enabled:
self.disable()
- # Just eat this state if it exists
- installed = self.restore_state("installed")
- try:
- if self.dogtag_constants.DOGTAG_VERSION >= 10:
- ipautil.run([paths.PKIDESTROY, "-i",
- self.dogtag_constants.PKI_INSTANCE_NAME,
- "-s", "CA"])
- else:
+ if self.dogtag_constants.DOGTAG_VERSION >= 10:
+ DogtagInstance.uninstall(self)
+ else:
+ if self.is_configured():
+ self.print_msg("Unconfiguring CA")
+
+ try:
ipautil.run([paths.PKIREMOVE,
- "-pki_instance_root=/var/lib",
+ "-pki_instance_root=%s" % paths.VAR_LIB,
"-pki_instance_name=%s" %
self.dogtag_constants.PKI_INSTANCE_NAME,
"--force"])
- except ipautil.CalledProcessError, e:
- root_logger.critical("failed to uninstall CA instance %s" % e)
+ except ipautil.CalledProcessError, e:
+ self.log.critical("failed to uninstall CA instance %s", e)
+
+ self.restore_state("installed")
# At one time we removed this user on uninstall. That can potentially
- # orphan files, or worse, if another useradd runs in the intermim,
+ # orphan files, or worse, if another useradd runs in the interim,
# cause files to have a new owner.
- user_exists = self.restore_state("user_exists")
+ self.restore_state("user_exists")
services.knownservices.messagebus.start()
cmonger = services.knownservices.certmonger
@@ -1383,43 +1261,33 @@ class CAInstance(service.Service):
cmonger.stop()
# remove CRL files
- root_logger.info("Remove old CRL files")
+ self.log.info("Remove old CRL files")
try:
for f in get_crl_files():
- root_logger.debug("Remove %s", f)
+ self.log.debug("Remove %s", f)
installutils.remove_file(f)
except OSError, e:
- root_logger.warning("Error while removing old CRL files: %s" % e)
+ self.log.warning("Error while removing old CRL files: %s", e)
# remove CRL directory
- root_logger.info("Remove CRL directory")
+ self.log.info("Remove CRL directory")
if os.path.exists(self.dogtag_constants.CRL_PUBLISH_PATH):
try:
shutil.rmtree(self.dogtag_constants.CRL_PUBLISH_PATH)
except OSError, e:
- root_logger.warning("Error while removing CRL publish "
- "directory: %s" % e)
+ self.log.warning("Error while removing CRL publish "
+ "directory: %s", e)
def publish_ca_cert(self, location):
args = ["-L", "-n", self.canickname, "-a"]
- (cert, err, returncode) = self.__run_certutil(args)
+ (cert, _err, _returncode) = self.__run_certutil(args)
fd = open(location, "w+")
fd.write(cert)
fd.close()
os.chmod(location, 0444)
- def __http_proxy(self):
- template_filename = ipautil.SHARE_DIR + "ipa-pki-proxy.conf"
- sub_dict = dict(
- DOGTAG_PORT=self.dogtag_constants.AJP_PORT,
- CLONE='' if self.clone else '#',
- FQDN=self.fqdn,
- )
- template = ipautil.template_file(template_filename, sub_dict)
- with open(HTTPD_CONFD + "ipa-pki-proxy.conf", "w") as fd:
- fd.write(template)
-
- def configure_certmonger_renewal(self):
+ @staticmethod
+ def configure_certmonger_renewal():
"""
Create a new CA type for certmonger that will retrieve updated
certificates from the dogtag master server.
@@ -1450,41 +1318,30 @@ class CAInstance(service.Service):
pre_command=None,
post_command='renew_ra_cert')
except (ipautil.CalledProcessError, RuntimeError), e:
- root_logger.error(
- "certmonger failed to start tracking certificate: %s" % e)
+ self.log.error(
+ "certmonger failed to start tracking certificate: %s", e)
def __get_ca_pin(self):
try:
- return certmonger.get_pin('internal',
+ return certmonger.get_pin(
+ 'internal',
dogtag_constants=self.dogtag_constants)
except IOError, e:
raise RuntimeError(
'Unable to determine PIN for CA instance: %s' % e)
- def configure_renewal(self):
+ def configure_cert_renewal(self):
+ """
+ Configure system certificates for renewal.
+ """
reqs = (
('auditSigningCert cert-pki-ca', None),
('ocspSigningCert cert-pki-ca', None),
('subsystemCert cert-pki-ca', None),
('caSigningCert cert-pki-ca', 'ipaCACertRenewal'),
)
- pin = self.__get_ca_pin()
- # Server-Cert cert-pki-ca is renewed per-server
- for nickname, profile in reqs:
- try:
- certmonger.dogtag_start_tracking(
- ca='dogtag-ipa-ca-renew-agent',
- nickname=nickname,
- pin=pin,
- pinfile=None,
- secdir=self.dogtag_constants.ALIAS_DIR,
- pre_command='stop_pkicad',
- post_command='renew_ca_cert "%s"' % nickname,
- profile=profile)
- except (ipautil.CalledProcessError, RuntimeError), e:
- root_logger.error(
- "certmonger failed to start tracking certificate: %s" % e)
+ DogtagInstance.configure_renewal(self, reqs)
def track_servercert(self):
"""
@@ -1503,8 +1360,22 @@ class CAInstance(service.Service):
pre_command=None,
post_command=None)
except (ipautil.CalledProcessError, RuntimeError), e:
+ self.log.error(
+ "certmonger failed to start tracking certificate: %s", e)
+
+ @staticmethod
+ def stop_tracking_agent_certificate(dogtag_constants):
+ """Stop tracking agent certificate. Called on uninstall.
+ """
+ cmonger = services.knownservices.certmonger
+ services.knownservices.messagebus.start()
+ cmonger.start()
+ try:
+ certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname='ipaCert')
+ except (ipautil.CalledProcessError, RuntimeError), e:
root_logger.error(
- "certmonger failed to start tracking certificate: %s" % e)
+ "certmonger failed to stop tracking certificate: %s", e)
+ cmonger.stop()
def enable_subject_key_identifier(self):
"""
@@ -1517,7 +1388,7 @@ class CAInstance(service.Service):
# this is the default setting from pki-ca/pki-tomcat. Don't touch it
# if a user has manually modified it.
if setlist == '1,2,3,4,5,6,7,8' or setlist == '1,2,3,4,5,6,7,8,9':
- setlist = setlist + ',10'
+ setlist += ',10'
installutils.set_directive(
self.dogtag_constants.IPA_SERVICE_PROFILE,
'policyset.serverCertSet.list',
@@ -1565,7 +1436,7 @@ class CAInstance(service.Service):
# this is the default setting from pki-ca/pki-tomcat. Don't touch it
# if a user has manually modified it.
if setlist == '1,2,3,4,5,6,7,8,10' or setlist == '1,2,3,4,5,6,7,8,9,10':
- setlist = setlist + ',11'
+ setlist += ',11'
installutils.set_directive(
self.dogtag_constants.IPA_SERVICE_PROFILE,
'policyset.serverCertSet.list',
@@ -1608,13 +1479,14 @@ class CAInstance(service.Service):
"""
# Check the default validity period of the audit signing cert
# and set it to 2 years if it is 6 months.
- range = installutils.get_directive(
+ cert_range = installutils.get_directive(
'%s/caSignedLogCert.cfg' % self.dogtag_constants.SERVICE_PROFILE_DIR,
'policyset.caLogSigningSet.2.default.params.range',
separator='='
)
- root_logger.debug('caSignedLogCert.cfg profile validity range is %s' % range)
- if range == "180":
+ self.log.debug(
+ 'caSignedLogCert.cfg profile validity range is %s', cert_range)
+ if cert_range == "180":
installutils.set_directive(
'%s/caSignedLogCert.cfg' % self.dogtag_constants.SERVICE_PROFILE_DIR,
'policyset.caLogSigningSet.2.default.params.range',
@@ -1629,7 +1501,8 @@ class CAInstance(service.Service):
quotes=False,
separator='='
)
- root_logger.debug('updated caSignedLogCert.cfg profile validity range to 720')
+ self.log.debug(
+ 'updated caSignedLogCert.cfg profile validity range to 720')
return True
return False
@@ -1642,9 +1515,9 @@ class CAInstance(service.Service):
dn = DN(('cn', 'CA'), ('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'),
('cn', 'etc'), api.env.basedn)
- filter = '(ipaConfigString=caRenewalMaster)'
+ renewal_filter = '(ipaConfigString=caRenewalMaster)'
try:
- self.admin_conn.get_entries(base_dn=dn, filter=filter,
+ self.admin_conn.get_entries(base_dn=dn, filter=renewal_filter,
attrs_list=[])
except errors.NotFound:
return False
@@ -1684,6 +1557,31 @@ class CAInstance(service.Service):
self.admin_conn.update_entry(master_entry)
+ @staticmethod
+ def update_cert_config(nickname, cert, dogtag_constants=None):
+ """
+ When renewing a CA subsystem certificate the configuration file
+ needs to get the new certificate as well.
+
+ nickname is one of the known nicknames.
+ cert is a DER-encoded certificate.
+ """
+
+ if dogtag_constants is None:
+ dogtag_constants = dogtag.configured_constants()
+
+ # The cert directive to update per nickname
+ directives = {'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
+ 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
+ 'caSigningCert cert-pki-ca': 'ca.signing.cert',
+ 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
+ 'Server-Cert cert-pki-ca': 'ca.sslserver.cert'}
+
+ DogtagInstance.update_cert_cs_cfg(
+ nickname, cert, directives,
+ dogtag.configured_constants().CS_CFG_PATH,
+ dogtag_constants)
+
def replica_ca_install_check(config):
if not config.setup_ca:
return
@@ -1769,11 +1667,6 @@ def install_replica_ca(config, postinstall=False):
if ca.is_installed():
sys.exit("A CA is already configured on this system.")
- pkcs12_info = None
- if ipautil.file_exists(config.dir + "/dogtagcert.p12"):
- pkcs12_info = (config.dir + "/dogtagcert.p12",
- config.dir + "/dirsrv_pin.txt")
-
ca = CAInstance(config.realm_name, certs.NSS_DIR,
dogtag_constants=dogtag.install_constants)
if postinstall:
@@ -1812,32 +1705,6 @@ def install_replica_ca(config, postinstall=False):
return ca
-def update_cert_config(nickname, cert, dogtag_constants=None):
- """
- When renewing a CA subsystem certificate the configuration file
- needs to get the new certificate as well.
-
- nickname is one of the known nicknames.
- cert is a DER-encoded certificate.
- """
-
- if dogtag_constants is None:
- dogtag_constants = dogtag.configured_constants()
-
- # The cert directive to update per nickname
- directives = {'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
- 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
- 'caSigningCert cert-pki-ca': 'ca.signing.cert',
- 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
- 'Server-Cert cert-pki-ca': 'ca.sslserver.cert'}
-
- with stopped_service(dogtag_constants.SERVICE_NAME,
- instance_name=dogtag_constants.PKI_INSTANCE_NAME):
-
- installutils.set_directive(dogtag.configured_constants().CS_CFG_PATH,
- directives[nickname],
- base64.b64encode(cert),
- quotes=False, separator='=')
def update_people_entry(dercert):
"""
@@ -1874,11 +1741,11 @@ def update_people_entry(dercert):
conn.connect(
bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password)
- filter = conn.make_filter(
+ db_filter = conn.make_filter(
{'description': ';%s;%s' % (issuer, subject)},
exact=False, trailing_wildcard=False)
try:
- entries = conn.get_entries(base_dn, conn.SCOPE_SUBTREE, filter)
+ entries = conn.get_entries(base_dn, conn.SCOPE_SUBTREE, db_filter)
except errors.NotFound:
entries = []
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
new file mode 100644
index 000000000..c872f3103
--- /dev/null
+++ b/ipaserver/install/dogtaginstance.py
@@ -0,0 +1,399 @@
+# Authors: Ade Lee <alee@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import base64
+import os
+import shutil
+import tempfile
+import traceback
+
+from pki.client import PKIConnection
+import pki.system
+
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipapython import certmonger
+from ipapython import dogtag
+from ipapython import ipaldap
+from ipapython import ipautil
+from ipapython.dn import DN
+from ipaserver.install import service
+from ipaserver.install import installutils
+from ipaserver.install.installutils import stopped_service
+from ipapython.ipa_log_manager import log_mgr
+
+DEFAULT_DSPORT = dogtag.install_constants.DS_PORT
+
+PKI_USER = "pkiuser"
+PKI_DS_USER = dogtag.install_constants.DS_USER
+
+
+def check_inst(subsystem):
+ """
+ Validate that the appropriate dogtag/RHCS packages have been installed.
+ """
+
+ # Check for a couple of binaries we need
+ if not os.path.exists(dogtag.install_constants.SPAWN_BINARY):
+ return False
+ if not os.path.exists(dogtag.install_constants.DESTROY_BINARY):
+ return False
+
+ if not os.path.exists(paths.PKI_CONF_SERVER_XML_TEMPLATE % subsystem):
+ return False
+
+ return True
+
+
+def get_security_domain():
+ """
+ Get the security domain from the REST interface on the local Dogtag CA
+ This function will succeed if the local dogtag CA is up.
+ """
+ connection = PKIConnection()
+ domain_client = pki.system.SecurityDomainClient(connection)
+ info = domain_client.get_security_domain_info()
+ return info
+
+
+def is_installing_replica(sys_type):
+ """
+ We expect only one of each type of Dogtag subsystem in an IPA deployment.
+ That means that if a subsystem of the specified type has already been
+ deployed - and therefore appears in the security domain - then we must be
+ installing a replica.
+ """
+ info = get_security_domain()
+ try:
+ sys_list = info.systems[sys_type]
+ return len(sys_list.hosts) > 0
+ except KeyError:
+ return False
+
+
+class DogtagInstance(service.Service):
+ """
+ This is the base class for a Dogtag 10+ instance, which uses a
+ shared tomcat instance and DS to host the relevant subsystems.
+
+ It contains functions that will be common to installations of the
+ CA, KRA, and eventually TKS and TPS.
+ """
+
+ def __init__(self, realm, subsystem, service_desc, dogtag_constants=None,
+ host_name=None, dm_password=None, ldapi=True):
+ """Initializer"""
+
+ if dogtag_constants is None:
+ dogtag_constants = dogtag.configured_constants()
+
+ super(DogtagInstance, self).__init__(
+ '%sd' % dogtag_constants.PKI_INSTANCE_NAME,
+ service_desc=service_desc,
+ dm_password=dm_password,
+ ldapi=ldapi
+ )
+
+ self.dogtag_constants = dogtag_constants
+ self.realm = realm
+ self.dm_password = None
+ self.admin_password = None
+ self.fqdn = host_name
+ self.domain = None
+ self.pkcs12_info = None
+ self.clone = False
+
+ self.basedn = DN(('o', 'ipa%s' % subsystem.lower()))
+ self.admin_user = DN(('uid', 'admin'), ('ou', 'people'), ('o', 'ipaca'))
+ self.agent_db = tempfile.mkdtemp(prefix="tmp-")
+ self.ds_port = DEFAULT_DSPORT
+ self.server_root = dogtag_constants.SERVER_ROOT
+ self.subsystem = subsystem
+ self.security_domain_name = "IPA"
+ self.tracking_reqs = None
+
+ # replication parameters
+ self.master_host = None
+ self.master_replication_port = None
+ self.subject_base = None
+
+ self.log = log_mgr.get_logger(self)
+
+ def __del__(self):
+ shutil.rmtree(self.agent_db, ignore_errors=True)
+
+ def is_installed(self):
+ """
+ Determine if subsystem instance has been installed.
+
+ Returns True/False
+ """
+ return os.path.exists(os.path.join(
+ self.server_root, self.dogtag_constants.PKI_INSTANCE_NAME,
+ self.subsystem.lower()))
+
+ def spawn_instance(self, cfg_file, nolog_list=None):
+ """
+ Create and configure a new Dogtag instance using pkispawn.
+ Passes in a configuration file with IPA-specific
+ parameters.
+ """
+ subsystem = self.subsystem
+
+ # Define the things we don't want logged
+ if nolog_list is None:
+ nolog_list = []
+ nolog = tuple(nolog_list) + (self.admin_password, self.dm_password)
+
+ args = [paths.PKISPAWN,
+ "-s", subsystem,
+ "-f", cfg_file]
+
+ with open(cfg_file) as f:
+ self.log.debug(
+ 'Contents of pkispawn configuration file (%s):\n%s',
+ cfg_file, ipautil.nolog_replace(f.read(), nolog))
+
+ try:
+ ipautil.run(args, nolog=nolog)
+ except ipautil.CalledProcessError, e:
+ self.log.critical("failed to configure %s instance %s",
+ subsystem, e)
+ raise RuntimeError('Configuration of %s failed' % subsystem)
+
+ def enable(self):
+ self.backup_state("enabled", self.is_enabled())
+ # We do not let the system start IPA components on its own,
+ # Instead we reply on the IPA init script to start only enabled
+ # components as found in our LDAP configuration tree
+ # We need to install DS before we can actually ldap_enable a service.
+ # so actual enablement is delayed.
+
+ def restart_instance(self):
+ try:
+ self.restart(self.dogtag_constants.PKI_INSTANCE_NAME)
+ except Exception:
+ self.log.debug(traceback.format_exc())
+ self.log.critical(
+ "Failed to restart the Dogtag instance."
+ "See the installation log for details.")
+
+ def start_instance(self):
+ try:
+ self.start(self.dogtag_constants.PKI_INSTANCE_NAME)
+ except Exception:
+ self.log.debug(traceback.format_exc())
+ self.log.critical(
+ "Failed to restart the Dogtag instance."
+ "See the installation log for details.")
+
+ def stop_instance(self):
+ try:
+ self.stop(self.dogtag_constants.PKI_INSTANCE_NAME)
+ except Exception:
+ self.log.debug(traceback.format_exc())
+ self.log.critical(
+ "Failed to restart the Dogtag instance."
+ "See the installation log for details.")
+
+ def enable_client_auth_to_db(self, config):
+ """
+ Enable client auth connection to the internal db.
+ Path to CS.cfg config file passed in.
+ """
+
+ with stopped_service(
+ self.dogtag_constants.SERVICE_NAME,
+ instance_name=self.dogtag_constants.PKI_INSTANCE_NAME):
+ installutils.set_directive(
+ config,
+ 'authz.instance.DirAclAuthz.ldap.ldapauth.authtype',
+ 'SslClientAuth', quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'authz.instance.DirAclAuthz.ldap.ldapauth.bindDN',
+ 'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname',
+ 'subsystemCert cert-pki-ca', quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'authz.instance.DirAclAuthz.ldap.ldapconn.port',
+ str(dogtag.install_constants.DS_SECURE_PORT),
+ quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'authz.instance.DirAclAuthz.ldap.ldapconn.secureConn',
+ 'true', quotes=False, separator='=')
+
+ installutils.set_directive(
+ config,
+ 'internaldb.ldapauth.authtype',
+ 'SslClientAuth', quotes=False, separator='=')
+
+ installutils.set_directive(
+ config,
+ 'internaldb.ldapauth.bindDN',
+ 'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'internaldb.ldapauth.clientCertNickname',
+ 'subsystemCert cert-pki-ca', quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'internaldb.ldapconn.port',
+ str(dogtag.install_constants.DS_SECURE_PORT),
+ quotes=False, separator='=')
+ installutils.set_directive(
+ config,
+ 'internaldb.ldapconn.secureConn', 'true', quotes=False,
+ separator='=')
+
+ def uninstall(self):
+ if self.is_installed():
+ self.print_msg("Unconfiguring %s" % self.subsystem)
+
+ try:
+ ipautil.run([paths.PKIDESTROY, "-i",
+ self.dogtag_constants.PKI_INSTANCE_NAME,
+ "-s", self.subsystem])
+ except ipautil.CalledProcessError, e:
+ self.log.critical("failed to uninstall %s instance %s",
+ self.subsystem, e)
+
+ def http_proxy(self):
+ """ Update the http proxy file """
+ template_filename = ipautil.SHARE_DIR + "ipa-pki-proxy.conf"
+ sub_dict = dict(
+ DOGTAG_PORT=self.dogtag_constants.AJP_PORT,
+ CLONE='' if self.clone else '#',
+ FQDN=self.fqdn,
+ )
+ template = ipautil.template_file(template_filename, sub_dict)
+ with open(paths.HTTPD_IPA_PKI_PROXY_CONF, "w") as fd:
+ fd.write(template)
+
+ def __get_pin(self):
+ try:
+ return certmonger.get_pin('internal',
+ dogtag_constants=self.dogtag_constants)
+ except IOError, e:
+ self.log.debug(
+ 'Unable to determine PIN for the Dogtag instance: %s', e)
+ raise RuntimeError(e)
+
+ def configure_renewal(self, reqs=None):
+ """ Configure certmonger to renew system certs
+
+ @param reqs: list of nicknames and profiles
+ """
+ cmonger = services.knownservices.certmonger
+ cmonger.enable()
+ services.knownservices.messagebus.start()
+ cmonger.start()
+
+ pin = self.__get_pin()
+
+ if reqs is None:
+ reqs = self.tracking_reqs
+
+ for nickname, profile in reqs:
+ try:
+ certmonger.dogtag_start_tracking(
+ ca='dogtag-ipa-ca-renew-agent',
+ nickname=nickname,
+ pin=pin,
+ pinfile=None,
+ secdir=self.dogtag_constants.ALIAS_DIR,
+ pre_command='stop_pkicad',
+ post_command='renew_ca_cert "%s"' % nickname,
+ profile=profile)
+ except (ipautil.CalledProcessError, RuntimeError), e:
+ self.log.error(
+ "certmonger failed to start tracking certificate: %s", e)
+
+ def stop_tracking_certificates(self, dogtag_constants, reqs=None):
+ """Stop tracking our certificates. Called on uninstall.
+ """
+ cmonger = services.knownservices.certmonger
+ services.knownservices.messagebus.start()
+ cmonger.start()
+
+ if reqs is None:
+ reqs = self.tracking_reqs
+
+ for nickname, _profile in reqs:
+ try:
+ certmonger.stop_tracking(
+ dogtag_constants.ALIAS_DIR, nickname=nickname)
+ except (ipautil.CalledProcessError, RuntimeError), e:
+ self.log.error(
+ "certmonger failed to stop tracking certificate: %s", e)
+
+ cmonger.stop()
+
+ @staticmethod
+ def update_cert_cs_cfg(nickname, cert, directives, cs_cfg,
+ dogtag_constants=None):
+ """
+ When renewing a Dogtag subsystem certificate the configuration file
+ needs to get the new certificate as well.
+
+ nickname is one of the known nicknames.
+ cert is a DER-encoded certificate.
+ directives is the list of directives to be updated for the subsystem
+ cs_cfg is the path to the CS.cfg file
+ """
+
+ if dogtag_constants is None:
+ dogtag_constants = dogtag.configured_constants()
+
+ with stopped_service(dogtag_constants.SERVICE_NAME,
+ instance_name=dogtag_constants.PKI_INSTANCE_NAME):
+ installutils.set_directive(
+ cs_cfg,
+ directives[nickname],
+ base64.b64encode(cert),
+ quotes=False,
+ separator='=')
+
+ def get_admin_cert(self):
+ """
+ Get the certificate for the admin user by checking the ldap entry
+ for the user. There should be only one certificate per user.
+ """
+ self.log.debug('Trying to find the certificate for the admin user')
+ conn = None
+
+ try:
+ conn = ipaldap.IPAdmin(self.fqdn, self.ds_port)
+ conn.do_simple_bind(
+ DN(('cn', 'Directory Manager')),
+ self.dm_password)
+
+ entry_attrs = conn.get_entry(self.admin_user, ['usercertificate'])
+ admin_cert = entry_attrs.get('usercertificate')[0]
+
+ # TODO(edewata) Add check to warn if there is more than one cert.
+ finally:
+ if conn is not None:
+ conn.unbind()
+
+ return base64.b64encode(admin_cert)
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 242e04d99..1719df46d 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -25,7 +25,6 @@ import os
import re
import time
import tempfile
-import base64
import stat
import grp
@@ -38,7 +37,9 @@ import ldap
from ipaserver.install import ldapupdate
from ipaserver.install import replication
from ipaserver.install import sysupgrade
-from ipalib import errors, certstore
+from ipalib import api
+from ipalib import certstore
+from ipalib import errors
from ipaplatform.tasks import tasks
from ipalib.constants import CACERT
from ipapython.dn import DN
@@ -952,3 +953,85 @@ class DsInstance(service.Service):
pass
self.ldap_disconnect()
+
+ def find_subject_base(self):
+ """
+ Try to find the current value of certificate subject base.
+ 1) Look in sysupgrade first
+ 2) If no value is found there, look in DS (start DS if necessary)
+ 3) Last resort, look in the certmap.conf itself
+ 4) If all fails, log loudly and return None
+
+ Note that this method can only be executed AFTER the ipa server
+ is configured, the api is initialized elsewhere and
+ that a ticket already have been acquired.
+ """
+ root_logger.debug(
+ 'Trying to find certificate subject base in sysupgrade')
+ subject_base = sysupgrade.get_upgrade_state(
+ 'certmap.conf', 'subject_base')
+
+ if subject_base:
+ root_logger.debug(
+ 'Found certificate subject base in sysupgrade: %s',
+ subject_base)
+ return subject_base
+
+ root_logger.debug(
+ 'Unable to find certificate subject base in sysupgrade')
+ root_logger.debug(
+ 'Trying to find certificate subject base in DS')
+
+ ds_is_running = is_ds_running()
+ if not ds_is_running:
+ try:
+ self.start()
+ ds_is_running = True
+ except ipautil.CalledProcessError as e:
+ root_logger.error('Cannot start DS to find certificate '
+ 'subject base: %s', e)
+
+ if ds_is_running:
+ try:
+ api.Backend.ldap2.connect(autobind=True)
+ ret = api.Command['config_show']()
+ subject_base = str(
+ ret['result']['ipacertificatesubjectbase'][0])
+ root_logger.debug(
+ 'Found certificate subject base in DS: %s', subject_base)
+ except errors.PublicError, e:
+ root_logger.error('Cannot connect to DS to find certificate '
+ 'subject base: %s', e)
+ finally:
+ try:
+ api.Backend.ldap2.disconnect()
+ except Exception:
+ pass
+
+ if not subject_base:
+ root_logger.debug('Unable to find certificate subject base in DS')
+ root_logger.debug('Trying to find certificate subject base in '
+ 'certmap.conf')
+
+ certmap_dir = config_dirname(
+ realm_to_serverid(api.env.realm)
+ )
+ try:
+ with open(os.path.join(certmap_dir, 'certmap.conf')) as f:
+ for line in f:
+ if line.startswith('certmap ipaca'):
+ subject_base = line.strip().split(',')[-1]
+ root_logger.debug(
+ 'Found certificate subject base in certmap.conf: '
+ '%s', subject_base)
+
+ except IOError as e:
+ root_logger.error('Cannot open certmap.conf to find certificate '
+ 'subject base: %s', e.strerror)
+
+ if subject_base:
+ return subject_base
+
+ root_logger.debug('Unable to find certificate subject base in '
+ 'certmap.conf')
+ return None
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 6ad7106b5..dc98d7a51 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -35,9 +35,9 @@ from dns.exception import DNSException
import ldap
from nss.error import NSPRError
-from ipapython import ipautil, sysrestore, admintool, dogtag
+from ipapython import ipautil, sysrestore, admintool, dogtag, version
from ipapython.admintool import ScriptError
-from ipapython.ipa_log_manager import *
+from ipapython.ipa_log_manager import root_logger
from ipalib.util import validate_hostname
from ipapython import config
from ipalib import errors, x509
@@ -68,7 +68,7 @@ class HostnameLocalhost(HostLookupError):
pass
class ReplicaConfig:
- def __init__(self):
+ def __init__(self, top_dir=None):
self.realm_name = ""
self.domain_name = ""
self.master_host_name = ""
@@ -78,6 +78,7 @@ class ReplicaConfig:
self.subject_base = None
self.setup_ca = False
self.version = 0
+ self.top_dir = top_dir
subject_base = ipautil.dn_attribute_property('_subject_base')
@@ -174,7 +175,7 @@ def verify_fqdn(host_name, no_host_dns=False, local_hostname=True):
raise HostReverseLookupError("The host name %s does not match the reverse lookup %s" % (host_name, revname))
verified.add(address)
-def record_in_hosts(ip, host_name=None, file=paths.HOSTS):
+def record_in_hosts(ip, host_name=None, conf_file=paths.HOSTS):
"""
Search record in /etc/hosts - static table lookup for hostnames
@@ -184,9 +185,9 @@ def record_in_hosts(ip, host_name=None, file=paths.HOSTS):
:param ip: IP address
:param host_name: Optional hostname to search
- :param file: Optional path to the lookup table
+ :param conf_file: Optional path to the lookup table
"""
- hosts = open(file, 'r').readlines()
+ hosts = open(conf_file, 'r').readlines()
for line in hosts:
line = line.rstrip('\n')
fields = line.partition('#')[0].split()
@@ -206,13 +207,13 @@ def record_in_hosts(ip, host_name=None, file=paths.HOSTS):
return None
return (hosts_ip, names)
except IndexError:
- print "Warning: Erroneous line '%s' in %s" % (line, file)
+ print "Warning: Erroneous line '%s' in %s" % (line, conf_file)
continue
return None
-def add_record_to_hosts(ip, host_name, file=paths.HOSTS):
- hosts_fd = open(file, 'r+')
+def add_record_to_hosts(ip, host_name, conf_file=paths.HOSTS):
+ hosts_fd = open(conf_file, 'r+')
hosts_fd.seek(0, 2)
hosts_fd.write(ip+'\t'+host_name+' '+host_name.split('.')[0]+'\n')
hosts_fd.close()
@@ -512,20 +513,20 @@ def expand_replica_info(filename, password):
"""
top_dir = tempfile.mkdtemp("ipa")
tarfile = top_dir+"/files.tar"
- dir = top_dir + "/realm_info"
+ dir_path = 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
+ return top_dir, dir_path
-def read_replica_info(dir, rconfig):
+def read_replica_info(dir_path, rconfig):
"""
Read the contents of a replica installation file.
rconfig is a ReplicaConfig object
"""
- filename = dir + "/realm_info"
+ filename = dir_path + "/realm_info"
fd = open(filename)
config = SafeConfigParser()
config.readfp(fd)
@@ -556,6 +557,67 @@ def read_replica_info_dogtag_port(config_dir):
return dogtag_master_ds_port
+def read_replica_info_kra_enabled(config_dir):
+ """
+ Check the replica info to determine if a KRA has been installed
+ on the master
+ """
+ default_file = config_dir + "/default.conf"
+ if not ipautil.file_exists(default_file):
+ return False
+ else:
+ with open(default_file) as fd:
+ config = SafeConfigParser()
+ config.readfp(fd)
+
+ enable_kra = bool(config.get("global", "enable_kra"))
+ return enable_kra
+
+
+def create_replica_config(dirman_password, filename, options):
+ top_dir = None
+ try:
+ top_dir, dir = expand_replica_info(filename, dirman_password)
+ except Exception, e:
+ root_logger.error("Failed to decrypt or open the replica file.")
+ print "ERROR: Failed to decrypt or open the replica file."
+ print "Verify you entered the correct Directory Manager password."
+ sys.exit(1)
+ config = ReplicaConfig(top_dir)
+ read_replica_info(dir, config)
+ root_logger.debug(
+ 'Installing replica file with version %d (0 means no version in prepared file).',
+ config.version)
+ if config.version and config.version > version.NUM_VERSION:
+ root_logger.error(
+ 'A replica file from a newer release (%d) cannot be installed on an older version (%d)',
+ config.version, version.NUM_VERSION)
+ sys.exit(1)
+ config.dirman_password = dirman_password
+ try:
+ host = get_host_name(options.no_host_dns)
+ except BadHostError, e:
+ root_logger.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)
+ if not ipautil.user_input("This may cause problems. Continue?", False):
+ root_logger.debug(
+ "Replica was created for %s but machine is named %s "
+ "User chose to exit",
+ config.host_name, host)
+ sys.exit(0)
+ config.host_name = host
+ print ""
+ except KeyboardInterrupt:
+ root_logger.debug("Keyboard Interrupt")
+ sys.exit(0)
+ config.dir = dir
+ config.ca_ds_port = read_replica_info_dogtag_port(config.dir)
+ return config
+
+
def check_server_configuration():
"""
Check if IPA server is configured on the system.
@@ -572,6 +634,7 @@ def check_server_configuration():
if not server_fstore.has_files():
raise RuntimeError("IPA is not configured on this system.")
+
def remove_file(filename):
"""
Remove a file and log any exceptions raised.
@@ -582,6 +645,7 @@ def remove_file(filename):
except Exception, e:
root_logger.error('Error removing %s: %s' % (filename, str(e)))
+
def rmtree(path):
"""
Remove a directory structure and log any exceptions raised.
@@ -592,6 +656,7 @@ def rmtree(path):
except Exception, e:
root_logger.error('Error removing %s: %s' % (path, str(e)))
+
def is_ipa_configured():
"""
Using the state and index install files determine if IPA is already
@@ -764,7 +829,7 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
raise ScriptError(
'%s server certificates found in %s, expecting only one' %
(len(server_certs), pkcs12_filename))
- [(server_cert_name, server_cert_trust)] = server_certs
+ [(server_cert_name, _server_cert_trust)] = server_certs
# Check we have the whole cert chain & the CA is in it
trust_chain = nssdb.get_trust_chain(server_cert_name)
@@ -849,23 +914,23 @@ def stopped_service(service, instance_name=""):
root_logger.debug('Starting %s%s.', service, log_instance_name)
services.knownservices[service].start(instance_name)
+
def check_entropy():
- '''
+ """
Checks if the system has enough entropy, if not, displays warning message
- '''
+ """
try:
- with open('/proc/sys/kernel/random/entropy_avail', 'r') as efname:
+ with open(paths.ENTROPY_AVAIL, 'r') as efname:
if int(efname.read()) < 200:
emsg = 'WARNING: Your system is running out of entropy, ' \
'you may experience long delays'
service.print_msg(emsg)
root_logger.debug(emsg)
except IOError as e:
- root_logger.debug("Could not open /proc/sys/kernel/random/entropy_avail: %s" % \
- e)
+ root_logger.debug(
+ "Could not open %s: %s", paths.ENTROPY_AVAIL, e)
except ValueError as e:
- root_logger.debug("Invalid value in /proc/sys/kernel/random/entropy_avail %s" % \
- e)
+ root_logger.debug("Invalid value in %s %s", paths.ENTROPY_AVAIL, e)
def validate_external_cert(cert_file, ca_file, subject_base):
extcert = None
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 8f27e8a60..0830eb0c5 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -157,7 +157,7 @@ class Backup(admintool.AdminTool):
paths.NTP_CONF,
paths.SMB_CONF,
paths.SAMBA_KEYTAB,
- paths.CA_AGENT_P12,
+ paths.DOGTAG_AGENT_P12,
paths.CACERT_P12,
paths.KRB5KDC_KDC_CONF,
paths.SYSTEMD_IPA_SERVICE,
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
new file mode 100644
index 000000000..2c4f2dcaa
--- /dev/null
+++ b/ipaserver/install/ipa_kra_install.py
@@ -0,0 +1,243 @@
+#! /usr/bin/python2 -E
+# Authors: Ade Lee <alee@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from ConfigParser import RawConfigParser
+from textwrap import dedent
+from ipalib import api
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipapython import admintool
+from ipapython import dogtag
+from ipapython import ipautil
+from ipaserver.install import cainstance
+from ipaserver.install import dogtaginstance
+from ipaserver.install import krainstance
+from ipaserver.install import dsinstance
+from ipaserver.install import installutils
+from ipaserver.install import service
+from ipaserver.install.installutils import (
+ read_replica_info_kra_enabled, create_replica_config)
+
+
+class KRAInstall(admintool.AdminTool):
+
+ command_name = 'ipa-kra-install'
+
+ usage = "%prog [options] [replica_file]"
+
+ description = "Install a master or replica KRA."
+
+ @classmethod
+ def add_options(cls, parser, debug_option=True):
+ super(KRAInstall, cls).add_options(parser, debug_option=True)
+
+ parser.add_option(
+ "-p", "--password",
+ dest="password", sensitive=True,
+ help="Directory Manager (existing master) password")
+
+ parser.add_option(
+ "-U", "--unattended",
+ dest="unattended", action="store_true", default=False,
+ help="unattended installation never prompts the user")
+
+ parser.add_option(
+ "--uninstall",
+ dest="uninstall", action="store_true", default=False,
+ help="uninstall an existing installation. The uninstall can "
+ "be run with --unattended option")
+
+ def validate_options(self, needs_root=True):
+ super(KRAInstall, self).validate_options(needs_root=True)
+
+ installutils.check_server_configuration()
+
+ if self.options.unattended and self.options.password is None:
+ self.option_parser.error(
+ "Directory Manager password must be specified using -p"
+ " in unattended mode"
+ )
+
+ api.bootstrap(in_server=True)
+ api.finalize()
+
+ def ask_for_options(self):
+ super(KRAInstall, self).ask_for_options()
+
+ if not self.options.password:
+ self.options.password = installutils.read_password(
+ "Directory Manager", confirm=False,
+ validate=False, retry=False)
+ if self.options.password is None:
+ raise admintool.ScriptError(
+ "Directory Manager password required")
+
+ @classmethod
+ def get_command_class(cls, options, args):
+ if options.uninstall:
+ return KRAUninstaller
+ else:
+ return KRAInstaller
+
+
+class KRAUninstaller(KRAInstall):
+ log_file_name = paths.PKI_KRA_UNINSTALL_LOG
+
+ def validate_options(self, needs_root=True):
+ super(KRAUninstaller, self).validate_options(needs_root=True)
+
+ if self.args:
+ self.option_parser.error("Too many parameters provided.")
+
+ if not api.env.enable_kra:
+ self.option_parser.error(
+ "Cannot uninstall. There is no KRA installed on this system."
+ )
+
+ def run(self):
+ super(KRAUninstaller, self).run()
+ dogtag_constants = dogtag.configured_constants()
+
+ # temporarily disable uninstall until Dogtag ticket:
+ # https://fedorahosted.org/pki/ticket/1113 is fixed
+ # TODO(alee) remove this once the above ticket is fixed
+ raise admintool.ScriptError(
+ "Uninstall is temporarily disabled. To uninstall, please "
+ "use ipa-server-install --uninstall"
+ )
+
+ kra_instance = krainstance.KRAInstance(
+ api.env.realm, dogtag_constants=dogtag_constants)
+ kra_instance.stop_tracking_certificates(dogtag_constants)
+ if kra_instance.is_installed():
+ kra_instance.uninstall()
+
+ # Update config file
+ parser = RawConfigParser()
+ parser.read(paths.IPA_DEFAULT_CONF)
+ parser.set('global', 'enable_kra', 'False')
+
+ with open(paths.IPA_DEFAULT_CONF, 'w') as f:
+ parser.write(f)
+
+
+class KRAInstaller(KRAInstall):
+ log_file_name = paths.PKI_KRA_INSTALL_LOG
+
+ INSTALLER_START_MESSAGE = '''
+ ===================================================================
+ This program will setup Dogtag KRA for the FreeIPA Server.
+
+ '''
+
+ FAIL_MESSAGE = '''
+ Your system may be partly configured.
+ Run ipa-kra-install --uninstall to clean up.
+ '''
+
+ def validate_options(self, needs_root=True):
+ super(KRAInstaller, self).validate_options(needs_root=True)
+
+ dogtag_version = int(api.env.dogtag_version)
+ enable_kra = api.env.enable_kra
+
+ if enable_kra:
+ self.option_parser.error("KRA is already installed.")
+
+ ca_installed = cainstance.is_ca_installed_locally()
+
+ if ca_installed:
+ if dogtag_version >= 10:
+ # correct dogtag version of CA installed
+ pass
+ else:
+ self.option_parser.error(
+ "Dogtag must be version 10.2 or above to install KRA")
+ else:
+ self.option_parser.error(
+ "Dogtag CA is not installed. Please install the CA first")
+
+ self.installing_replica = dogtaginstance.is_installing_replica("KRA")
+ if self.installing_replica:
+ if not self.args:
+ self.option_parser.error("A replica file is required.")
+ if len(self.args) > 1:
+ self.option_parser.error("Too many arguments provided")
+
+ self.replica_file = self.args[0]
+ if not ipautil.file_exists(self.replica_file):
+ self.option_parser.error(
+ "Replica file %s does not exist" % self.replica_file)
+ else:
+ if self.args:
+ self.option_parser.error("Too many parameters provided. "
+ "No replica file is required.")
+
+ def _run(self):
+ super(KRAInstaller, self).run()
+ print dedent(self.INSTALLER_START_MESSAGE)
+
+ subject = dsinstance.DsInstance().find_subject_base()
+ if not self.installing_replica:
+ kra = krainstance.KRAInstance(
+ api.env.realm,
+ dogtag_constants=dogtag.install_constants)
+
+ kra.configure_instance(
+ api.env.host, api.env.domain, self.options.password,
+ self.options.password, subject_base=subject)
+ else:
+ replica_config = create_replica_config(
+ self.options.password,
+ self.replica_file,
+ self.options)
+
+ if not read_replica_info_kra_enabled(replica_config.dir):
+ raise admintool.ScriptError(
+ "Either KRA is not installed on the master system or "
+ "your replica file is out of date"
+ )
+
+ kra = krainstance.install_replica_kra(replica_config)
+ service.print_msg("Restarting the directory server")
+
+ ds = dsinstance.DsInstance()
+ ds.restart()
+
+ kra.enable_client_auth_to_db(kra.dogtag_constants.KRA_CS_CFG_PATH)
+
+ # Restart apache for new proxy config file
+ services.knownservices.httpd.restart(capture_output=True)
+
+ # Update config file
+ parser = RawConfigParser()
+ parser.read(paths.IPA_DEFAULT_CONF)
+ parser.set('global', 'enable_kra', 'True')
+
+ with open(paths.IPA_DEFAULT_CONF, 'w') as f:
+ parser.write(f)
+
+ def run(self):
+ try:
+ self._run()
+ except:
+ self.log.error(dedent(self.FAIL_MESSAGE))
+ raise
+
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index 81b54211f..1099046dd 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -371,6 +371,7 @@ class ReplicaPrepare(admintool.AdminTool):
cacert_filename = paths.CACERT_PEM
if ipautil.file_exists(cacert_filename):
self.copy_info_file(cacert_filename, "cacert.pem")
+ self.copy_info_file(paths.IPA_DEFAULT_CONF, "default.conf")
def save_config(self):
self.log.info("Finalizing configuration")
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
new file mode 100644
index 000000000..182e8e034
--- /dev/null
+++ b/ipaserver/install/krainstance.py
@@ -0,0 +1,346 @@
+# Authors: Ade Lee <alee@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import ConfigParser
+import os
+import pwd
+import shutil
+import sys
+import tempfile
+
+from ipalib import api
+from ipaplatform import services
+from ipaplatform.paths import paths
+from ipapython import dogtag
+from ipapython import ipaldap
+from ipapython import ipautil
+from ipapython.dn import DN
+from ipaserver.install import certs
+from ipaserver.install import cainstance
+from ipaserver.install import service
+from ipaserver.install.dogtaginstance import DogtagInstance
+from ipaserver.install.dogtaginstance import DEFAULT_DSPORT, PKI_USER
+from ipapython.ipa_log_manager import log_mgr
+
+# When IPA is installed with DNS support, this CNAME should hold all IPA
+# replicas with KRA configured
+IPA_KRA_RECORD = "ipa-kra"
+
+
+class KRAInstance(DogtagInstance):
+ """
+ We assume that the CA has already been installed, and we use the
+ same tomcat instance to host both the CA and KRA.
+ The mod_nss database will contain the RA agent cert that will be used
+ to do authenticated requests against dogtag. The RA agent cert will
+ be the same for both the CA and KRA.
+ """
+
+ def __init__(self, realm, dogtag_constants=None):
+ if dogtag_constants is None:
+ dogtag_constants = dogtag.configured_constants()
+
+ super(KRAInstance, self).__init__(
+ realm=realm,
+ subsystem="KRA",
+ service_desc="KRA server",
+ dogtag_constants=dogtag_constants
+ )
+
+ self.basedn = DN(('o', 'kra'), ('o', 'ipaca'))
+ self.tracking_reqs = (('auditSigningCert cert-pki-kra', None),
+ ('transportCert cert-pki-kra', None),
+ ('storageCert cert-pki-kra', None))
+ self.log = log_mgr.get_logger(self)
+
+ def configure_instance(self, host_name, domain, dm_password,
+ admin_password, ds_port=DEFAULT_DSPORT,
+ pkcs12_info=None, master_host=None,
+ master_replication_port=None,
+ subject_base=None):
+ """Create a KRA instance.
+
+ To create a clone, pass in pkcs12_info.
+ """
+ self.fqdn = host_name
+ self.domain = domain
+ self.dm_password = dm_password
+ self.admin_password = admin_password
+ self.ds_port = ds_port
+ self.pkcs12_info = pkcs12_info
+ if self.pkcs12_info is not None:
+ self.clone = True
+ self.master_host = master_host
+ self.master_replication_port = master_replication_port
+ if subject_base is None:
+ self.subject_base = DN(('O', self.realm))
+ else:
+ self.subject_base = subject_base
+
+ # Confirm that a KRA does not already exist
+ if self.is_installed():
+ raise RuntimeError(
+ "KRA already installed.")
+ # Confirm that a Dogtag 10 CA instance already exists
+ ca = cainstance.CAInstance(
+ api.env.realm, certs.NSS_DIR,
+ dogtag_constants=dogtag.Dogtag10Constants)
+ if not ca.is_installed():
+ raise RuntimeError(
+ "KRA configuration failed. "
+ "A Dogtag CA must be installed first")
+
+ self.step("configuring KRA instance", self.__spawn_instance)
+ if not self.clone:
+ self.step("add RA user to KRA agent group",
+ self.__add_ra_user_to_agent_group)
+ self.step("restarting KRA", self.restart_instance)
+ self.step("configure certificate renewals", self.configure_renewal)
+ self.step("Configure HTTP to proxy connections",
+ self.http_proxy)
+
+ self.start_creation(runtime=126)
+
+ def __spawn_instance(self):
+ """
+ Create and configure a new KRA instance using pkispawn.
+ Creates a configuration file with IPA-specific
+ parameters and passes it to the base class to call pkispawn
+ """
+
+ # Create an empty and secured file
+ (cfg_fd, cfg_file) = tempfile.mkstemp()
+ os.close(cfg_fd)
+ pent = pwd.getpwnam(PKI_USER)
+ os.chown(cfg_file, pent.pw_uid, pent.pw_gid)
+
+ # Create KRA configuration
+ config = ConfigParser.ConfigParser()
+ config.optionxform = str
+ config.add_section("KRA")
+
+ # Security Domain Authentication
+ config.set("KRA", "pki_security_domain_https_port", "443")
+ config.set("KRA", "pki_security_domain_password", self.admin_password)
+ config.set("KRA", "pki_security_domain_user", "admin")
+
+ # issuing ca
+ config.set("KRA", "pki_issuing_ca_uri", "https://%s" %
+ ipautil.format_netloc(self.fqdn, 443))
+
+ # Server
+ config.set("KRA", "pki_enable_proxy", "True")
+ config.set("KRA", "pki_restart_configured_instance", "False")
+ config.set("KRA", "pki_backup_keys", "True")
+ config.set("KRA", "pki_backup_password", self.admin_password)
+
+ # Client security database
+ config.set("KRA", "pki_client_database_dir", self.agent_db)
+ config.set("KRA", "pki_client_database_password", self.admin_password)
+ config.set("KRA", "pki_client_database_purge", "False")
+ config.set("KRA", "pki_client_pkcs12_password", self.admin_password)
+
+ # Administrator
+ config.set("KRA", "pki_admin_name", "admin")
+ config.set("KRA", "pki_admin_uid", "admin")
+ config.set("KRA", "pki_admin_email", "root@localhost")
+ config.set("KRA", "pki_admin_password", self.admin_password)
+ config.set("KRA", "pki_admin_nickname", "ipa-ca-agent")
+ config.set("KRA", "pki_admin_subject_dn",
+ str(DN(('cn', 'ipa-ca-agent'), self.subject_base)))
+ config.set("KRA", "pki_import_admin_cert", "True")
+ config.set("KRA", "pki_admin_cert_file", paths.ADMIN_CERT_PATH)
+ config.set("KRA", "pki_client_admin_cert_p12", paths.DOGTAG_AGENT_P12)
+
+ # Directory server
+ config.set("KRA", "pki_ds_ldap_port", str(self.ds_port))
+ config.set("KRA", "pki_ds_password", self.dm_password)
+ config.set("KRA", "pki_ds_base_dn", self.basedn)
+ config.set("KRA", "pki_ds_database", "ipaca")
+ config.set("KRA", "pki_ds_create_new_db", "False")
+
+ # Certificate subject DNs
+ config.set("KRA", "pki_subsystem_subject_dn",
+ str(DN(('cn', 'CA Subsystem'), self.subject_base)))
+ config.set("KRA", "pki_ssl_server_subject_dn",
+ str(DN(('cn', self.fqdn), self.subject_base)))
+ config.set("KRA", "pki_audit_signing_subject_dn",
+ str(DN(('cn', 'KRA Audit'), self.subject_base)))
+ config.set(
+ "KRA", "pki_transport_subject_dn",
+ str(DN(('cn', 'KRA Transport Certificate'), self.subject_base)))
+ config.set(
+ "KRA", "pki_storage_subject_dn",
+ str(DN(('cn', 'KRA Storage Certificate'), self.subject_base)))
+
+ # Certificate nicknames
+ # Note that both the server certs and subsystem certs reuse
+ # the ca certs.
+ config.set("KRA", "pki_subsystem_nickname",
+ "subsystemCert cert-pki-ca")
+ config.set("KRA", "pki_ssl_server_nickname",
+ "Server-Cert cert-pki-ca")
+ config.set("KRA", "pki_audit_signing_nickname",
+ "auditSigningCert cert-pki-kra")
+ config.set("KRA", "pki_transport_nickname",
+ "transportCert cert-pki-kra")
+ config.set("KRA", "pki_storage_nickname",
+ "storageCert cert-pki-kra")
+
+ # Shared db settings
+ # Needed because CA and KRA share the same database
+ # We will use the dbuser created for the CA
+ config.set("KRA", "pki_share_db", "True")
+ config.set(
+ "KRA", "pki_share_dbuser_dn",
+ str(DN(('uid', 'pkidbuser'), ('ou', 'people'), ('o', 'ipaca'))))
+
+ _p12_tmpfile_handle, p12_tmpfile_name = tempfile.mkstemp(dir=paths.TMP)
+ if self.clone:
+ krafile = self.pkcs12_info[0]
+ shutil.copy(krafile, p12_tmpfile_name)
+ pent = pwd.getpwnam(PKI_USER)
+ os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid)
+
+ # create admin cert file if it does not exist
+ cert = DogtagInstance.get_admin_cert(self)
+ with open(paths.ADMIN_CERT_PATH, "w") as admin_path:
+ admin_path.write(cert)
+
+ # Security domain registration
+ config.set("KRA", "pki_security_domain_hostname", self.master_host)
+ config.set("KRA", "pki_security_domain_https_port", "443")
+ config.set("KRA", "pki_security_domain_user", "admin")
+ config.set("KRA", "pki_security_domain_password",
+ self.admin_password)
+
+ # Clone
+ config.set("KRA", "pki_clone", "True")
+ config.set("KRA", "pki_clone_pkcs12_path", p12_tmpfile_name)
+ config.set("KRA", "pki_clone_pkcs12_password", self.dm_password)
+ config.set("KRA", "pki_clone_setup_replication", "False")
+ config.set(
+ "KRA", "pki_clone_uri",
+ "https://%s" % ipautil.format_netloc(self.master_host, 443))
+
+ # Generate configuration file
+ with open(cfg_file, "wb") as f:
+ config.write(f)
+
+ try:
+ DogtagInstance.spawn_instance(self, cfg_file)
+ finally:
+ os.remove(p12_tmpfile_name)
+ os.remove(cfg_file)
+
+ shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12)
+ self.log.debug("completed creating KRA instance")
+
+ def __add_ra_user_to_agent_group(self):
+ """
+ Add RA agent created for CA to KRA agent group.
+ """
+ conn = ipaldap.IPAdmin(self.fqdn, self.ds_port)
+ conn.do_simple_bind(DN(('cn', 'Directory Manager')), self.dm_password)
+
+ entry_dn = DN(('uid', "ipara"), ('ou', 'People'), ('o', 'ipaca'))
+ dn = DN(('cn', 'Data Recovery Manager Agents'), ('ou', 'groups'),
+ self.basedn)
+ modlist = [(0, 'uniqueMember', '%s' % entry_dn)]
+ conn.modify_s(dn, modlist)
+
+ conn.unbind()
+
+ @staticmethod
+ def update_cert_config(nickname, cert, dogtag_constants=None):
+ """
+ When renewing a KRA subsystem certificate the configuration file
+ needs to get the new certificate as well.
+
+ nickname is one of the known nicknames.
+ cert is a DER-encoded certificate.
+ """
+
+ if dogtag_constants is None:
+ dogtag_constants = dogtag.configured_constants()
+
+ # The cert directive to update per nickname
+ directives = {
+ 'auditSigningCert cert-pki-kra': 'kra.audit_signing.cert',
+ 'storageCert cert-pki-kra': 'kra.storage.cert',
+ 'transportCert cert-pki-kra': 'kra.transport.cert',
+ 'subsystemCert cert-pki-kra': 'kra.subsystem.cert',
+ 'Server-Cert cert-pki-ca': 'kra.sslserver.cert'}
+
+ DogtagInstance.update_cert_cs_cfg(
+ nickname, cert, directives,
+ dogtag.configured_constants().KRA_CS_CFG_PATH,
+ dogtag_constants)
+
+
+def install_replica_kra(config, postinstall=False):
+ """
+ Install a KRA on a replica.
+
+ There are two modes of doing this controlled:
+ - While the replica is being installed
+ - Post-replica installation
+
+ config is a ReplicaConfig object
+
+ Returns a KRA instance
+ """
+ # note that the cacert.p12 file is regenerated during the
+ # ipa-replica-prepare process and should include all the certs
+ # for the CA and KRA
+ krafile = config.dir + "/cacert.p12"
+
+ if not ipautil.file_exists(krafile):
+ raise RuntimeError(
+ "Unable to clone KRA."
+ " cacert.p12 file not found in replica file")
+
+ _kra = KRAInstance(config.realm_name,
+ dogtag_constants=dogtag.install_constants)
+ _kra.dm_password = config.dirman_password
+ _kra.subject_base = config.subject_base
+ if _kra.is_installed():
+ sys.exit("A KRA is already configured on this system.")
+
+ _kra.configure_instance(config.host_name, config.domain_name,
+ config.dirman_password, config.dirman_password,
+ pkcs12_info=(krafile,),
+ master_host=config.master_host_name,
+ master_replication_port=config.ca_ds_port,
+ subject_base=config.subject_base)
+
+ # Restart httpd since we changed it's config and added ipa-pki-proxy.conf
+ if postinstall:
+ services.knownservices.httpd.restart()
+
+ # The dogtag DS instance needs to be restarted after installation.
+ # The procedure for this is: stop dogtag, stop DS, start DS, start
+ # dogtag
+
+ service.print_msg("Restarting the directory and KRA servers")
+ _kra.stop(dogtag.install_constants.PKI_INSTANCE_NAME)
+ services.knownservices.dirsrv.restart()
+ _kra.start(dogtag.install_constants.PKI_INSTANCE_NAME)
+
+ return _kra