ipa-certupdate: track lightweight CA certificates

Enhance the ipa-certupdate program to add Certmonger tracking
requests for lightweight CA certificates.

Also update the dogtag-ipa-ca-renew-agent-submit to not store or
retrieve lightweight CA certificates, becaues Dogtag clones observe
renewals and update their NSSDBs on their own, and allow the helper
to request non-self-signed certificates.

Part of: https://fedorahosted.org/freeipa/ticket/4559

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
Fraser Tweedale 2016-06-21 15:01:41 +10:00 committed by Jan Cholasta
parent b720aa94e9
commit 0078e7a919
2 changed files with 82 additions and 9 deletions

View File

@ -62,6 +62,24 @@ if six.PY3:
unicode = str
IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
def get_nickname():
csr = os.environ.get('CERTMONGER_CSR')
return pkcs10.get_friendlyname(csr) if csr else None
def is_lightweight_ca():
nickname = get_nickname() or ''
return nickname != IPA_CA_NICKNAME and nickname.startswith(IPA_CA_NICKNAME)
def is_renewable():
cert = os.environ.get('CERTMONGER_CERTIFICATE')
if not cert:
return False
else:
return x509.is_self_signed(cert) or is_lightweight_ca()
@contextlib.contextmanager
def ldap_connect():
conn = None
@ -210,6 +228,11 @@ def store_cert():
if not cert:
return (REJECTED, "New certificate requests not supported")
if is_lightweight_ca():
# Lightweight CAs are updated in Dogtag's NSSDB
# by Dogtag itself, so do not store it
return (ISSUED, cert)
dercert = x509.normalize_certificate(cert)
dn = DN(('cn', nickname), ('cn', 'ca_renewal'),
@ -338,6 +361,12 @@ def retrieve_cert_continuous():
if old_cert:
old_cert = x509.normalize_certificate(old_cert)
if is_lightweight_ca():
# Lightweight CAs are updated in Dogtag's NSSDB
# by Dogtag itself, so do not try to retrieve it.
# Everything is fine as is.
return (ISSUED, os.environ.get('CERTMONGER_CERTIFICATE'))
result = call_handler(retrieve_or_reuse_cert)
if result[0] != ISSUED:
return result
@ -393,13 +422,12 @@ def renew_ca_cert():
cert = os.environ.get('CERTMONGER_CERTIFICATE')
if not cert:
return (REJECTED, "New certificate requests not supported")
is_self_signed = x509.is_self_signed(cert)
operation = os.environ.get('CERTMONGER_OPERATION')
if operation == 'SUBMIT':
state = 'retrieve'
if is_self_signed:
if is_renewable():
ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
if ca.is_renewal_master():
state = 'request'
@ -419,10 +447,11 @@ def renew_ca_cert():
if state == 'retrieve':
result = call_handler(retrieve_cert)
if result[0] == REJECTED and not is_self_signed:
if result[0] == REJECTED and not is_renewable():
syslog.syslog(syslog.LOG_ALERT,
"IPA CA certificate is about to expire, "
"use ipa-cacert-manage to renew it")
"Certificate with subject '%s' is about to expire, "
"use ipa-cacert-manage to renew it"
% (os.environ.get("CERTMONGER_REQ_SUBJECT"),))
elif state == 'request':
profile = os.environ['CERTMONGER_CA_PROFILE']
os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert'

View File

@ -29,7 +29,10 @@ from ipaplatform import services
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
from ipalib import api, errors, x509, certstore
from ipalib.constants import IPA_CA_CN
IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
RENEWAL_CA_NAME = 'dogtag-ipa-ca-renew-agent'
class CertUpdate(admintool.AdminTool):
command_name = 'ipa-certupdate'
@ -76,18 +79,27 @@ class CertUpdate(admintool.AdminTool):
version=u'2.0',
)
ca_enabled = result['result']['enable_ra']
api.Backend.rpcclient.disconnect()
ldap.do_sasl_gssapi_bind()
certs = certstore.get_ca_certs(ldap, api.env.basedn,
api.env.realm, ca_enabled)
# find lightweight CAs (on renewal master only)
lwcas = []
for ca_obj in api.Command.ca_find()['result']:
if IPA_CA_CN not in ca_obj['cn']:
lwcas.append(ca_obj)
api.Backend.rpcclient.disconnect()
finally:
shutil.rmtree(tmpdir)
server_fstore = sysrestore.FileStore(paths.SYSRESTORE)
if server_fstore.has_files():
self.update_server(certs)
for entry in lwcas:
self.server_track_lightweight_ca(entry)
self.update_client(certs)
@ -122,11 +134,10 @@ class CertUpdate(admintool.AdminTool):
if services.knownservices.httpd.is_running():
services.knownservices.httpd.restart()
nickname = 'caSigningCert cert-pki-ca'
criteria = {
'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
'cert-nickname': nickname,
'ca-name': 'dogtag-ipa-ca-renew-agent',
'cert-nickname': IPA_CA_NICKNAME,
'ca-name': RENEWAL_CA_NAME
}
request_id = certmonger.get_request_id(criteria)
if request_id is not None:
@ -152,6 +163,39 @@ class CertUpdate(admintool.AdminTool):
self.update_file(paths.CA_CRT, certs)
def server_track_lightweight_ca(self, entry):
nickname = "{} {}".format(IPA_CA_NICKNAME, entry['ipacaid'][0])
criteria = {
'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
'cert-nickname': nickname,
'ca-name': RENEWAL_CA_NAME,
}
request_id = certmonger.get_request_id(criteria)
if request_id is None:
try:
certmonger.dogtag_start_tracking(
secdir=paths.PKI_TOMCAT_ALIAS_DIR,
pin=certmonger.get_pin('internal'),
pinfile=None,
nickname=nickname,
ca=RENEWAL_CA_NAME,
pre_command='stop_pkicad',
post_command='renew_ca_cert "%s"' % nickname,
)
request_id = certmonger.get_request_id(criteria)
certmonger.modify(request_id, profile='ipaCACertRenewal')
self.log.debug(
'Lightweight CA renewal: '
'added tracking request for "%s"', nickname)
except RuntimeError as e:
self.log.error(
'Lightweight CA renewal: Certmonger failed to '
'start tracking certificate: %s', e)
else:
self.log.debug(
'Lightweight CA renewal: '
'already tracking certificate "%s"', nickname)
def update_file(self, filename, certs, mode=0o444):
certs = (c[0] for c in certs if c[2] is not False)
try: