Require an ipa-ca SAN on 3rd party certs if ACME is enabled

ACME requires an ipa-ca SAN to have a fixed URL to connect to.
If the Apache certificate is replaced by a 3rd party cert then
it must provide this SAN otherwise it will break ACME.

https://pagure.io/freeipa/issue/8498

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
Reviewed-By: Mohammad Rizwan <myusuf@redhat.com>
This commit is contained in:
Rob Crittenden
2020-10-26 12:48:18 -04:00
parent 9c4785f042
commit 2768b0dbaf
4 changed files with 49 additions and 2 deletions

View File

@@ -31,6 +31,7 @@ import ldap
import os
import re
import shutil
import ssl
import sys
import syslog
import time
@@ -2271,6 +2272,30 @@ def import_ra_key(custodia):
CAInstance.configure_agent_renewal()
def check_ipa_ca_san(cert):
"""
Test whether the certificate has an ipa-ca SAN
:param cert: x509.IPACertificate
This SAN is necessary for ACME.
The caller is responsible for initializing the api.
On success returns None, on failure raises ValidationError
"""
expect = f'{ipalib.constants.IPA_CA_RECORD}.' \
f'{ipautil.format_netloc(api.env.domain)}'
try:
cert.match_hostname(expect)
except ssl.CertificateError:
raise errors.ValidationError(
name='certificate',
error='Does not have a \'{}\' SAN'.format(expect)
)
if __name__ == "__main__":
standard_logging_setup("install.log")
ds = dsinstance.DsInstance()

View File

@@ -4,7 +4,7 @@
import enum
from ipalib import api, errors
from ipalib import api, errors, x509
from ipalib import _
from ipalib.facts import is_ipa_configured
from ipaplatform.paths import paths
@@ -90,6 +90,13 @@ class IPAACMEManage(AdminTool):
except ValueError:
self.option_parser.error(f'unknown command "{self.args[0]}"')
def check_san_status(self):
"""
Require the Apache cert to have ipa-ca.$DOMAIN SAN
"""
cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
cainstance.check_ipa_ca_san(cert)
def run(self):
if not is_ipa_configured():
print("IPA is not configured.")
@@ -106,6 +113,7 @@ class IPAACMEManage(AdminTool):
state = acme_state(api)
with state as ca_api:
if self.command == Command.ENABLE:
self.check_san_status()
ca_api.enable()
elif self.command == Command.DISABLE:
ca_api.disable()

View File

@@ -27,12 +27,13 @@ import optparse # pylint: disable=deprecated-module
from ipalib import x509
from ipalib.install import certmonger
from ipaplatform.paths import paths
from ipapython import admintool
from ipapython import admintool, dogtag
from ipapython.certdb import NSSDatabase, get_ca_nickname
from ipapython.dn import DN
from ipapython import ipaldap
from ipalib import api, errors
from ipaserver.install import certs, dsinstance, installutils, krbinstance
from ipaserver.install import cainstance
class ServerCertInstall(admintool.AdminTool):
@@ -105,11 +106,22 @@ class ServerCertInstall(admintool.AdminTool):
raise admintool.ScriptError(
"Private key unlock password required")
def validate_http_cert(self):
if dogtag.acme_status():
cert, unused, _unused = self.load_pkcs12(
ca_chain_fname=paths.IPA_CA_CRT,
host_name=api.env.host
)
cainstance.check_ipa_ca_san(cert)
def run(self):
api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
api.finalize()
api.Backend.ldap2.connect(bind_pw=self.options.dirman_password)
if self.options.http:
self.validate_http_cert()
if self.options.dirsrv:
self.install_dirsrv_cert()