Skip to content

Commit

Permalink
setup: Allow renewing certificates during the warning period
Browse files Browse the repository at this point in the history
The default value of CertExpirationWarnPeriodInDays, which applies to
CA, Engine and host certificates, is 365.  However engine-setup
offers (and allows) renewing the CA and Engine certificates only 60
days or less before their expiration.  This results in warnings about
certificate expiration presented to users, while the users cannot
actually renew the certificates for more than 300 days.

Let’s allow renewing CA and Engine certificate during the warning
period or, if it cannot be obtained, up to 365 days before their
expiration.

Bug-Url: https://bugzilla.redhat.com/2093954
Bug-Url: https://bugzilla.redhat.com/2096862
Bug-Url: https://bugzilla.redhat.com/2097725
  • Loading branch information
mz-pdm authored and mrkev-gh committed Jun 28, 2022
1 parent b11e94b commit 1cf709d
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 22 deletions.
40 changes: 35 additions & 5 deletions packaging/setup/ovirt_engine_setup/engine_common/pki_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from cryptography.x509.extensions import ExtensionNotFound

from ovirt_engine_setup.engine import constants as oenginecons
from ovirt_engine_setup.engine import vdcoption
from ovirt_engine_setup.engine_common import database


def x509_load_cert(fname):
Expand All @@ -33,8 +35,13 @@ def x509_load_cert(fname):
)


def cert_expires(x509cert):
# input: x509cert: cryptography.x509.Certificate object
def cert_expires(x509cert, short_life, environment, logger):
# input:
# - logger: The logger to use for logging
# - x509cert: cryptography.x509.Certificate object
# - short_life: Whether the certificate is a short-life (e.g. browser)
# certificate.
# - environment: Environment to pass to database.Statement
# return: bool

#
Expand All @@ -59,10 +66,28 @@ def cert_expires(x509cert):
# py cryptography to support timezones or by using other
# means, such as calling the openssl utility, e.g.
# openssl x509 -in ca.pem -noout -startdate
if short_life:
days = 60
else:
try:
days_str = vdcoption.VdcOption(
statement=database.Statement(
dbenvkeys=oenginecons.Const.ENGINE_DB_ENV_KEYS,
environment=environment,
),
).getVdcOption(
'CertExpirationWarnPeriodInDays',
ownConnection=True,
)
days = int(days_str)
except Exception as e:
logger.info("CertExpirationWarnPeriodInDays config value not "
"available, using 365 days: %s", e)
days = 365
return (
x509cert.not_valid_after.replace(tzinfo=None) -
datetime.datetime.utcnow() <
datetime.timedelta(days=60)
datetime.timedelta(days=days)
)


Expand All @@ -88,16 +113,21 @@ def cert_has_SAN(logger, x509cert):
return res


def ok_to_renew_cert(logger, x509cert, ca_cert, name, extract):
def ok_to_renew_cert(logger, x509cert, ca_cert, name, extract, short_life,
environment):
# input:
# - logger: The logger to use for logging
# - x509cert: cryptography.x509.Certificate object
# - ca_cert: cryptography.x509.Certificate object
# - name: A base name (--name param of pki-* scripts)
# - extract: bool. If True, we need to check the extracted cert
# - short_life: Whether the certificate is a short-life (e.g. browser)
# certificate.
# - environment: Environment to pass to database.Statement
# return: bool
res = False
if x509cert and (
cert_expires(x509cert) or
cert_expires(x509cert, short_life, environment, logger) or
not cert_has_SAN(logger, x509cert)
):
if not extract or ca_cert is None:
Expand Down
2 changes: 2 additions & 0 deletions packaging/setup/ovirt_engine_setup/remote_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ def enroll_cert(self):
None,
self._base_name,
True,
False,
self.environment,
):
self._need_cert = True

Expand Down
20 changes: 15 additions & 5 deletions packaging/setup/plugins/ovirt-engine-setup/ovirt-engine/pki/ca.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def _enrollCertificate(self, name, uninstall_files, keepKey=False,
},
)

def _ok_to_renew_cert(self, pkcs12, name, extract):
def _ok_to_renew_cert(self, pkcs12, name, extract, short_life):
# input:
# - pkcs12: A PKCS#12 file name
# - name: A base name (--name param of pki-* scripts)
Expand All @@ -312,6 +312,8 @@ def _ok_to_renew_cert(self, pkcs12, name, extract):
),
name,
extract,
short_life,
self.environment,
)

def _enrollCertificates(self, renew, uninstall_files):
Expand Down Expand Up @@ -339,7 +341,8 @@ def _enrollCertificates(self, renew, uninstall_files):
enroll = self._ok_to_renew_cert(
pkcs12,
entry['name'],
entry['extract']
entry['extract'],
entry['shortLife'],
)

if enroll:
Expand Down Expand Up @@ -496,7 +499,8 @@ def _customization(self):
)
def _customization_upgrade(self):
if True in [
pki_utils.cert_expires(pki_utils.x509_load_cert(cert))
pki_utils.cert_expires(pki_utils.x509_load_cert(cert), False,
self.environment, self.logger)
for cert in self._CA_FILES
if os.path.exists(cert)
] + [
Expand All @@ -506,7 +510,8 @@ def _customization_upgrade(self):
'%s.p12' % entry['name']
),
entry['name'],
entry['extract']
entry['extract'],
entry['shortLife'],
)
for entry in self.environment[oenginecons.PKIEnv.ENTITIES]
]:
Expand Down Expand Up @@ -718,7 +723,12 @@ def _template_aia(template):
for ca_file in self._CA_FILES:
if (
os.path.exists(ca_file) and
pki_utils.cert_expires(pki_utils.x509_load_cert(ca_file))
pki_utils.cert_expires(
pki_utils.x509_load_cert(ca_file),
False,
self.environment,
self.logger,
)
):
self._renewed_ca_files.add(ca_file)
self.logger.info(_('Renewing CA: %s'), ca_file)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,25 @@ def _(m):
return gettext.dgettext(message=m, domain='ovirt-engine-setup')


def _refresh_needed(cert_path, check_cert=True):
ca_cert_path = oenginecons.FileLocations.OVIRT_ENGINE_PKI_ENGINE_CA_CERT
return (not os.path.exists(cert_path) or
os.stat(ca_cert_path).st_mtime > os.stat(cert_path).st_mtime or
(check_cert and
pki_utils.cert_expires(pki_utils.x509_load_cert(cert_path))))


@util.export
class Plugin(plugin.PluginBase):
"""vmconsole proxy configuration plugin."""

def _subjectComponentEscape(self, s):
return outil.escape(s, '/\\')

def _refresh_needed(self, cert_path, check_cert=True):
ca_path = oenginecons.FileLocations.OVIRT_ENGINE_PKI_ENGINE_CA_CERT
return (not os.path.exists(cert_path) or
os.stat(ca_path).st_mtime > os.stat(cert_path).st_mtime or
(check_cert and
pki_utils.cert_expires(
pki_utils.x509_load_cert(cert_path),
False,
self.environment,
self.logger,
)))

def _enrollSSHKeys(self, host_mode, uninstall_files):
suffix = 'host' if host_mode else 'user'
name = '%s-%s' % (
Expand Down Expand Up @@ -187,7 +191,7 @@ def _setup(self):
condition=lambda self: (
self.environment[
ovmpcons.ConfigEnv.VMCONSOLE_PROXY_CONFIG
] and _refresh_needed(
] and self._refresh_needed(
ovmpcons.FileLocations.
OVIRT_ENGINE_PKI_VMCONSOLE_PROXY_HELPER_CERT
)
Expand Down Expand Up @@ -293,15 +297,15 @@ def _ssh_cert_file(self, suffix):
self.environment[
ovmpcons.ConfigEnv.VMCONSOLE_PROXY_CONFIG
] and (
_refresh_needed(
self._refresh_needed(
os.path.join(
ovmpcons.FileLocations.VMCONSOLE_PKI_DIR,
'proxy-ssh_host_rsa',
),
check_cert=False
) or
_refresh_needed(self._ssh_cert_file('user')) or
_refresh_needed(self._ssh_cert_file('host'))
self._refresh_needed(self._ssh_cert_file('user')) or
self._refresh_needed(self._ssh_cert_file('host'))
)
),
)
Expand Down

0 comments on commit 1cf709d

Please sign in to comment.