mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 14:26:10 -04:00
- Finishing refactor of postconf/postfix command-line utilities - Plugin uses starttls_policy plugin to specify per-domain policies Cleaning up TLS policy code. Print warning when setting configuration parameter that is overridden by master. Update client to use new policy API Cleanup and test fixes Documentation fix smaller fixes Policy is now an enhancement and reverting works Added a README, and small documentation fixes throughout Moving testing infra from starttls repo to certbot-postfix fixing tests and lint Changes against new policy API starttls-everywhere => starttls-policy testing(postfix): Added more varieties of certificates to test against. Moar fixes against policy API. Address comments on README and setup.py Address small comments on postconf and util Address comments in installer Python 3 fixes and Postconf tester extends TempDir test class Mock out postconf calls from tests and test coverage for master overrides More various fixes. Everything minus testing done Remove STARTTLS policy enhancement from this branch. sphinx quickstart 99% test coverage some cleanup and testfixing cleanup leftover files Remove print statement testfix for python 3.4 Revert dockerfile change mypy fix fix(postfix): brad's comments test(postfix): coverage to 100 test(postfix): mypy import mypy types fix(postfix docs): add .rst files and fix build fix(postfix): tls_only and server_only params behave nicely together some cleanup lint fix more comments bump version number
152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
"""Classes that wrap the postconf command line utility.
|
|
"""
|
|
import six
|
|
from certbot import errors
|
|
from certbot_postfix import util
|
|
|
|
# pylint: disable=unused-import, no-name-in-module
|
|
from acme.magic_typing import Dict, List, Tuple
|
|
# pylint: enable=unused-import, no-name-in-module
|
|
|
|
class ConfigMain(util.PostfixUtilBase):
|
|
"""A parser for Postfix's main.cf file."""
|
|
|
|
def __init__(self, executable, ignore_master_overrides=False, config_dir=None):
|
|
super(ConfigMain, self).__init__(executable, config_dir)
|
|
# Whether to ignore overrides from master.
|
|
self._ignore_master_overrides = ignore_master_overrides
|
|
# List of all current Postfix parameters, from `postconf` command.
|
|
self._db = {} # type: Dict[str, str]
|
|
# List of current master.cf overrides from Postfix config. Dictionary
|
|
# of parameter name => list of tuples (service name, paramter value)
|
|
# Note: We should never modify master without explicit permission.
|
|
self._master_db = {} # type: Dict[str, List[Tuple[str, str]]]
|
|
# List of all changes requested to the Postfix parameters as they are now
|
|
# in _db. These changes are flushed to `postconf` on `flush`.
|
|
self._updated = {} # type: Dict[str, str]
|
|
self._read_from_conf()
|
|
|
|
def _read_from_conf(self):
|
|
"""Reads initial parameter state from `main.cf` into this object.
|
|
"""
|
|
out = self._get_output()
|
|
for name, value in _parse_main_output(out):
|
|
self._db[name] = value
|
|
out = self._get_output_master()
|
|
for name, value in _parse_main_output(out):
|
|
service, param_name = name.rsplit("/", 1)
|
|
if param_name not in self._master_db:
|
|
self._master_db[param_name] = []
|
|
self._master_db[param_name].append((service, value))
|
|
|
|
def _get_output_master(self):
|
|
"""Retrieves output for `master.cf` parameters."""
|
|
return self._get_output('-P')
|
|
|
|
def get_default(self, name):
|
|
"""Retrieves default value of parameter `name` from postfix parameters.
|
|
|
|
:param str name: The name of the parameter to fetch.
|
|
:returns: The default value of parameter `name`.
|
|
:rtype: str
|
|
"""
|
|
out = self._get_output(['-d', name])
|
|
_, value = next(_parse_main_output(out), (None, None))
|
|
return value
|
|
|
|
def get(self, name):
|
|
"""Retrieves working value of parameter `name` from postfix parameters.
|
|
|
|
:param str name: The name of the parameter to fetch.
|
|
:returns: The value of parameter `name`.
|
|
:rtype: str
|
|
"""
|
|
if name in self._updated:
|
|
return self._updated[name]
|
|
return self._db[name]
|
|
|
|
def get_master_overrides(self, name):
|
|
"""Retrieves list of overrides for parameter `name` in postfix's Master config
|
|
file.
|
|
|
|
:returns: List of tuples (service, value), meaning that parameter `name`
|
|
is overridden as `value` for `service`.
|
|
:rtype: `list` of `tuple` of `str`
|
|
"""
|
|
if name in self._master_db:
|
|
return self._master_db[name]
|
|
return None
|
|
|
|
def set(self, name, value, acceptable_overrides=None):
|
|
"""Sets parameter `name` to `value`. If `name` is overridden by a particular service in
|
|
`master.cf`, reports any of these parameter conflicts as long as
|
|
`ignore_master_overrides` was not set.
|
|
|
|
.. note:: that this function does not flush these parameter values to main.cf;
|
|
To do that, use `flush`.
|
|
|
|
:param str name: The name of the parameter to set.
|
|
:param str value: The value of the parameter.
|
|
:param tuple acceptable_overrides: If the master configuration file overrides `value`
|
|
with a value in acceptable_overrides.
|
|
"""
|
|
if name not in self._db:
|
|
raise KeyError("Parameter name %s is not a valid Postfix parameter name.", name)
|
|
# Check to see if this parameter is overridden by master.
|
|
overrides = self.get_master_overrides(name)
|
|
if not self._ignore_master_overrides and overrides is not None:
|
|
util.report_master_overrides(name, overrides, acceptable_overrides)
|
|
if value != self._db[name]:
|
|
# _db contains the "original" state of parameters. We only care about
|
|
# writes if they cause a delta from the original state.
|
|
self._updated[name] = value
|
|
elif name in self._updated:
|
|
# If this write reverts a previously updated parameter back to the
|
|
# original DB's state, we don't have to keep track of it in _updated.
|
|
del self._updated[name]
|
|
|
|
def flush(self):
|
|
"""Flushes all parameter changes made using `self.set`, to `main.cf`
|
|
|
|
:raises error.PluginError: When flush to main.cf fails for some reason.
|
|
"""
|
|
if len(self._updated) == 0:
|
|
return
|
|
args = ['-e']
|
|
for name, value in six.iteritems(self._updated):
|
|
args.append('{0}={1}'.format(name, value))
|
|
try:
|
|
self._get_output(args)
|
|
except IOError as e:
|
|
raise errors.PluginError("Unable to save to Postfix config: %v", e)
|
|
for name, value in six.iteritems(self._updated):
|
|
self._db[name] = value
|
|
self._updated = {}
|
|
|
|
def get_changes(self):
|
|
""" Return queued changes to main.cf.
|
|
|
|
:rtype: dict[str, str]
|
|
"""
|
|
return self._updated
|
|
|
|
def _parse_main_output(output):
|
|
"""Parses the raw output from Postconf about main.cf.
|
|
|
|
Expects the output to look like:
|
|
|
|
.. code-block:: none
|
|
|
|
name1 = value1
|
|
name2 = value2
|
|
|
|
:param str output: data postconf wrote to stdout about main.cf
|
|
|
|
:returns: generator providing key-value pairs from main.cf
|
|
:rtype: Iterator[tuple(str, str)]
|
|
"""
|
|
for line in output.splitlines():
|
|
name, _, value = line.partition(" =")
|
|
yield name, value.strip()
|
|
|
|
|