|
|
460745 |
From 8c576e8c3640b84869abacc43a74aa250df5a8e9 Mon Sep 17 00:00:00 2001
|
|
|
460745 |
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
|
460745 |
Date: Tue, 5 Sep 2017 16:17:31 +0200
|
|
|
460745 |
Subject: [PATCH] Backport 4-5: Fix ipa-server-upgrade with server cert
|
|
|
460745 |
tracking
|
|
|
460745 |
|
|
|
460745 |
ipa-server-upgrade fails with Server-Cert not found, when trying to
|
|
|
460745 |
track httpd/ldap server certificates. There are 2 issues in the upgrade:
|
|
|
460745 |
- the certificates should be tracked only if they were issued by IPA CA
|
|
|
460745 |
(it is possible to have CA configured but 3rd part certs)
|
|
|
460745 |
- the certificate nickname can be different from Server-Cert
|
|
|
460745 |
|
|
|
460745 |
The fix provides methods to find the server crt nickname for http and ldap,
|
|
|
460745 |
and a method to check if the server certs are issued by IPA and need to be
|
|
|
460745 |
tracked by certmonger.
|
|
|
460745 |
|
|
|
460745 |
https://pagure.io/freeipa/issue/7141
|
|
|
460745 |
|
|
|
460745 |
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
|
|
|
460745 |
Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
|
|
|
460745 |
---
|
|
|
460745 |
ipaserver/install/certs.py | 27 ++++++++++++++++++++++
|
|
|
460745 |
ipaserver/install/dsinstance.py | 45 +++++++++++++++++++++++++++++++++----
|
|
|
460745 |
ipaserver/install/httpinstance.py | 16 ++++++++++---
|
|
|
460745 |
ipaserver/install/server/upgrade.py | 4 ++--
|
|
|
460745 |
4 files changed, 83 insertions(+), 9 deletions(-)
|
|
|
460745 |
|
|
|
460745 |
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
|
|
|
460745 |
index 02c479d92511fcf4043e7d6798c85cf8256c3299..de96318db51b03f2515814d574cfebf1b242b6a6 100644
|
|
|
460745 |
--- a/ipaserver/install/certs.py
|
|
|
460745 |
+++ b/ipaserver/install/certs.py
|
|
|
460745 |
@@ -42,6 +42,7 @@ from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
|
|
|
460745 |
from ipapython.dn import DN
|
|
|
460745 |
from ipalib import pkcs10, x509, api
|
|
|
460745 |
from ipalib.errors import CertificateOperationError
|
|
|
460745 |
+from ipalib.install import certstore
|
|
|
460745 |
from ipalib.text import _
|
|
|
460745 |
from ipaplatform.paths import paths
|
|
|
460745 |
|
|
|
460745 |
@@ -669,6 +670,32 @@ class CertDB(object):
|
|
|
460745 |
subject=host,
|
|
|
460745 |
passwd_fname=self.passwd_fname)
|
|
|
460745 |
|
|
|
460745 |
+ def is_ipa_issued_cert(self, api, nickname):
|
|
|
460745 |
+ """
|
|
|
460745 |
+ Return True if the certificate contained in the CertDB with the
|
|
|
460745 |
+ provided nickname has been issued by IPA.
|
|
|
460745 |
+
|
|
|
460745 |
+ Note that this method can only be executed if api has been initialized
|
|
|
460745 |
+ """
|
|
|
460745 |
+ # This method needs to compare the cert issuer (from the NSS DB
|
|
|
460745 |
+ # and the subject from the CA (from LDAP), because nicknames are not
|
|
|
460745 |
+ # always aligned.
|
|
|
460745 |
+
|
|
|
460745 |
+ cacert_subject = certstore.get_ca_subject(
|
|
|
460745 |
+ api.Backend.ldap2,
|
|
|
460745 |
+ api.env.container_ca,
|
|
|
460745 |
+ api.env.basedn)
|
|
|
460745 |
+
|
|
|
460745 |
+ # The cert can be issued directly by IPA. In this case, the cert
|
|
|
460745 |
+ # issuer is IPA CA subject.
|
|
|
460745 |
+ cert = self.get_cert_from_db(nickname)
|
|
|
460745 |
+ if cert is None:
|
|
|
460745 |
+ raise RuntimeError("Could not find the cert %s in %s"
|
|
|
460745 |
+ % (nickname, self.secdir))
|
|
|
460745 |
+ issuer = DN(x509.load_certificate(cert).issuer)
|
|
|
460745 |
+
|
|
|
460745 |
+ return issuer == cacert_subject
|
|
|
460745 |
+
|
|
|
460745 |
|
|
|
460745 |
class _CrossProcessLock(object):
|
|
|
460745 |
_DATETIME_FORMAT = '%Y%m%d%H%M%S%f'
|
|
|
460745 |
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
|
|
|
460745 |
index 39248edb285ee4d792b4500d83d88b24f5732d10..c9db8ac28c3ca10539b745ca09f4d8aaece02e0c 100644
|
|
|
460745 |
--- a/ipaserver/install/dsinstance.py
|
|
|
460745 |
+++ b/ipaserver/install/dsinstance.py
|
|
|
460745 |
@@ -1028,22 +1028,59 @@ class DsInstance(service.Service):
|
|
|
460745 |
root_logger.error(
|
|
|
460745 |
'Unable to restart DS instance %s: %s', ds_instance, e)
|
|
|
460745 |
|
|
|
460745 |
+ def get_server_cert_nickname(self, serverid=None):
|
|
|
460745 |
+ """
|
|
|
460745 |
+ Retrieve the nickname of the server cert used by dirsrv.
|
|
|
460745 |
+
|
|
|
460745 |
+ The method directly reads the dse.ldif to find the attribute
|
|
|
460745 |
+ nsSSLPersonalitySSL of cn=RSA,cn=encryption,cn=config because
|
|
|
460745 |
+ LDAP is not always accessible when we need to get the nickname
|
|
|
460745 |
+ (for instance during uninstall).
|
|
|
460745 |
+ """
|
|
|
460745 |
+ if serverid is None:
|
|
|
460745 |
+ serverid = self.get_state("serverid")
|
|
|
460745 |
+ if serverid is not None:
|
|
|
460745 |
+ dirname = config_dirname(serverid)
|
|
|
460745 |
+ config_file = os.path.join(dirname, "dse.ldif")
|
|
|
460745 |
+ rsa_dn = "cn=RSA,cn=encryption,cn=config"
|
|
|
460745 |
+ with open(config_file, "r") as in_file:
|
|
|
460745 |
+ parser = upgradeinstance.GetEntryFromLDIF(
|
|
|
460745 |
+ in_file,
|
|
|
460745 |
+ entries_dn=[rsa_dn])
|
|
|
460745 |
+ parser.parse()
|
|
|
460745 |
+ try:
|
|
|
460745 |
+ config_entry = parser.get_results()[rsa_dn]
|
|
|
460745 |
+ nickname = config_entry["nsSSLPersonalitySSL"][0]
|
|
|
460745 |
+ return nickname.decode('utf-8')
|
|
|
460745 |
+ except (KeyError, IndexError):
|
|
|
460745 |
+ root_logger.error("Unable to find server cert nickname in "
|
|
|
460745 |
+ "%s", config_file)
|
|
|
460745 |
+
|
|
|
460745 |
+ root_logger.debug("Falling back to nickname Server-Cert")
|
|
|
460745 |
+ return 'Server-Cert'
|
|
|
460745 |
+
|
|
|
460745 |
def stop_tracking_certificates(self, serverid=None):
|
|
|
460745 |
if serverid is None:
|
|
|
460745 |
serverid = self.get_state("serverid")
|
|
|
460745 |
if not serverid is None:
|
|
|
460745 |
+ nickname = self.get_server_cert_nickname(serverid)
|
|
|
460745 |
# drop the trailing / off the config_dirname so the directory
|
|
|
460745 |
# will match what is in certmonger
|
|
|
460745 |
dirname = config_dirname(serverid)[:-1]
|
|
|
460745 |
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
|
|
460745 |
- dsdb.untrack_server_cert(self.nickname)
|
|
|
460745 |
+ dsdb.untrack_server_cert(nickname)
|
|
|
460745 |
|
|
|
460745 |
def start_tracking_certificates(self, serverid):
|
|
|
460745 |
+ nickname = self.get_server_cert_nickname(serverid)
|
|
|
460745 |
dirname = config_dirname(serverid)[:-1]
|
|
|
460745 |
dsdb = certs.CertDB(self.realm, nssdir=dirname)
|
|
|
460745 |
- dsdb.track_server_cert(self.nickname, self.principal,
|
|
|
460745 |
- dsdb.passwd_fname,
|
|
|
460745 |
- 'restart_dirsrv %s' % serverid)
|
|
|
460745 |
+ if dsdb.is_ipa_issued_cert(api, nickname):
|
|
|
460745 |
+ dsdb.track_server_cert(nickname, self.principal,
|
|
|
460745 |
+ dsdb.passwd_fname,
|
|
|
460745 |
+ 'restart_dirsrv %s' % serverid)
|
|
|
460745 |
+ else:
|
|
|
460745 |
+ root_logger.debug("Will not track DS server certificate %s as it "
|
|
|
460745 |
+ "is not issued by IPA", nickname)
|
|
|
460745 |
|
|
|
460745 |
# we could probably move this function into the service.Service
|
|
|
460745 |
# class - it's very generic - all we need is a way to get an
|
|
|
460745 |
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
|
|
|
460745 |
index f637b97db8f21ddbc00c4f70e18e836d300b2f33..e55edebc5d4e45d7cb4cb66d28a270e6d6a56e33 100644
|
|
|
460745 |
--- a/ipaserver/install/httpinstance.py
|
|
|
460745 |
+++ b/ipaserver/install/httpinstance.py
|
|
|
460745 |
@@ -266,6 +266,11 @@ class HTTPInstance(service.Service):
|
|
|
460745 |
installutils.set_directive(
|
|
|
460745 |
paths.HTTPD_NSS_CONF, 'NSSNickname', quoted_nickname, quotes=False)
|
|
|
460745 |
|
|
|
460745 |
+ def get_mod_nss_nickname(self):
|
|
|
460745 |
+ cert = installutils.get_directive(paths.HTTPD_NSS_CONF, 'NSSNickname')
|
|
|
460745 |
+ nickname = installutils.unquote_directive_value(cert, quote_char="'")
|
|
|
460745 |
+ return nickname
|
|
|
460745 |
+
|
|
|
460745 |
def set_mod_nss_protocol(self):
|
|
|
460745 |
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSProtocol', 'TLSv1.0,TLSv1.1,TLSv1.2', False)
|
|
|
460745 |
|
|
|
460745 |
@@ -582,12 +587,17 @@ class HTTPInstance(service.Service):
|
|
|
460745 |
|
|
|
460745 |
def stop_tracking_certificates(self):
|
|
|
460745 |
db = certs.CertDB(api.env.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
|
|
460745 |
- db.untrack_server_cert(self.cert_nickname)
|
|
|
460745 |
+ db.untrack_server_cert(self.get_mod_nss_nickname())
|
|
|
460745 |
|
|
|
460745 |
def start_tracking_certificates(self):
|
|
|
460745 |
db = certs.CertDB(self.realm, nssdir=paths.HTTPD_ALIAS_DIR)
|
|
|
460745 |
- db.track_server_cert(self.cert_nickname, self.principal,
|
|
|
460745 |
- db.passwd_fname, 'restart_httpd')
|
|
|
460745 |
+ nickname = self.get_mod_nss_nickname()
|
|
|
460745 |
+ if db.is_ipa_issued_cert(api, nickname):
|
|
|
460745 |
+ db.track_server_cert(nickname, self.principal,
|
|
|
460745 |
+ db.passwd_fname, 'restart_httpd')
|
|
|
460745 |
+ else:
|
|
|
460745 |
+ root_logger.debug("Will not track HTTP server cert %s as it is "
|
|
|
460745 |
+ "not issued by IPA", nickname)
|
|
|
460745 |
|
|
|
460745 |
def request_service_keytab(self):
|
|
|
460745 |
super(HTTPInstance, self).request_service_keytab()
|
|
|
460745 |
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
|
|
460745 |
index 109e922e3a3ea25f882fdd81765788a3881e87bd..0947766c076251e7608241803d3a1eabee65ae11 100644
|
|
|
460745 |
--- a/ipaserver/install/server/upgrade.py
|
|
|
460745 |
+++ b/ipaserver/install/server/upgrade.py
|
|
|
460745 |
@@ -957,13 +957,13 @@ def certificate_renewal_update(ca, ds, http):
|
|
|
460745 |
},
|
|
|
460745 |
{
|
|
|
460745 |
'cert-database': paths.HTTPD_ALIAS_DIR,
|
|
|
460745 |
- 'cert-nickname': 'Server-Cert',
|
|
|
460745 |
+ 'cert-nickname': http.get_mod_nss_nickname(),
|
|
|
460745 |
'ca': 'IPA',
|
|
|
460745 |
'cert-postsave-command': template % 'restart_httpd',
|
|
|
460745 |
},
|
|
|
460745 |
{
|
|
|
460745 |
'cert-database': dsinstance.config_dirname(serverid),
|
|
|
460745 |
- 'cert-nickname': 'Server-Cert',
|
|
|
460745 |
+ 'cert-nickname': ds.get_server_cert_nickname(serverid),
|
|
|
460745 |
'ca': 'IPA',
|
|
|
460745 |
'cert-postsave-command':
|
|
|
460745 |
'%s %s' % (template % 'restart_dirsrv', serverid),
|
|
|
460745 |
--
|
|
|
460745 |
2.13.5
|