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 ipapython.dn import DN
|
||||||
from ipalib import pkcs10, x509, api
|
from ipalib import pkcs10, x509, api
|
||||||
from ipalib.errors import CertificateOperationError
|
from ipalib.errors import CertificateOperationError
|
||||||
|
from ipalib.install import certstore
|
||||||
from ipalib.text import _
|
from ipalib.text import _
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
@ -659,6 +660,31 @@ class CertDB(object):
|
|||||||
subject=host,
|
subject=host,
|
||||||
passwd_fname=self.passwd_fname)
|
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):
|
class _CrossProcessLock(object):
|
||||||
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
||||||
|
@ -1033,22 +1033,59 @@ class DsInstance(service.Service):
|
|||||||
logger.error(
|
logger.error(
|
||||||
'Unable to restart DS instance %s: %s', ds_instance, e)
|
'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):
|
def stop_tracking_certificates(self, serverid=None):
|
||||||
if serverid is None:
|
if serverid is None:
|
||||||
serverid = self.get_state("serverid")
|
serverid = self.get_state("serverid")
|
||||||
if not serverid is None:
|
if not serverid is None:
|
||||||
|
nickname = self.get_server_cert_nickname(serverid)
|
||||||
# drop the trailing / off the config_dirname so the directory
|
# drop the trailing / off the config_dirname so the directory
|
||||||
# will match what is in certmonger
|
# will match what is in certmonger
|
||||||
dirname = config_dirname(serverid)[:-1]
|
dirname = config_dirname(serverid)[:-1]
|
||||||
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
||||||
dsdb.untrack_server_cert(self.nickname)
|
dsdb.untrack_server_cert(nickname)
|
||||||
|
|
||||||
def start_tracking_certificates(self, serverid):
|
def start_tracking_certificates(self, serverid):
|
||||||
|
nickname = self.get_server_cert_nickname(serverid)
|
||||||
dirname = config_dirname(serverid)[:-1]
|
dirname = config_dirname(serverid)[:-1]
|
||||||
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
||||||
dsdb.track_server_cert(self.nickname, self.principal,
|
if dsdb.is_ipa_issued_cert(api, nickname):
|
||||||
dsdb.passwd_fname,
|
dsdb.track_server_cert(nickname, self.principal,
|
||||||
'restart_dirsrv %s' % serverid)
|
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
|
# we could probably move this function into the service.Service
|
||||||
# class - it's very generic - all we need is a way to get an
|
# 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(
|
installutils.set_directive(
|
||||||
paths.HTTPD_NSS_CONF, 'NSSNickname', quoted_nickname, quotes=False)
|
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):
|
def set_mod_nss_protocol(self):
|
||||||
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSProtocol', 'TLSv1.0,TLSv1.1,TLSv1.2', False)
|
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):
|
def stop_tracking_certificates(self):
|
||||||
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
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):
|
def start_tracking_certificates(self):
|
||||||
db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
||||||
db.track_server_cert(self.cert_nickname, self.principal,
|
nickname = self.get_mod_nss_nickname()
|
||||||
db.passwd_fname, 'restart_httpd')
|
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):
|
def request_service_keytab(self):
|
||||||
super(HTTPInstance, self).request_service_keytab()
|
super(HTTPInstance, self).request_service_keytab()
|
||||||
|
@ -972,13 +972,13 @@ def certificate_renewal_update(ca, ds, http):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'cert-database': paths.HTTPD_ALIAS_DIR,
|
'cert-database': paths.HTTPD_ALIAS_DIR,
|
||||||
'cert-nickname': 'Server-Cert',
|
'cert-nickname': http.get_mod_nss_nickname(),
|
||||||
'ca': 'IPA',
|
'ca': 'IPA',
|
||||||
'cert-postsave-command': template % 'restart_httpd',
|
'cert-postsave-command': template % 'restart_httpd',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'cert-database': dsinstance.config_dirname(serverid),
|
'cert-database': dsinstance.config_dirname(serverid),
|
||||||
'cert-nickname': 'Server-Cert',
|
'cert-nickname': ds.get_server_cert_nickname(serverid),
|
||||||
'ca': 'IPA',
|
'ca': 'IPA',
|
||||||
'cert-postsave-command':
|
'cert-postsave-command':
|
||||||
'%s %s' % (template % 'restart_dirsrv', serverid),
|
'%s %s' % (template % 'restart_dirsrv', serverid),
|
||||||
|
Loading…
Reference in New Issue
Block a user