mirror of
https://github.com/certbot/certbot.git
synced 2026-06-03 22:08:07 -04:00
Conditional DBM handling
This commit is contained in:
parent
85187ea90d
commit
c63607b7f9
2 changed files with 57 additions and 33 deletions
|
|
@ -198,6 +198,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
# Temporary state for AutoHSTS enhancement
|
||||
self._autohsts = {} # type: Dict[str, Dict[str, Union[int, float]]]
|
||||
self._ocsp_prefetch = {} # type: Dict[str, str]
|
||||
self._ocsp_dbm_bsddb = False
|
||||
|
||||
# These will be set in the prepare function
|
||||
self._prepared = False
|
||||
|
|
@ -2561,15 +2562,53 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
if handler.ocsp_request_to_file(ocsp_workfile):
|
||||
# Guaranteed good response
|
||||
cache_path = os.path.join(self.config.config_dir, "ocsp", "ocsp_cache")
|
||||
# dbm.open automatically adds the file extension, it will be
|
||||
db = dbm.open(cache_path, "c")
|
||||
db = self._ocsp_dbm_open(cache_path)
|
||||
cert_sha = apache_util.certid_sha1(cert_path)
|
||||
db[cert_sha] = self._ocsp_response_dbm(ocsp_workfile)
|
||||
db.close()
|
||||
self._ocsp_dbm_close(db)
|
||||
else:
|
||||
logger.warning("Encountered an issue while trying to prefetch OCSP "
|
||||
"response for certificate: %s", cert_path)
|
||||
|
||||
def _ensure_ocsp_prefetch_compatibility(self):
|
||||
"""Make sure that the operating system supports the required libraries
|
||||
to manage Apache DBM files.
|
||||
|
||||
:raises: errors.NotSupportedError
|
||||
"""
|
||||
try:
|
||||
import bsddb
|
||||
except ImportError:
|
||||
import dbm
|
||||
if not hasattr(dbm, 'ndbm'):
|
||||
msg = ("Unfortunately your operating system does not have a "
|
||||
"compatible database module available for managing "
|
||||
"Apache cache database.")
|
||||
raise errors.NotSupportedError(msg)
|
||||
|
||||
def _ocsp_dbm_open(self, filepath):
|
||||
"""Helper method to open an DBM file in a way that depends on the platform
|
||||
that Certbot is run on. Returns an open database structure."""
|
||||
try:
|
||||
import bsddb
|
||||
self._ocsp_dbm_bsddb = True
|
||||
cache_path = filepath + ".db"
|
||||
database = bsddb.hashopen(cache_path, 'c')
|
||||
except ImportError:
|
||||
# Python3 doesn't have bsddb module, so we use dbm.ndbm instead
|
||||
import dbm
|
||||
database = dbm.ndbm.open(filepath, 'c')
|
||||
return database
|
||||
|
||||
def _ocsp_dbm_close(self, database):
|
||||
"""Helper method to sync and close a DBM file, in a way required by the
|
||||
used dbm implementation."""
|
||||
if self._ocsp_dbm_bsddb:
|
||||
database.sync()
|
||||
database.close()
|
||||
else:
|
||||
database.close()
|
||||
|
||||
def _ocsp_response_dbm(self, workfile):
|
||||
"""Creates a dbm entry for OCSP response data
|
||||
|
||||
|
|
@ -2645,6 +2684,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
TLS. i.e. clients would not have to query the OCSP responder.
|
||||
|
||||
"""
|
||||
# Fail early if we are not able to support this
|
||||
self._ensure_ocsp_prefetch_compatibility()
|
||||
prefetch_vhosts = set()
|
||||
for domain in domains:
|
||||
matched_vhosts = self.choose_vhosts(domain, create_if_no_ssl=False)
|
||||
|
|
@ -2658,6 +2699,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
self._enable_ocsp_stapling(vh, None, prefetch=True)
|
||||
self._ocsp_prefetch_save(lineage.cert_path, lineage.chain_path)
|
||||
self.restart()
|
||||
logger.warning(apache_util.certid_sha1_hex(lineage.cert_path))
|
||||
self._ocsp_refresh(lineage.cert_path, lineage.chain_path)
|
||||
|
||||
def update_ocsp_prefetch(self, _unused_lineage):
|
||||
|
|
|
|||
|
|
@ -12,19 +12,6 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
"""Tests for OCSP Prefetch feature"""
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def openDBM(self, path):
|
||||
self.config._ensure_ocsp_dirs()
|
||||
try:
|
||||
import dbm.ndbm as dbm # pragma: no cover
|
||||
d = dbm.open(path, 'c')
|
||||
except ImportError: # pragma: no cover
|
||||
# dbm.ndbm only available on Python3
|
||||
import anydbm # pragma: no cover
|
||||
anydbm._names = ["dbm"]
|
||||
d = anydbm.open(path, 'c')
|
||||
return d
|
||||
|
||||
|
||||
def setUp(self): # pylint: disable=arguments-differ
|
||||
super(OCSPPrefetchTest, self).setUp()
|
||||
|
||||
|
|
@ -73,6 +60,7 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
@mock.patch("certbot_apache.constants.OCSP_INTERNAL_TTL", 0)
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
||||
def test_ocsp_prefetch_refresh(self, _mock_restart):
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache")
|
||||
def ocsp_req_mock(workfile):
|
||||
"""Method to mock the OCSP request and write response to file"""
|
||||
with open(workfile, 'w') as fh:
|
||||
|
|
@ -84,11 +72,12 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
self.call_mocked(self.config.enable_ocsp_prefetch,
|
||||
self.lineage,
|
||||
["ocspvhost.com"])
|
||||
odbm = dbm.open(os.path.join(self.config_dir, "ocsp", "ocsp_cache"), 'c')
|
||||
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
self.assertEquals(len(odbm.keys()), 1)
|
||||
# The actual response data is prepended by Apache timestamp
|
||||
self.assertTrue(odbm[odbm.keys()[0]].endswith(b'MOCKRESPONSE'))
|
||||
odbm.close()
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
with mock.patch(ocsp_path, side_effect=ocsp_req_mock) as mock_ocsp:
|
||||
self.call_mocked(self.config.update_ocsp_prefetch, None)
|
||||
|
|
@ -115,17 +104,18 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.config_test")
|
||||
def test_ocsp_prefetch_backup_db(self, _mock_test):
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache.db")
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache")
|
||||
def ocsp_del_db():
|
||||
"""Side effect of _reload() that deletes the DBM file, like Apache
|
||||
does when restarting"""
|
||||
os.remove(db_path)
|
||||
self.assertFalse(os.path.isfile(db_path))
|
||||
full_db_path = db_path + ".db"
|
||||
os.remove(full_db_path)
|
||||
self.assertFalse(os.path.isfile(full_db_path))
|
||||
|
||||
self.config._ensure_ocsp_dirs()
|
||||
odbm = dbm.open(db_path[:-3], 'c')
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
odbm["mock_key"] = b'mock_value'
|
||||
odbm.close()
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
# Mock OCSP prefetch dict to signify that there should be a db
|
||||
self.config._ocsp_prefetch = {"mock": "value"}
|
||||
|
|
@ -133,9 +123,9 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
with mock.patch(rel_path, side_effect=ocsp_del_db) as mock_reload:
|
||||
self.config.restart()
|
||||
|
||||
odbm = dbm.open(db_path[:-3], 'c')
|
||||
odbm = self.config._ocsp_dbm_open(db_path)
|
||||
self.assertEquals(odbm["mock_key"], b'mock_value')
|
||||
odbm.close()
|
||||
self.config._ocsp_dbm_close(odbm)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.config_test")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._reload")
|
||||
|
|
@ -172,14 +162,6 @@ class OCSPPrefetchTest(util.ApacheTest):
|
|||
self.config.update_ocsp_prefetch(None)
|
||||
self.assertFalse(mock_refresh.called)
|
||||
|
||||
def test_dbm_format(self):
|
||||
db_path = os.path.join(self.config_dir, "ocsp", "ocsp_cache.db")
|
||||
dobject = self.openDBM(db_path)
|
||||
dbm_dir = dir(dobject)
|
||||
dbm_dir.append(dobject.__module__)
|
||||
self.assertEquals(1, dbm_dir)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
Loading…
Reference in a new issue