mirror of
https://github.com/certbot/certbot.git
synced 2026-03-03 05:40:46 -05:00
This PR adds two new command line parameters, --apache-ctlpath and --apache-binpath both of which are used to construct commands that we shell out for. The way that we previously fetched values either from Certbot configuration object or the dictionary of distribution based constants is now also unified, and the active options are parsed in prepare() to make it easier to override needed values for the distributions needing this behavior. Fixes: #5338 * Added the command line options and parsing * Refactor existing code * Distro override updates * Handle vhost_root from cli * Fix compatibility tests * Add comment about changes to command line arguments * Check None properly * Made help texts consistent * Keep the old defaults * Move to shorter CLI parameter names * No need for specific bin path, nor apache_cmd anymore * Make sure that we use user provided vhost-root value * Fix alt restart commands in overrides * Fix version_cmd defaults in overrides * Fix comparison * Remove cruft, and use configuration object for parser parameter
187 lines
8.8 KiB
Python
187 lines
8.8 KiB
Python
# pylint: disable=too-many-public-methods,too-many-lines
|
|
"""Test for certbot_apache.configurator AutoHSTS functionality"""
|
|
import re
|
|
import unittest
|
|
import mock
|
|
# six is used in mock.patch()
|
|
import six # pylint: disable=unused-import
|
|
|
|
from certbot import errors
|
|
from certbot_apache import constants
|
|
from certbot_apache.tests import util
|
|
|
|
|
|
class AutoHSTSTest(util.ApacheTest):
|
|
"""Tests for AutoHSTS feature"""
|
|
# pylint: disable=protected-access
|
|
|
|
def setUp(self): # pylint: disable=arguments-differ
|
|
super(AutoHSTSTest, self).setUp()
|
|
|
|
self.config = util.get_apache_configurator(
|
|
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
|
|
self.config.parser.modules.add("headers_module")
|
|
self.config.parser.modules.add("mod_headers.c")
|
|
self.config.parser.modules.add("ssl_module")
|
|
self.config.parser.modules.add("mod_ssl.c")
|
|
|
|
self.vh_truth = util.get_vh_truth(
|
|
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
|
|
|
|
def get_autohsts_value(self, vh_path):
|
|
""" Get value from Strict-Transport-Security header """
|
|
header_path = self.config.parser.find_dir("Header", None, vh_path)
|
|
if header_path:
|
|
pat = '(?:[ "]|^)(strict-transport-security)(?:[ "]|$)'
|
|
for head in header_path:
|
|
if re.search(pat, self.config.parser.aug.get(head).lower()):
|
|
return self.config.parser.aug.get(head.replace("arg[3]",
|
|
"arg[4]"))
|
|
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod")
|
|
def test_autohsts_enable_headers_mod(self, mock_enable, _restart):
|
|
self.config.parser.modules.discard("headers_module")
|
|
self.config.parser.modules.discard("mod_header.c")
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
self.assertTrue(mock_enable.called)
|
|
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
def test_autohsts_deploy_already_exists(self, _restart):
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
self.assertRaises(errors.PluginEnhancementAlreadyPresent,
|
|
self.config.enable_autohsts,
|
|
mock.MagicMock(), ["ocspvhost.com"])
|
|
|
|
@mock.patch("certbot_apache.constants.AUTOHSTS_FREQ", 0)
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.prepare")
|
|
def test_autohsts_increase(self, mock_prepare, _mock_restart):
|
|
self.config._prepared = False
|
|
maxage = "\"max-age={0}\""
|
|
initial_val = maxage.format(constants.AUTOHSTS_STEPS[0])
|
|
inc_val = maxage.format(constants.AUTOHSTS_STEPS[1])
|
|
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
# Verify initial value
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
initial_val)
|
|
# Increase
|
|
self.config.update_autohsts(mock.MagicMock())
|
|
# Verify increased value
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
inc_val)
|
|
self.assertTrue(mock_prepare.called)
|
|
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator._autohsts_increase")
|
|
def test_autohsts_increase_noop(self, mock_increase, _restart):
|
|
maxage = "\"max-age={0}\""
|
|
initial_val = maxage.format(constants.AUTOHSTS_STEPS[0])
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
# Verify initial value
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
initial_val)
|
|
|
|
self.config.update_autohsts(mock.MagicMock())
|
|
# Freq not patched, so value shouldn't increase
|
|
self.assertFalse(mock_increase.called)
|
|
|
|
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
@mock.patch("certbot_apache.constants.AUTOHSTS_FREQ", 0)
|
|
def test_autohsts_increase_no_header(self, _restart):
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
# Remove the header
|
|
dir_locs = self.config.parser.find_dir("Header", None,
|
|
self.vh_truth[7].path)
|
|
dir_loc = "/".join(dir_locs[0].split("/")[:-1])
|
|
self.config.parser.aug.remove(dir_loc)
|
|
self.assertRaises(errors.PluginError,
|
|
self.config.update_autohsts,
|
|
mock.MagicMock())
|
|
|
|
@mock.patch("certbot_apache.constants.AUTOHSTS_FREQ", 0)
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
def test_autohsts_increase_and_make_permanent(self, _mock_restart):
|
|
maxage = "\"max-age={0}\""
|
|
max_val = maxage.format(constants.AUTOHSTS_PERMANENT)
|
|
mock_lineage = mock.MagicMock()
|
|
mock_lineage.key_path = "/etc/apache2/ssl/key-certbot_15.pem"
|
|
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
|
for i in range(len(constants.AUTOHSTS_STEPS)-1):
|
|
# Ensure that value is not made permanent prematurely
|
|
self.config.deploy_autohsts(mock_lineage)
|
|
self.assertNotEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
max_val)
|
|
self.config.update_autohsts(mock.MagicMock())
|
|
# Value should match pre-permanent increment step
|
|
cur_val = maxage.format(constants.AUTOHSTS_STEPS[i+1])
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
cur_val)
|
|
# Ensure that the value is raised to max
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
maxage.format(constants.AUTOHSTS_STEPS[-1]))
|
|
# Make permanent
|
|
self.config.deploy_autohsts(mock_lineage)
|
|
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
|
max_val)
|
|
|
|
def test_autohsts_update_noop(self):
|
|
with mock.patch("time.time") as mock_time:
|
|
# Time mock is used to make sure that the execution does not
|
|
# continue when no autohsts entries exist in pluginstorage
|
|
self.config.update_autohsts(mock.MagicMock())
|
|
self.assertFalse(mock_time.called)
|
|
|
|
def test_autohsts_make_permanent_noop(self):
|
|
self.config.storage.put = mock.MagicMock()
|
|
self.config.deploy_autohsts(mock.MagicMock())
|
|
# Make sure that the execution does not continue when no entries in store
|
|
self.assertFalse(self.config.storage.put.called)
|
|
|
|
@mock.patch("certbot_apache.display_ops.select_vhost")
|
|
def test_autohsts_no_ssl_vhost(self, mock_select):
|
|
mock_select.return_value = self.vh_truth[0]
|
|
with mock.patch("certbot_apache.configurator.logger.warning") as mock_log:
|
|
self.assertRaises(errors.PluginError,
|
|
self.config.enable_autohsts,
|
|
mock.MagicMock(), "invalid.example.com")
|
|
self.assertTrue(
|
|
"Certbot was not able to find SSL" in mock_log.call_args[0][0])
|
|
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.restart")
|
|
@mock.patch("certbot_apache.configurator.ApacheConfigurator.add_vhost_id")
|
|
def test_autohsts_dont_enhance_twice(self, mock_id, _restart):
|
|
mock_id.return_value = "1234567"
|
|
self.config.enable_autohsts(mock.MagicMock(),
|
|
["ocspvhost.com", "ocspvhost.com"])
|
|
self.assertEquals(mock_id.call_count, 1)
|
|
|
|
def test_autohsts_remove_orphaned(self):
|
|
# pylint: disable=protected-access
|
|
self.config._autohsts_fetch_state()
|
|
self.config._autohsts["orphan_id"] = {"laststep": 0, "timestamp": 0}
|
|
|
|
self.config._autohsts_save_state()
|
|
self.config.update_autohsts(mock.MagicMock())
|
|
self.assertFalse("orphan_id" in self.config._autohsts)
|
|
# Make sure it's removed from the pluginstorage file as well
|
|
self.config._autohsts = None
|
|
self.config._autohsts_fetch_state()
|
|
self.assertFalse(self.config._autohsts)
|
|
|
|
def test_autohsts_make_permanent_vhost_not_found(self):
|
|
# pylint: disable=protected-access
|
|
self.config._autohsts_fetch_state()
|
|
self.config._autohsts["orphan_id"] = {"laststep": 999, "timestamp": 0}
|
|
self.config._autohsts_save_state()
|
|
with mock.patch("certbot_apache.configurator.logger.warning") as mock_log:
|
|
self.config.deploy_autohsts(mock.MagicMock())
|
|
self.assertTrue(mock_log.called)
|
|
self.assertTrue(
|
|
"VirtualHost with id orphan_id was not" in mock_log.call_args[0][0])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() # pragma: no cover
|