mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Fix ipa-server-upgrade with server cert tracking
ipa-server-upgrade fails with Server-Cert not found, when trying to track httpd/ldap server certificates. There are 2 issues in the upgrade: - the certificates should be tracked only if they were issued by IPA CA (it is possible to have CA configured but 3rd part certs) - the certificate nickname can be different from Server-Cert The fix provides methods to find the server crt nickname for http and ldap, and a method to check if the server certs are issued by IPA and need to be tracked by certmonger. https://pagure.io/freeipa/issue/7141 Reviewed-By: Stanislav Laznicka <slaznick@redhat.com> Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
This commit is contained in:
parent
62e72c2a53
commit
87540fe1ef
@ -42,6 +42,7 @@ from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
|
||||
from ipapython.dn import DN
|
||||
from ipalib import pkcs10, x509, api
|
||||
from ipalib.errors import CertificateOperationError
|
||||
from ipalib.install import certstore
|
||||
from ipalib.text import _
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
@ -659,6 +660,31 @@ class CertDB(object):
|
||||
subject=host,
|
||||
passwd_fname=self.passwd_fname)
|
||||
|
||||
def is_ipa_issued_cert(self, api, nickname):
|
||||
"""
|
||||
Return True if the certificate contained in the CertDB with the
|
||||
provided nickname has been issued by IPA.
|
||||
|
||||
Note that this method can only be executed if api has been initialized
|
||||
"""
|
||||
# This method needs to compare the cert issuer (from the NSS DB
|
||||
# and the subject from the CA (from LDAP), because nicknames are not
|
||||
# always aligned.
|
||||
|
||||
cacert_subject = certstore.get_ca_subject(
|
||||
api.Backend.ldap2,
|
||||
api.env.container_ca,
|
||||
api.env.basedn)
|
||||
|
||||
# The cert can be issued directly by IPA. In this case, the cert
|
||||
# issuer is IPA CA subject.
|
||||
cert = self.get_cert_from_db(nickname)
|
||||
if cert is None:
|
||||
raise RuntimeError("Could not find the cert %s in %s"
|
||||
% (nickname, self.secdir))
|
||||
|
||||
return DN(cert.issuer) == cacert_subject
|
||||
|
||||
|
||||
class _CrossProcessLock(object):
|
||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||
|
@ -1033,22 +1033,59 @@ class DsInstance(service.Service):
|
||||
logger.error(
|
||||
'Unable to restart DS instance %s: %s', ds_instance, e)
|
||||
|
||||
def get_server_cert_nickname(self, serverid=None):
|
||||
"""
|
||||
Retrieve the nickname of the server cert used by dirsrv.
|
||||
|
||||
The method directly reads the dse.ldif to find the attribute
|
||||
nsSSLPersonalitySSL of cn=RSA,cn=encryption,cn=config because
|
||||
LDAP is not always accessible when we need to get the nickname
|
||||
(for instance during uninstall).
|
||||
"""
|
||||
if serverid is None:
|
||||
serverid = self.get_state("serverid")
|
||||
if serverid is not None:
|
||||
dirname = config_dirname(serverid)
|
||||
config_file = os.path.join(dirname, "dse.ldif")
|
||||
rsa_dn = "cn=RSA,cn=encryption,cn=config"
|
||||
with open(config_file, "r") as in_file:
|
||||
parser = upgradeinstance.GetEntryFromLDIF(
|
||||
in_file,
|
||||
entries_dn=[rsa_dn])
|
||||
parser.parse()
|
||||
try:
|
||||
config_entry = parser.get_results()[rsa_dn]
|
||||
nickname = config_entry["nsSSLPersonalitySSL"][0]
|
||||
return nickname.decode('utf-8')
|
||||
except (KeyError, IndexError):
|
||||
logger.error("Unable to find server cert nickname in %s",
|
||||
config_file)
|
||||
|
||||
logger.debug("Falling back to nickname Server-Cert")
|
||||
return 'Server-Cert'
|
||||
|
||||
def stop_tracking_certificates(self, serverid=None):
|
||||
if serverid is None:
|
||||
serverid = self.get_state("serverid")
|
||||
if not serverid is None:
|
||||
nickname = self.get_server_cert_nickname(serverid)
|
||||
# drop the trailing / off the config_dirname so the directory
|
||||
# will match what is in certmonger
|
||||
dirname = config_dirname(serverid)[:-1]
|
||||
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
||||
dsdb.untrack_server_cert(self.nickname)
|
||||
dsdb.untrack_server_cert(nickname)
|
||||
|
||||
def start_tracking_certificates(self, serverid):
|
||||
nickname = self.get_server_cert_nickname(serverid)
|
||||
dirname = config_dirname(serverid)[:-1]
|
||||
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
||||
dsdb.track_server_cert(self.nickname, self.principal,
|
||||
dsdb.passwd_fname,
|
||||
'restart_dirsrv %s' % serverid)
|
||||
if dsdb.is_ipa_issued_cert(api, nickname):
|
||||
dsdb.track_server_cert(nickname, self.principal,
|
||||
dsdb.passwd_fname,
|
||||
'restart_dirsrv %s' % serverid)
|
||||
else:
|
||||
logger.debug("Will not track DS server certificate %s as it is "
|
||||
"not issued by IPA", nickname)
|
||||
|
||||
# we could probably move this function into the service.Service
|
||||
# class - it's very generic - all we need is a way to get an
|
||||
|
@ -262,6 +262,11 @@ class HTTPInstance(service.Service):
|
||||
installutils.set_directive(
|
||||
paths.HTTPD_NSS_CONF, 'NSSNickname', quoted_nickname, quotes=False)
|
||||
|
||||
def get_mod_nss_nickname(self):
|
||||
cert = installutils.get_directive(paths.HTTPD_NSS_CONF, 'NSSNickname')
|
||||
nickname = installutils.unquote_directive_value(cert, quote_char="'")
|
||||
return nickname
|
||||
|
||||
def set_mod_nss_protocol(self):
|
||||
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSProtocol', 'TLSv1.0,TLSv1.1,TLSv1.2', False)
|
||||
|
||||
@ -578,12 +583,17 @@ class HTTPInstance(service.Service):
|
||||
|
||||
def stop_tracking_certificates(self):
|
||||
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
||||
db.untrack_server_cert(self.cert_nickname)
|
||||
db.untrack_server_cert(self.get_mod_nss_nickname())
|
||||
|
||||
def start_tracking_certificates(self):
|
||||
db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
||||
db.track_server_cert(self.cert_nickname, self.principal,
|
||||
db.passwd_fname, 'restart_httpd')
|
||||
nickname = self.get_mod_nss_nickname()
|
||||
if db.is_ipa_issued_cert(api, nickname):
|
||||
db.track_server_cert(nickname, self.principal,
|
||||
db.passwd_fname, 'restart_httpd')
|
||||
else:
|
||||
logger.debug("Will not track HTTP server cert %s as it is not "
|
||||
"issued by IPA", nickname)
|
||||
|
||||
def request_service_keytab(self):
|
||||
super(HTTPInstance, self).request_service_keytab()
|
||||
|
@ -972,13 +972,13 @@ def certificate_renewal_update(ca, ds, http):
|
||||
},
|
||||
{
|
||||
'cert-database': paths.HTTPD_ALIAS_DIR,
|
||||
'cert-nickname': 'Server-Cert',
|
||||
'cert-nickname': http.get_mod_nss_nickname(),
|
||||
'ca': 'IPA',
|
||||
'cert-postsave-command': template % 'restart_httpd',
|
||||
},
|
||||
{
|
||||
'cert-database': dsinstance.config_dirname(serverid),
|
||||
'cert-nickname': 'Server-Cert',
|
||||
'cert-nickname': ds.get_server_cert_nickname(serverid),
|
||||
'ca': 'IPA',
|
||||
'cert-postsave-command':
|
||||
'%s %s' % (template % 'restart_dirsrv', serverid),
|
||||
|
Loading…
Reference in New Issue
Block a user