mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Secure AJP connector between Dogtag and Apache proxy
AJP implementation in Tomcat is vulnerable to CVE-2020-1938 if used without shared secret. Set up a shared secret between localhost connector and Apache mod_proxy_ajp pass-through. For existing secured AJP pass-through make sure the option used for configuration on the tomcat side is up to date. Tomcat 9.0.31.0 deprecated 'requiredSecret' option name in favor of 'secret'. Details can be found at https://tomcat.apache.org/migration-9.html#Upgrading_9.0.x Fixes: https://pagure.io/freeipa/issue/8221 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com> Reviewed-By: Florence Blanc-Renaud <flo@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
parent
593fac1ca9
commit
ec73de969f
@ -1,4 +1,4 @@
|
||||
# VERSION 13 - DO NOT REMOVE THIS LINE
|
||||
# VERSION 14 - DO NOT REMOVE THIS LINE
|
||||
|
||||
ProxyRequests Off
|
||||
|
||||
@ -6,7 +6,7 @@ ProxyRequests Off
|
||||
<LocationMatch "^/ca/ee/ca/checkRequest|^/ca/ee/ca/getCertChain|^/ca/ee/ca/getTokenInfo|^/ca/ee/ca/tokenAuthenticate|^/ca/ocsp|^/ca/ee/ca/updateNumberRange|^/ca/ee/ca/getCRL|^/ca/ee/ca/profileSubmit">
|
||||
SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
|
||||
SSLVerifyClient none
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT $DOGTAG_AJP_SECRET
|
||||
ProxyPassReverse ajp://localhost:$DOGTAG_PORT
|
||||
</LocationMatch>
|
||||
|
||||
@ -14,7 +14,7 @@ ProxyRequests Off
|
||||
<LocationMatch "^/ca/admin/ca/getCertChain|^/ca/admin/ca/getConfigEntries|^/ca/admin/ca/getCookie|^/ca/admin/ca/getStatus|^/ca/admin/ca/securityDomainLogin|^/ca/admin/ca/getDomainXML|^/ca/admin/ca/updateNumberRange|^/ca/admin/ca/tokenAuthenticate|^/ca/admin/ca/updateNumberRange|^/ca/admin/ca/updateDomainXML|^/ca/admin/ca/updateConnector|^/ca/admin/ca/getSubsystemCert|^/kra/admin/kra/updateNumberRange|^/kra/admin/kra/getConfigEntries">
|
||||
SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
|
||||
SSLVerifyClient none
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT $DOGTAG_AJP_SECRET
|
||||
ProxyPassReverse ajp://localhost:$DOGTAG_PORT
|
||||
</LocationMatch>
|
||||
|
||||
@ -22,7 +22,7 @@ ProxyRequests Off
|
||||
<LocationMatch "^/ca/agent/ca/displayBySerial|^/ca/agent/ca/doRevoke|^/ca/agent/ca/doUnrevoke|^/ca/agent/ca/updateDomainXML|^/ca/eeca/ca/profileSubmitSSLClient|^/kra/agent/kra/connector">
|
||||
SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
|
||||
SSLVerifyClient require
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT $DOGTAG_AJP_SECRET
|
||||
ProxyPassReverse ajp://localhost:$DOGTAG_PORT
|
||||
</LocationMatch>
|
||||
|
||||
@ -30,7 +30,7 @@ ProxyRequests Off
|
||||
<LocationMatch "^/ca/rest/account/login|^/ca/rest/account/logout|^/ca/rest/installer/installToken|^/ca/rest/securityDomain/domainInfo|^/ca/rest/securityDomain/installToken|^/ca/rest/profiles|^/ca/rest/authorities|^/ca/rest/certrequests|^/ca/rest/admin/kraconnector/remove|^/ca/rest/certs/search">
|
||||
SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
|
||||
SSLVerifyClient optional
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT $DOGTAG_AJP_SECRET
|
||||
ProxyPassReverse ajp://localhost:$DOGTAG_PORT
|
||||
</LocationMatch>
|
||||
|
||||
@ -38,7 +38,7 @@ ProxyRequests Off
|
||||
<LocationMatch "^/kra/rest/config/cert/transport|^/kra/rest/account|^/kra/rest/agent/keyrequests|^/kra/rest/agent/keys">
|
||||
SSLOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
|
||||
SSLVerifyClient optional
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT
|
||||
ProxyPassMatch ajp://localhost:$DOGTAG_PORT $DOGTAG_AJP_SECRET
|
||||
ProxyPassReverse ajp://localhost:$DOGTAG_PORT
|
||||
</LocationMatch>
|
||||
|
||||
|
@ -108,6 +108,7 @@ class BasePathNamespace:
|
||||
PKI_TOMCAT_ALIAS_DIR = "/etc/pki/pki-tomcat/alias"
|
||||
PKI_TOMCAT_ALIAS_PWDFILE_TXT = "/etc/pki/pki-tomcat/alias/pwdfile.txt"
|
||||
PKI_TOMCAT_PASSWORD_CONF = "/etc/pki/pki-tomcat/password.conf"
|
||||
PKI_TOMCAT_SERVER_XML = "/etc/pki/pki-tomcat/server.xml"
|
||||
ETC_REDHAT_RELEASE = "/etc/redhat-release"
|
||||
RESOLV_CONF = "/etc/resolv.conf"
|
||||
SAMBA_KEYTAB = "/etc/samba/samba.keytab"
|
||||
@ -339,6 +340,7 @@ class BasePathNamespace:
|
||||
KRB5KDC_LOG = "/var/log/krb5kdc.log"
|
||||
MESSAGES = "/var/log/messages"
|
||||
VAR_LOG_PKI_DIR = "/var/log/pki/"
|
||||
BIN_TOMCAT = "/usr/sbin/tomcat"
|
||||
TOMCAT_TOPLEVEL_DIR = "/var/log/pki/pki-tomcat"
|
||||
TOMCAT_CA_DIR = "/var/log/pki/pki-tomcat/ca"
|
||||
TOMCAT_CA_ARCHIVE_DIR = "/var/log/pki/pki-tomcat/ca/archive"
|
||||
|
@ -59,6 +59,7 @@ class DebianPathNamespace(BasePathNamespace):
|
||||
SYSCONFIG_PKI = "/etc/dogtag/"
|
||||
SYSCONFIG_PKI_TOMCAT = "/etc/default/pki-tomcat"
|
||||
SYSCONFIG_PKI_TOMCAT_PKI_TOMCAT_DIR = "/etc/dogtag/tomcat/pki-tomcat"
|
||||
BIN_TOMCAT = "/usr/share/tomcat9/bin/version.sh"
|
||||
SYSTEMD_SYSTEM_HTTPD_D_DIR = "/etc/systemd/system/apache2.service.d/"
|
||||
SYSTEMD_SYSTEM_HTTPD_IPA_CONF = "/etc/systemd/system/apache2.service.d/ipa.conf"
|
||||
DNSSEC_TRUSTED_KEY = "/etc/bind/trusted-key.key"
|
||||
|
@ -401,6 +401,7 @@ class CAInstance(DogtagInstance):
|
||||
self.step("configuring certificate server instance",
|
||||
self.__spawn_instance)
|
||||
self.step("Add ipa-pki-wait-running", self.add_ipa_wait)
|
||||
self.step("secure AJP connector", self.secure_ajp_connector)
|
||||
self.step("reindex attributes", self.reindex_task)
|
||||
self.step("exporting Dogtag certificate store pin",
|
||||
self.create_certstore_passwdfile)
|
||||
@ -452,6 +453,7 @@ class CAInstance(DogtagInstance):
|
||||
self.step("configure certificate renewals", self.configure_renewal)
|
||||
self.step("Configure HTTP to proxy connections",
|
||||
self.http_proxy)
|
||||
# This restart is needed for ACL reload in CA, do not remove it
|
||||
self.step("restarting certificate server", self.restart_instance)
|
||||
self.step("updating IPA configuration", update_ipa_conf)
|
||||
self.step("enabling CA instance", self.__enable_instance)
|
||||
|
@ -28,6 +28,9 @@ import os
|
||||
import shutil
|
||||
import traceback
|
||||
import dbus
|
||||
import re
|
||||
import pwd
|
||||
import lxml.etree
|
||||
|
||||
from configparser import DEFAULTSECT, ConfigParser, RawConfigParser
|
||||
|
||||
@ -42,6 +45,7 @@ from ipalib.constants import CA_DBUS_TIMEOUT, IPA_CA_RECORD, RENEWAL_CA_NAME
|
||||
from ipaplatform import services
|
||||
from ipaplatform.constants import constants
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
from ipapython import directivesetter
|
||||
from ipapython import ipaldap
|
||||
from ipapython import ipautil
|
||||
@ -158,6 +162,7 @@ class DogtagInstance(service.Service):
|
||||
self.pki_config_override = None
|
||||
self.ca_subject = None
|
||||
self.subject_base = None
|
||||
self.ajp_secret = None
|
||||
|
||||
def is_installed(self):
|
||||
"""
|
||||
@ -275,6 +280,72 @@ class DogtagInstance(service.Service):
|
||||
logger.critical("failed to uninstall %s instance %s",
|
||||
self.subsystem, e)
|
||||
|
||||
def __is_newer_tomcat_version(self, default=None):
|
||||
try:
|
||||
result = ipautil.run([paths.BIN_TOMCAT, "version"],
|
||||
capture_output=True)
|
||||
sn = re.search(
|
||||
r'Server number:\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)',
|
||||
result.output)
|
||||
if sn is None:
|
||||
logger.info("tomcat version cannot be parsed, "
|
||||
"default to pre-%s", default)
|
||||
return False
|
||||
v = tasks.parse_ipa_version(sn.group(1))
|
||||
if v >= tasks.parse_ipa_version(default):
|
||||
return True
|
||||
except ipautil.CalledProcessError as e:
|
||||
logger.info(
|
||||
"failed to discover tomcat version, "
|
||||
"default to pre-%s, error: %s",
|
||||
default, str(e))
|
||||
return False
|
||||
|
||||
def secure_ajp_connector(self):
|
||||
""" Update AJP connector to use a password protection """
|
||||
|
||||
server_xml = lxml.etree.parse(paths.PKI_TOMCAT_SERVER_XML)
|
||||
doc = server_xml.getroot()
|
||||
|
||||
# no AJP connector means no need to update anything
|
||||
connectors = doc.xpath('//Connector[@port="8009"]')
|
||||
if len(connectors) == 0:
|
||||
return
|
||||
|
||||
# AJP connector is set on port 8009. Use non-greedy search to find it
|
||||
connector = connectors[0]
|
||||
|
||||
# Detect tomcat version and choose the right option name
|
||||
# pre-9.0.31.0 uses 'requiredSecret'
|
||||
# 9.0.31.0 or later uses 'secret'
|
||||
secretattr = 'requiredSecret'
|
||||
oldattr = 'requiredSecret'
|
||||
if self.__is_newer_tomcat_version('9.0.31.0'):
|
||||
secretattr = 'secret'
|
||||
|
||||
rewrite = True
|
||||
if secretattr in connector.attrib:
|
||||
# secret is already in place
|
||||
# Perhaps, we need to synchronize it with Apache configuration
|
||||
self.ajp_secret = connector.attrib[secretattr]
|
||||
rewrite = False
|
||||
else:
|
||||
if oldattr in connector.attrib:
|
||||
self.ajp_secret = connector.attrib[oldattr]
|
||||
connector.attrib[secretattr] = self.ajp_secret
|
||||
del connector.attrib[oldattr]
|
||||
else:
|
||||
# Generate password, don't use special chars to not break XML
|
||||
self.ajp_secret = ipautil.ipa_generate_password(special=None)
|
||||
connector.attrib[secretattr] = self.ajp_secret
|
||||
|
||||
if rewrite:
|
||||
pent = pwd.getpwnam(constants.PKI_USER)
|
||||
with open(paths.PKI_TOMCAT_SERVER_XML, "wb") as fd:
|
||||
server_xml.write(fd, pretty_print=True, encoding="utf-8")
|
||||
os.fchmod(fd.fileno(), 0o660)
|
||||
os.fchown(fd.fileno(), pent.pw_uid, pent.pw_gid)
|
||||
|
||||
def http_proxy(self):
|
||||
""" Update the http proxy file """
|
||||
template_filename = (
|
||||
@ -284,11 +355,20 @@ class DogtagInstance(service.Service):
|
||||
DOGTAG_PORT=8009,
|
||||
CLONE='' if self.clone else '#',
|
||||
FQDN=self.fqdn,
|
||||
DOGTAG_AJP_SECRET='',
|
||||
)
|
||||
if self.ajp_secret:
|
||||
sub_dict['DOGTAG_AJP_SECRET'] = "secret={}".format(self.ajp_secret)
|
||||
template = ipautil.template_file(template_filename, sub_dict)
|
||||
with open(paths.HTTPD_IPA_PKI_PROXY_CONF, "w") as fd:
|
||||
fd.write(template)
|
||||
os.fchmod(fd.fileno(), 0o640)
|
||||
# Restart httpd
|
||||
http_service = services.knownservices.httpd
|
||||
logger.debug("Restarting %s to apply AJP changes",
|
||||
http_service.service_name)
|
||||
http_service.restart()
|
||||
logger.debug("%s successfully restarted", http_service.service_name)
|
||||
|
||||
def configure_certmonger_renewal_helpers(self):
|
||||
"""
|
||||
|
@ -133,8 +133,6 @@ class KRAInstance(DogtagInstance):
|
||||
self.step("configure certmonger for renewals",
|
||||
self.configure_certmonger_renewal_helpers)
|
||||
self.step("configure certificate renewals", self.configure_renewal)
|
||||
self.step("configure HTTP to proxy connections",
|
||||
self.http_proxy)
|
||||
if not self.clone:
|
||||
self.step("add vault container", self.__add_vault_container)
|
||||
self.step("apply LDAP updates", self.__apply_updates)
|
||||
|
@ -1966,6 +1966,14 @@ def upgrade_configuration():
|
||||
os.path.join(paths.USR_SHARE_IPA_DIR,
|
||||
"ipa-kdc-proxy.conf.template"))
|
||||
if ca.is_configured():
|
||||
# Handle upgrade of AJP connector configuration
|
||||
ca.secure_ajp_connector()
|
||||
if ca.ajp_secret:
|
||||
sub_dict['DOGTAG_AJP_SECRET'] = "secret={}".format(
|
||||
ca.ajp_secret)
|
||||
else:
|
||||
sub_dict['DOGTAG_AJP_SECRET'] = ''
|
||||
|
||||
upgrade_file(
|
||||
sub_dict,
|
||||
paths.HTTPD_IPA_PKI_PROXY_CONF,
|
||||
|
Loading…
Reference in New Issue
Block a user